]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
rxrpc: Fix read-after-free in rxrpc_queue_local()
authorDavid Howells <dhowells@redhat.com>
Tue, 13 Aug 2019 21:26:36 +0000 (22:26 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 29 Aug 2019 06:30:28 +0000 (08:30 +0200)
commit 06d9532fa6b34f12a6d75711162d47c17c1add72 upstream.

rxrpc_queue_local() attempts to queue the local endpoint it is given and
then, if successful, prints a trace line.  The trace line includes the
current usage count - but we're not allowed to look at the local endpoint
at this point as we passed our ref on it to the workqueue.

Fix this by reading the usage count before queuing the work item.

Also fix the reading of local->debug_id for trace lines, which must be done
with the same consideration as reading the usage count.

Fixes: 09d2bf595db4 ("rxrpc: Add a tracepoint to track rxrpc_local refcounting")
Reported-by: syzbot+78e71c5bab4f76a6a719@syzkaller.appspotmail.com
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/trace/events/rxrpc.h
net/rxrpc/local_object.c

index cc1d060cbf133e92460cf91f24c35babccbd9248..fa06b528c73c51d14f757be9328470d987e1945f 100644 (file)
@@ -498,10 +498,10 @@ rxrpc_tx_points;
 #define E_(a, b)       { a, b }
 
 TRACE_EVENT(rxrpc_local,
-           TP_PROTO(struct rxrpc_local *local, enum rxrpc_local_trace op,
+           TP_PROTO(unsigned int local_debug_id, enum rxrpc_local_trace op,
                     int usage, const void *where),
 
-           TP_ARGS(local, op, usage, where),
+           TP_ARGS(local_debug_id, op, usage, where),
 
            TP_STRUCT__entry(
                    __field(unsigned int,       local           )
@@ -511,7 +511,7 @@ TRACE_EVENT(rxrpc_local,
                             ),
 
            TP_fast_assign(
-                   __entry->local = local->debug_id;
+                   __entry->local = local_debug_id;
                    __entry->op = op;
                    __entry->usage = usage;
                    __entry->where = where;
index 9798159ee65fad6f68fcecc15c7aa4e90acb351e..9368dae857cac660bd4860d2579bb62d1d78d651 100644 (file)
@@ -93,7 +93,7 @@ static struct rxrpc_local *rxrpc_alloc_local(struct rxrpc_net *rxnet,
                local->debug_id = atomic_inc_return(&rxrpc_debug_id);
                memcpy(&local->srx, srx, sizeof(*srx));
                local->srx.srx_service = 0;
-               trace_rxrpc_local(local, rxrpc_local_new, 1, NULL);
+               trace_rxrpc_local(local->debug_id, rxrpc_local_new, 1, NULL);
        }
 
        _leave(" = %p", local);
@@ -321,7 +321,7 @@ struct rxrpc_local *rxrpc_get_local(struct rxrpc_local *local)
        int n;
 
        n = atomic_inc_return(&local->usage);
-       trace_rxrpc_local(local, rxrpc_local_got, n, here);
+       trace_rxrpc_local(local->debug_id, rxrpc_local_got, n, here);
        return local;
 }
 
@@ -335,7 +335,8 @@ struct rxrpc_local *rxrpc_get_local_maybe(struct rxrpc_local *local)
        if (local) {
                int n = atomic_fetch_add_unless(&local->usage, 1, 0);
                if (n > 0)
-                       trace_rxrpc_local(local, rxrpc_local_got, n + 1, here);
+                       trace_rxrpc_local(local->debug_id, rxrpc_local_got,
+                                         n + 1, here);
                else
                        local = NULL;
        }
@@ -343,16 +344,16 @@ struct rxrpc_local *rxrpc_get_local_maybe(struct rxrpc_local *local)
 }
 
 /*
- * Queue a local endpoint unless it has become unreferenced and pass the
- * caller's reference to the work item.
+ * Queue a local endpoint and pass the caller's reference to the work item.
  */
 void rxrpc_queue_local(struct rxrpc_local *local)
 {
        const void *here = __builtin_return_address(0);
+       unsigned int debug_id = local->debug_id;
+       int n = atomic_read(&local->usage);
 
        if (rxrpc_queue_work(&local->processor))
-               trace_rxrpc_local(local, rxrpc_local_queued,
-                                 atomic_read(&local->usage), here);
+               trace_rxrpc_local(debug_id, rxrpc_local_queued, n, here);
        else
                rxrpc_put_local(local);
 }
@@ -367,7 +368,7 @@ void rxrpc_put_local(struct rxrpc_local *local)
 
        if (local) {
                n = atomic_dec_return(&local->usage);
-               trace_rxrpc_local(local, rxrpc_local_put, n, here);
+               trace_rxrpc_local(local->debug_id, rxrpc_local_put, n, here);
 
                if (n == 0)
                        call_rcu(&local->rcu, rxrpc_local_rcu);
@@ -454,7 +455,7 @@ static void rxrpc_local_processor(struct work_struct *work)
                container_of(work, struct rxrpc_local, processor);
        bool again;
 
-       trace_rxrpc_local(local, rxrpc_local_processing,
+       trace_rxrpc_local(local->debug_id, rxrpc_local_processing,
                          atomic_read(&local->usage), NULL);
 
        do {