Skip to content

Commit f8194e5

Browse files
q2vengregkh
authored andcommitted
af_unix: Link struct unix_edge when queuing skb.
commit 42f298c06b30bfe0a8cbee5d38644e618699e26e upstream. Just before queuing skb with inflight fds, we call scm_stat_add(), which is a good place to set up the preallocated struct unix_vertex and struct unix_edge in UNIXCB(skb).fp. Then, we call unix_add_edges() and construct the directed graph as follows: 1. Set the inflight socket's unix_sock to unix_edge.predecessor. 2. Set the receiver's unix_sock to unix_edge.successor. 3. Set the preallocated vertex to inflight socket's unix_sock.vertex. 4. Link inflight socket's unix_vertex.entry to unix_unvisited_vertices. 5. Link unix_edge.vertex_entry to the inflight socket's unix_vertex.edges. Let's say we pass the fd of AF_UNIX socket A to B and the fd of B to C. The graph looks like this: +-------------------------+ | unix_unvisited_vertices | <-------------------------. +-------------------------+ | + | | +--------------+ +--------------+ | +--------------+ | | unix_sock A | <---. .---> | unix_sock B | <-|-. .---> | unix_sock C | | +--------------+ | | +--------------+ | | | +--------------+ | .-+ | vertex | | | .-+ | vertex | | | | | vertex | | | +--------------+ | | | +--------------+ | | | +--------------+ | | | | | | | | | | +--------------+ | | | +--------------+ | | | | '-> | unix_vertex | | | '-> | unix_vertex | | | | | +--------------+ | | +--------------+ | | | `---> | entry | +---------> | entry | +-' | | |--------------| | | |--------------| | | | edges | <-. | | | edges | <-. | | +--------------+ | | | +--------------+ | | | | | | | | | .----------------------' | | .----------------------' | | | | | | | | | +--------------+ | | | +--------------+ | | | | unix_edge | | | | | unix_edge | | | | +--------------+ | | | +--------------+ | | `-> | vertex_entry | | | `-> | vertex_entry | | | |--------------| | | |--------------| | | | predecessor | +---' | | predecessor | +---' | |--------------| | |--------------| | | successor | +-----' | successor | +-----' +--------------+ +--------------+ Henceforth, we denote such a graph as A -> B (-> C). Now, we can express all inflight fd graphs that do not contain embryo sockets. We will support the particular case later. 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]> Signed-off-by: Lee Jones <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 6b7a036 commit f8194e5

File tree

5 files changed

+100
-3
lines changed

5 files changed

+100
-3
lines changed

include/net/af_unix.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ extern unsigned int unix_tot_inflight;
2222

2323
void unix_inflight(struct user_struct *user, struct file *fp);
2424
void unix_notinflight(struct user_struct *user, struct file *fp);
25+
void unix_add_edges(struct scm_fp_list *fpl, struct unix_sock *receiver);
26+
void unix_del_edges(struct scm_fp_list *fpl);
2527
int unix_prepare_fpl(struct scm_fp_list *fpl);
2628
void unix_destroy_fpl(struct scm_fp_list *fpl);
2729
void unix_gc(void);

include/net/scm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ struct scm_fp_list {
3030
short count_unix;
3131
short max;
3232
#ifdef CONFIG_UNIX
33+
bool inflight;
3334
struct list_head vertices;
3435
struct unix_edge *edges;
3536
#endif

net/core/scm.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
9090
fpl->max = SCM_MAX_FD;
9191
fpl->user = NULL;
9292
#if IS_ENABLED(CONFIG_UNIX)
93+
fpl->inflight = false;
9394
fpl->edges = NULL;
9495
INIT_LIST_HEAD(&fpl->vertices);
9596
#endif
@@ -380,6 +381,7 @@ struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl)
380381
new_fpl->max = new_fpl->count;
381382
new_fpl->user = get_uid(fpl->user);
382383
#if IS_ENABLED(CONFIG_UNIX)
384+
new_fpl->inflight = false;
383385
new_fpl->edges = NULL;
384386
INIT_LIST_HEAD(&new_fpl->vertices);
385387
#endif

net/unix/af_unix.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1910,17 +1910,21 @@ static void scm_stat_add(struct sock *sk, struct sk_buff *skb)
19101910
struct scm_fp_list *fp = UNIXCB(skb).fp;
19111911
struct unix_sock *u = unix_sk(sk);
19121912

1913-
if (unlikely(fp && fp->count))
1913+
if (unlikely(fp && fp->count)) {
19141914
atomic_add(fp->count, &u->scm_stat.nr_fds);
1915+
unix_add_edges(fp, u);
1916+
}
19151917
}
19161918

19171919
static void scm_stat_del(struct sock *sk, struct sk_buff *skb)
19181920
{
19191921
struct scm_fp_list *fp = UNIXCB(skb).fp;
19201922
struct unix_sock *u = unix_sk(sk);
19211923

1922-
if (unlikely(fp && fp->count))
1924+
if (unlikely(fp && fp->count)) {
19231925
atomic_sub(fp->count, &u->scm_stat.nr_fds);
1926+
unix_del_edges(fp);
1927+
}
19241928
}
19251929

19261930
/*

net/unix/garbage.c

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,38 @@ struct unix_sock *unix_get_socket(struct file *filp)
101101
return NULL;
102102
}
103103

104+
static LIST_HEAD(unix_unvisited_vertices);
105+
106+
static void unix_add_edge(struct scm_fp_list *fpl, struct unix_edge *edge)
107+
{
108+
struct unix_vertex *vertex = edge->predecessor->vertex;
109+
110+
if (!vertex) {
111+
vertex = list_first_entry(&fpl->vertices, typeof(*vertex), entry);
112+
vertex->out_degree = 0;
113+
INIT_LIST_HEAD(&vertex->edges);
114+
115+
list_move_tail(&vertex->entry, &unix_unvisited_vertices);
116+
edge->predecessor->vertex = vertex;
117+
}
118+
119+
vertex->out_degree++;
120+
list_add_tail(&edge->vertex_entry, &vertex->edges);
121+
}
122+
123+
static void unix_del_edge(struct scm_fp_list *fpl, struct unix_edge *edge)
124+
{
125+
struct unix_vertex *vertex = edge->predecessor->vertex;
126+
127+
list_del(&edge->vertex_entry);
128+
vertex->out_degree--;
129+
130+
if (!vertex->out_degree) {
131+
edge->predecessor->vertex = NULL;
132+
list_move_tail(&vertex->entry, &fpl->vertices);
133+
}
134+
}
135+
104136
static void unix_free_vertices(struct scm_fp_list *fpl)
105137
{
106138
struct unix_vertex *vertex, *next_vertex;
@@ -111,6 +143,60 @@ static void unix_free_vertices(struct scm_fp_list *fpl)
111143
}
112144
}
113145

146+
DEFINE_SPINLOCK(unix_gc_lock);
147+
148+
void unix_add_edges(struct scm_fp_list *fpl, struct unix_sock *receiver)
149+
{
150+
int i = 0, j = 0;
151+
152+
spin_lock(&unix_gc_lock);
153+
154+
if (!fpl->count_unix)
155+
goto out;
156+
157+
do {
158+
struct unix_sock *inflight = unix_get_socket(fpl->fp[j++]);
159+
struct unix_edge *edge;
160+
161+
if (!inflight)
162+
continue;
163+
164+
edge = fpl->edges + i++;
165+
edge->predecessor = inflight;
166+
edge->successor = receiver;
167+
168+
unix_add_edge(fpl, edge);
169+
} while (i < fpl->count_unix);
170+
171+
out:
172+
spin_unlock(&unix_gc_lock);
173+
174+
fpl->inflight = true;
175+
176+
unix_free_vertices(fpl);
177+
}
178+
179+
void unix_del_edges(struct scm_fp_list *fpl)
180+
{
181+
int i = 0;
182+
183+
spin_lock(&unix_gc_lock);
184+
185+
if (!fpl->count_unix)
186+
goto out;
187+
188+
do {
189+
struct unix_edge *edge = fpl->edges + i++;
190+
191+
unix_del_edge(fpl, edge);
192+
} while (i < fpl->count_unix);
193+
194+
out:
195+
spin_unlock(&unix_gc_lock);
196+
197+
fpl->inflight = false;
198+
}
199+
114200
int unix_prepare_fpl(struct scm_fp_list *fpl)
115201
{
116202
struct unix_vertex *vertex;
@@ -141,11 +227,13 @@ int unix_prepare_fpl(struct scm_fp_list *fpl)
141227

142228
void unix_destroy_fpl(struct scm_fp_list *fpl)
143229
{
230+
if (fpl->inflight)
231+
unix_del_edges(fpl);
232+
144233
kvfree(fpl->edges);
145234
unix_free_vertices(fpl);
146235
}
147236

148-
DEFINE_SPINLOCK(unix_gc_lock);
149237
unsigned int unix_tot_inflight;
150238
static LIST_HEAD(gc_candidates);
151239
static LIST_HEAD(gc_inflight_list);

0 commit comments

Comments
 (0)