Skip to content

Commit 7172dc9

Browse files
q2venkuba-moo
authored andcommitted
af_unix: Add dead flag to struct scm_fp_list.
Commit 1af2dfa ("af_unix: Don't access successor in unix_del_edges() during GC.") fixed use-after-free by avoid accessing edge->successor while GC is in progress. However, there could be a small race window where another process could call unix_del_edges() while gc_in_progress is true and __skb_queue_purge() is on the way. So, we need another marker for struct scm_fp_list which indicates if the skb is garbage-collected. This patch adds dead flag in struct scm_fp_list and set it true before calling __skb_queue_purge(). Fixes: 1af2dfa ("af_unix: Don't access successor in unix_del_edges() during GC.") Signed-off-by: Kuniyuki Iwashima <[email protected]> Acked-by: Paolo Abeni <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 84c8b7a commit 7172dc9

File tree

3 files changed

+12
-4
lines changed

3 files changed

+12
-4
lines changed

include/net/scm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ struct scm_fp_list {
3333
short max;
3434
#ifdef CONFIG_UNIX
3535
bool inflight;
36+
bool dead;
3637
struct list_head vertices;
3738
struct unix_edge *edges;
3839
#endif

net/core/scm.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
9191
fpl->user = NULL;
9292
#if IS_ENABLED(CONFIG_UNIX)
9393
fpl->inflight = false;
94+
fpl->dead = false;
9495
fpl->edges = NULL;
9596
INIT_LIST_HEAD(&fpl->vertices);
9697
#endif

net/unix/garbage.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,11 @@ static void unix_add_edge(struct scm_fp_list *fpl, struct unix_edge *edge)
158158
unix_update_graph(unix_edge_successor(edge));
159159
}
160160

161-
static bool gc_in_progress;
162-
163161
static void unix_del_edge(struct scm_fp_list *fpl, struct unix_edge *edge)
164162
{
165163
struct unix_vertex *vertex = edge->predecessor->vertex;
166164

167-
if (!gc_in_progress)
165+
if (!fpl->dead)
168166
unix_update_graph(unix_edge_successor(edge));
169167

170168
list_del(&edge->vertex_entry);
@@ -240,7 +238,7 @@ void unix_del_edges(struct scm_fp_list *fpl)
240238
unix_del_edge(fpl, edge);
241239
} while (i < fpl->count_unix);
242240

243-
if (!gc_in_progress) {
241+
if (!fpl->dead) {
244242
receiver = fpl->edges[0].successor;
245243
receiver->scm_stat.nr_unix_fds -= fpl->count_unix;
246244
}
@@ -559,9 +557,12 @@ static void unix_walk_scc_fast(struct sk_buff_head *hitlist)
559557
list_replace_init(&unix_visited_vertices, &unix_unvisited_vertices);
560558
}
561559

560+
static bool gc_in_progress;
561+
562562
static void __unix_gc(struct work_struct *work)
563563
{
564564
struct sk_buff_head hitlist;
565+
struct sk_buff *skb;
565566

566567
spin_lock(&unix_gc_lock);
567568

@@ -579,6 +580,11 @@ static void __unix_gc(struct work_struct *work)
579580

580581
spin_unlock(&unix_gc_lock);
581582

583+
skb_queue_walk(&hitlist, skb) {
584+
if (UNIXCB(skb).fp)
585+
UNIXCB(skb).fp->dead = true;
586+
}
587+
582588
__skb_queue_purge(&hitlist);
583589
skip_gc:
584590
WRITE_ONCE(gc_in_progress, false);

0 commit comments

Comments
 (0)