summaryrefslogtreecommitdiff
path: root/net/unix
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2026-03-12 12:53:34 -0700
committerJakub Kicinski <kuba@kernel.org>2026-03-19 14:16:00 -0700
commitedab1ca5ec6fffecbf340e26956ce73e502901d5 (patch)
treed1cec843415f5b36a31d50e4f164d0d9a2a2fadb /net/unix
parent9ac76f3d0bb2940db3a9684d596b9c8f301ef315 (diff)
parenta1d9d8e833781c44ab688708804ce35f20f3cbbd (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Cross-merge networking fixes after downstream PR (net-7.0-rc5). net/netfilter/nft_set_rbtree.c 598adea720b97 ("netfilter: revert nft_set_rbtree: validate open interval overlap") 3aea466a43998 ("netfilter: nft_set_rbtree: don't disable bh when acquiring tree lock") https://lore.kernel.org/abgaQBpeGstdN4oq@sirena.org.uk No adjacent changes. Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'net/unix')
-rw-r--r--net/unix/af_unix.c2
-rw-r--r--net/unix/af_unix.h1
-rw-r--r--net/unix/garbage.c79
3 files changed, 54 insertions, 28 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index cd83d11bc8e4..5d4baac499af 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1958,6 +1958,8 @@ static void unix_detach_fds(struct scm_cookie *scm, struct sk_buff *skb)
static void unix_peek_fds(struct scm_cookie *scm, struct sk_buff *skb)
{
scm->fp = scm_fp_dup(UNIXCB(skb).fp);
+
+ unix_peek_fpl(scm->fp);
}
static void unix_destruct_scm(struct sk_buff *skb)
diff --git a/net/unix/af_unix.h b/net/unix/af_unix.h
index c4f1b2da363d..8119dbeef3a3 100644
--- a/net/unix/af_unix.h
+++ b/net/unix/af_unix.h
@@ -29,6 +29,7 @@ void unix_del_edges(struct scm_fp_list *fpl);
void unix_update_edges(struct unix_sock *receiver);
int unix_prepare_fpl(struct scm_fp_list *fpl);
void unix_destroy_fpl(struct scm_fp_list *fpl);
+void unix_peek_fpl(struct scm_fp_list *fpl);
void unix_schedule_gc(struct user_struct *user);
/* SOCK_DIAG */
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index 816e8fa2b062..a7967a345827 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -318,6 +318,25 @@ void unix_destroy_fpl(struct scm_fp_list *fpl)
unix_free_vertices(fpl);
}
+static bool gc_in_progress;
+static seqcount_t unix_peek_seq = SEQCNT_ZERO(unix_peek_seq);
+
+void unix_peek_fpl(struct scm_fp_list *fpl)
+{
+ static DEFINE_SPINLOCK(unix_peek_lock);
+
+ if (!fpl || !fpl->count_unix)
+ return;
+
+ if (!READ_ONCE(gc_in_progress))
+ return;
+
+ /* Invalidate the final refcnt check in unix_vertex_dead(). */
+ spin_lock(&unix_peek_lock);
+ raw_write_seqcount_barrier(&unix_peek_seq);
+ spin_unlock(&unix_peek_lock);
+}
+
static bool unix_vertex_dead(struct unix_vertex *vertex)
{
struct unix_edge *edge;
@@ -351,6 +370,36 @@ static bool unix_vertex_dead(struct unix_vertex *vertex)
return true;
}
+static LIST_HEAD(unix_visited_vertices);
+static unsigned long unix_vertex_grouped_index = UNIX_VERTEX_INDEX_MARK2;
+
+static bool unix_scc_dead(struct list_head *scc, bool fast)
+{
+ struct unix_vertex *vertex;
+ bool scc_dead = true;
+ unsigned int seq;
+
+ seq = read_seqcount_begin(&unix_peek_seq);
+
+ list_for_each_entry_reverse(vertex, scc, scc_entry) {
+ /* Don't restart DFS from this vertex. */
+ list_move_tail(&vertex->entry, &unix_visited_vertices);
+
+ /* Mark vertex as off-stack for __unix_walk_scc(). */
+ if (!fast)
+ vertex->index = unix_vertex_grouped_index;
+
+ if (scc_dead)
+ scc_dead = unix_vertex_dead(vertex);
+ }
+
+ /* If MSG_PEEK intervened, defer this SCC to the next round. */
+ if (read_seqcount_retry(&unix_peek_seq, seq))
+ return false;
+
+ return scc_dead;
+}
+
static void unix_collect_skb(struct list_head *scc, struct sk_buff_head *hitlist)
{
struct unix_vertex *vertex;
@@ -404,9 +453,6 @@ static bool unix_scc_cyclic(struct list_head *scc)
return false;
}
-static LIST_HEAD(unix_visited_vertices);
-static unsigned long unix_vertex_grouped_index = UNIX_VERTEX_INDEX_MARK2;
-
static unsigned long __unix_walk_scc(struct unix_vertex *vertex,
unsigned long *last_index,
struct sk_buff_head *hitlist)
@@ -474,9 +520,7 @@ prev_vertex:
}
if (vertex->index == vertex->scc_index) {
- struct unix_vertex *v;
struct list_head scc;
- bool scc_dead = true;
/* SCC finalised.
*
@@ -485,18 +529,7 @@ prev_vertex:
*/
__list_cut_position(&scc, &vertex_stack, &vertex->scc_entry);
- list_for_each_entry_reverse(v, &scc, scc_entry) {
- /* Don't restart DFS from this vertex in unix_walk_scc(). */
- list_move_tail(&v->entry, &unix_visited_vertices);
-
- /* Mark vertex as off-stack. */
- v->index = unix_vertex_grouped_index;
-
- if (scc_dead)
- scc_dead = unix_vertex_dead(v);
- }
-
- if (scc_dead) {
+ if (unix_scc_dead(&scc, false)) {
unix_collect_skb(&scc, hitlist);
} else {
if (unix_vertex_max_scc_index < vertex->scc_index)
@@ -550,19 +583,11 @@ static void unix_walk_scc_fast(struct sk_buff_head *hitlist)
while (!list_empty(&unix_unvisited_vertices)) {
struct unix_vertex *vertex;
struct list_head scc;
- bool scc_dead = true;
vertex = list_first_entry(&unix_unvisited_vertices, typeof(*vertex), entry);
list_add(&scc, &vertex->scc_entry);
- list_for_each_entry_reverse(vertex, &scc, scc_entry) {
- list_move_tail(&vertex->entry, &unix_visited_vertices);
-
- if (scc_dead)
- scc_dead = unix_vertex_dead(vertex);
- }
-
- if (scc_dead) {
+ if (unix_scc_dead(&scc, true)) {
cyclic_sccs--;
unix_collect_skb(&scc, hitlist);
}
@@ -577,8 +602,6 @@ static void unix_walk_scc_fast(struct sk_buff_head *hitlist)
cyclic_sccs ? UNIX_GRAPH_CYCLIC : UNIX_GRAPH_NOT_CYCLIC);
}
-static bool gc_in_progress;
-
static void unix_gc(struct work_struct *work)
{
struct sk_buff_head hitlist;