summaryrefslogtreecommitdiff
path: root/net/unix
diff options
context:
space:
mode:
authorKuniyuki Iwashima <kuniyu@amazon.com>2025-01-16 08:34:37 +0300
committerJakub Kicinski <kuba@kernel.org>2025-01-20 22:27:41 +0300
commitc49a157c33c45cf00a1881e8c1f65bed5ff0023e (patch)
treef0080545f1fca86f926cb222abebc7200acbf957 /net/unix
parent4d0446b7a214e2aa28c0e914329610731f665ad2 (diff)
downloadlinux-c49a157c33c45cf00a1881e8c1f65bed5ff0023e.tar.xz
af_unix: Set drop reason in __unix_gc().
Inflight file descriptors by SCM_RIGHTS hold references to the struct file. AF_UNIX sockets could hold references to each other, forming reference cycles. Once such sockets are close()d without the fd recv()ed, they will be unaccessible from userspace but remain in kernel. __unix_gc() garbage-collects skb with the dead file descriptors and frees them by __skb_queue_purge(). Let's set SKB_DROP_REASON_SOCKET_CLOSE there. # echo 1 > /sys/kernel/tracing/events/skb/kfree_skb/enable # python3 >>> from socket import * >>> from array import array >>> >>> # Create a reference cycle >>> s1 = socket(AF_UNIX, SOCK_DGRAM) >>> s1.bind('') >>> s1.sendmsg([b"nop"], [(SOL_SOCKET, SCM_RIGHTS, array("i", [s1.fileno()]))], 0, s1.getsockname()) >>> s1.close() >>> >>> # Trigger GC >>> s2 = socket(AF_UNIX) >>> s2.close() # cat /sys/kernel/tracing/trace_pipe ... kworker/u16:2-42 ... kfree_skb: ... location=__unix_gc+0x4ad/0x580 reason: SOCKET_CLOSE Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com> Link: https://patch.msgid.link/20250116053441.5758-5-kuniyu@amazon.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'net/unix')
-rw-r--r--net/unix/garbage.c2
1 files changed, 1 insertions, 1 deletions
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index 0068e758be4d..9848b7b78701 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -573,7 +573,7 @@ static void __unix_gc(struct work_struct *work)
UNIXCB(skb).fp->dead = true;
}
- __skb_queue_purge(&hitlist);
+ __skb_queue_purge_reason(&hitlist, SKB_DROP_REASON_SOCKET_CLOSE);
skip_gc:
WRITE_ONCE(gc_in_progress, false);
}