Netdev Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH net 0/2] rxrpc: Fix local endpoint handling
@ 2019-08-14 10:47 David Howells
  2019-08-14 10:47 ` [PATCH net 1/2] rxrpc: Fix local endpoint replacement David Howells
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: David Howells @ 2019-08-14 10:47 UTC (permalink / raw)
  To: netdev; +Cc: dhowells, linux-afs, linux-kernel


Here's a pair of patches that fix two issues in the handling of local
endpoints (rxrpc_local structs):

 (1) Use list_replace_init() rather than list_replace() if we're going to
     unconditionally delete the replaced item later, lest the list get
     corrupted.

 (2) Don't access the rxrpc_local object after passing our ref to the
     workqueue, not even to illuminate tracepoints, as the work function
     may cause the object to be freed.  We have to cache the information
     beforehand.

The patches are tagged here:

	git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs.git
	rxrpc-fixes-20190814

and can also be found on the following branch:

	http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git/log/?h=rxrpc-fixes

David
---
David Howells (2):
      rxrpc: Fix local endpoint replacement
      rxrpc: Fix read-after-free in rxrpc_queue_local()


 include/trace/events/rxrpc.h |    6 +++---
 net/rxrpc/local_object.c     |   21 +++++++++++----------
 2 files changed, 14 insertions(+), 13 deletions(-)


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH net 1/2] rxrpc: Fix local endpoint replacement
  2019-08-14 10:47 [PATCH net 0/2] rxrpc: Fix local endpoint handling David Howells
@ 2019-08-14 10:47 ` David Howells
  2019-08-14 10:48 ` [PATCH net 2/2] rxrpc: Fix read-after-free in rxrpc_queue_local() David Howells
  2019-08-15 23:33 ` [PATCH net 0/2] rxrpc: Fix local endpoint handling David Miller
  2 siblings, 0 replies; 4+ messages in thread
From: David Howells @ 2019-08-14 10:47 UTC (permalink / raw)
  To: netdev; +Cc: dhowells, linux-afs, linux-kernel

When a local endpoint (struct rxrpc_local) ceases to be in use by any
AF_RXRPC sockets, it starts the process of being destroyed, but this
doesn't cause it to be removed from the namespace endpoint list immediately
as tearing it down isn't trivial and can't be done in softirq context, so
it gets deferred.

If a new socket comes along that wants to bind to the same endpoint, a new
rxrpc_local object will be allocated and rxrpc_lookup_local() will use
list_replace() to substitute the new one for the old.

Then, when the dying object gets to rxrpc_local_destroyer(), it is removed
unconditionally from whatever list it is on by calling list_del_init().

However, list_replace() doesn't reset the pointers in the replaced
list_head and so the list_del_init() will likely corrupt the local
endpoints list.

Fix this by using list_replace_init() instead.

Fixes: 730c5fd42c1e ("rxrpc: Fix local endpoint refcounting")
Reported-by: syzbot+193e29e9387ea5837f1d@syzkaller.appspotmail.com
Signed-off-by: David Howells <dhowells@redhat.com>
---

 net/rxrpc/local_object.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c
index c9db3e762d8d..c45765b7263e 100644
--- a/net/rxrpc/local_object.c
+++ b/net/rxrpc/local_object.c
@@ -283,7 +283,7 @@ struct rxrpc_local *rxrpc_lookup_local(struct net *net,
 		goto sock_error;
 
 	if (cursor != &rxnet->local_endpoints)
-		list_replace(cursor, &local->link);
+		list_replace_init(cursor, &local->link);
 	else
 		list_add_tail(&local->link, cursor);
 	age = "new";


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH net 2/2] rxrpc: Fix read-after-free in rxrpc_queue_local()
  2019-08-14 10:47 [PATCH net 0/2] rxrpc: Fix local endpoint handling David Howells
  2019-08-14 10:47 ` [PATCH net 1/2] rxrpc: Fix local endpoint replacement David Howells
@ 2019-08-14 10:48 ` David Howells
  2019-08-15 23:33 ` [PATCH net 0/2] rxrpc: Fix local endpoint handling David Miller
  2 siblings, 0 replies; 4+ messages in thread
From: David Howells @ 2019-08-14 10:48 UTC (permalink / raw)
  To: netdev; +Cc: dhowells, linux-afs, linux-kernel

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>
---

 include/trace/events/rxrpc.h |    6 +++---
 net/rxrpc/local_object.c     |   19 ++++++++++---------
 2 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
index cc1d060cbf13..fa06b528c73c 100644
--- a/include/trace/events/rxrpc.h
+++ b/include/trace/events/rxrpc.h
@@ -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;
diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c
index c45765b7263e..72a6e12a9304 100644
--- a/net/rxrpc/local_object.c
+++ b/net/rxrpc/local_object.c
@@ -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);
@@ -456,7 +457,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 {


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH net 0/2] rxrpc: Fix local endpoint handling
  2019-08-14 10:47 [PATCH net 0/2] rxrpc: Fix local endpoint handling David Howells
  2019-08-14 10:47 ` [PATCH net 1/2] rxrpc: Fix local endpoint replacement David Howells
  2019-08-14 10:48 ` [PATCH net 2/2] rxrpc: Fix read-after-free in rxrpc_queue_local() David Howells
@ 2019-08-15 23:33 ` David Miller
  2 siblings, 0 replies; 4+ messages in thread
From: David Miller @ 2019-08-15 23:33 UTC (permalink / raw)
  To: dhowells; +Cc: netdev, linux-afs, linux-kernel

From: David Howells <dhowells@redhat.com>
Date: Wed, 14 Aug 2019 11:47:51 +0100

> Here's a pair of patches that fix two issues in the handling of local
> endpoints (rxrpc_local structs):
> 
>  (1) Use list_replace_init() rather than list_replace() if we're going to
>      unconditionally delete the replaced item later, lest the list get
>      corrupted.
> 
>  (2) Don't access the rxrpc_local object after passing our ref to the
>      workqueue, not even to illuminate tracepoints, as the work function
>      may cause the object to be freed.  We have to cache the information
>      beforehand.

Pulled, thanks David.

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, back to index

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-14 10:47 [PATCH net 0/2] rxrpc: Fix local endpoint handling David Howells
2019-08-14 10:47 ` [PATCH net 1/2] rxrpc: Fix local endpoint replacement David Howells
2019-08-14 10:48 ` [PATCH net 2/2] rxrpc: Fix read-after-free in rxrpc_queue_local() David Howells
2019-08-15 23:33 ` [PATCH net 0/2] rxrpc: Fix local endpoint handling David Miller

Netdev Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/netdev/0 netdev/git/0.git
	git clone --mirror https://lore.kernel.org/netdev/1 netdev/git/1.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 netdev netdev/ https://lore.kernel.org/netdev \
		netdev@vger.kernel.org netdev@archiver.kernel.org
	public-inbox-index netdev


Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.netdev


AGPL code for this site: git clone https://public-inbox.org/ public-inbox