UPSTREAM: 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.
Bug: 404256079
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Acked-by: Paolo Abeni <pabeni@redhat.com>
Link: https://lore.kernel.org/r/20240325202425.60930-4-kuniyu@amazon.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Lee Jones <lee@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
(cherry picked from commit d2d9f382e2
)
Signed-off-by: Lee Jones <joneslee@google.com>
Change-Id: I276d0aaf86250b541d46cf7818165349525c5627
This commit is contained in:
committed by
Lee Jones
parent
de6b1e85b9
commit
769fc01f23
@@ -22,6 +22,8 @@ extern unsigned int unix_tot_inflight;
|
||||
|
||||
void unix_inflight(struct user_struct *user, struct file *fp);
|
||||
void unix_notinflight(struct user_struct *user, struct file *fp);
|
||||
void unix_add_edges(struct scm_fp_list *fpl, struct unix_sock *receiver);
|
||||
void unix_del_edges(struct scm_fp_list *fpl);
|
||||
int unix_prepare_fpl(struct scm_fp_list *fpl);
|
||||
void unix_destroy_fpl(struct scm_fp_list *fpl);
|
||||
void unix_gc(void);
|
||||
|
@@ -29,6 +29,9 @@ struct unix_edge;
|
||||
struct scm_fp_list {
|
||||
short count;
|
||||
short max;
|
||||
#ifdef CONFIG_UNIX
|
||||
bool inflight;
|
||||
#endif
|
||||
struct user_struct *user;
|
||||
struct file *fp[SCM_MAX_FD];
|
||||
#ifndef __GENKSYMS__
|
||||
|
@@ -90,6 +90,7 @@ static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
|
||||
fpl->max = SCM_MAX_FD;
|
||||
fpl->user = NULL;
|
||||
#if IS_ENABLED(CONFIG_UNIX)
|
||||
fpl->inflight = false;
|
||||
fpl->edges = NULL;
|
||||
INIT_LIST_HEAD(&fpl->vertices);
|
||||
#endif
|
||||
@@ -383,6 +384,7 @@ struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl)
|
||||
new_fpl->max = new_fpl->count;
|
||||
new_fpl->user = get_uid(fpl->user);
|
||||
#if IS_ENABLED(CONFIG_UNIX)
|
||||
new_fpl->inflight = false;
|
||||
new_fpl->edges = NULL;
|
||||
INIT_LIST_HEAD(&new_fpl->vertices);
|
||||
#endif
|
||||
|
@@ -1920,8 +1920,10 @@ static void scm_stat_add(struct sock *sk, struct sk_buff *skb)
|
||||
struct scm_fp_list *fp = UNIXCB(skb).fp;
|
||||
struct unix_sock *u = unix_sk(sk);
|
||||
|
||||
if (unlikely(fp && fp->count))
|
||||
if (unlikely(fp && fp->count)) {
|
||||
atomic_add(fp->count, &u->scm_stat.nr_fds);
|
||||
unix_add_edges(fp, u);
|
||||
}
|
||||
}
|
||||
|
||||
static void scm_stat_del(struct sock *sk, struct sk_buff *skb)
|
||||
@@ -1929,8 +1931,10 @@ static void scm_stat_del(struct sock *sk, struct sk_buff *skb)
|
||||
struct scm_fp_list *fp = UNIXCB(skb).fp;
|
||||
struct unix_sock *u = unix_sk(sk);
|
||||
|
||||
if (unlikely(fp && fp->count))
|
||||
if (unlikely(fp && fp->count)) {
|
||||
atomic_sub(fp->count, &u->scm_stat.nr_fds);
|
||||
unix_del_edges(fp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -101,6 +101,38 @@ struct unix_sock *unix_get_socket(struct file *filp)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static LIST_HEAD(unix_unvisited_vertices);
|
||||
|
||||
static void unix_add_edge(struct scm_fp_list *fpl, struct unix_edge *edge)
|
||||
{
|
||||
struct unix_vertex *vertex = edge->predecessor->vertex;
|
||||
|
||||
if (!vertex) {
|
||||
vertex = list_first_entry(&fpl->vertices, typeof(*vertex), entry);
|
||||
vertex->out_degree = 0;
|
||||
INIT_LIST_HEAD(&vertex->edges);
|
||||
|
||||
list_move_tail(&vertex->entry, &unix_unvisited_vertices);
|
||||
edge->predecessor->vertex = vertex;
|
||||
}
|
||||
|
||||
vertex->out_degree++;
|
||||
list_add_tail(&edge->vertex_entry, &vertex->edges);
|
||||
}
|
||||
|
||||
static void unix_del_edge(struct scm_fp_list *fpl, struct unix_edge *edge)
|
||||
{
|
||||
struct unix_vertex *vertex = edge->predecessor->vertex;
|
||||
|
||||
list_del(&edge->vertex_entry);
|
||||
vertex->out_degree--;
|
||||
|
||||
if (!vertex->out_degree) {
|
||||
edge->predecessor->vertex = NULL;
|
||||
list_move_tail(&vertex->entry, &fpl->vertices);
|
||||
}
|
||||
}
|
||||
|
||||
static void unix_free_vertices(struct scm_fp_list *fpl)
|
||||
{
|
||||
struct unix_vertex *vertex, *next_vertex;
|
||||
@@ -111,6 +143,60 @@ static void unix_free_vertices(struct scm_fp_list *fpl)
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_SPINLOCK(unix_gc_lock);
|
||||
|
||||
void unix_add_edges(struct scm_fp_list *fpl, struct unix_sock *receiver)
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
|
||||
spin_lock(&unix_gc_lock);
|
||||
|
||||
if (!fpl->count_unix)
|
||||
goto out;
|
||||
|
||||
do {
|
||||
struct unix_sock *inflight = unix_get_socket(fpl->fp[j++]);
|
||||
struct unix_edge *edge;
|
||||
|
||||
if (!inflight)
|
||||
continue;
|
||||
|
||||
edge = fpl->edges + i++;
|
||||
edge->predecessor = inflight;
|
||||
edge->successor = receiver;
|
||||
|
||||
unix_add_edge(fpl, edge);
|
||||
} while (i < fpl->count_unix);
|
||||
|
||||
out:
|
||||
spin_unlock(&unix_gc_lock);
|
||||
|
||||
fpl->inflight = true;
|
||||
|
||||
unix_free_vertices(fpl);
|
||||
}
|
||||
|
||||
void unix_del_edges(struct scm_fp_list *fpl)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
spin_lock(&unix_gc_lock);
|
||||
|
||||
if (!fpl->count_unix)
|
||||
goto out;
|
||||
|
||||
do {
|
||||
struct unix_edge *edge = fpl->edges + i++;
|
||||
|
||||
unix_del_edge(fpl, edge);
|
||||
} while (i < fpl->count_unix);
|
||||
|
||||
out:
|
||||
spin_unlock(&unix_gc_lock);
|
||||
|
||||
fpl->inflight = false;
|
||||
}
|
||||
|
||||
int unix_prepare_fpl(struct scm_fp_list *fpl)
|
||||
{
|
||||
struct unix_vertex *vertex;
|
||||
@@ -141,11 +227,13 @@ err:
|
||||
|
||||
void unix_destroy_fpl(struct scm_fp_list *fpl)
|
||||
{
|
||||
if (fpl->inflight)
|
||||
unix_del_edges(fpl);
|
||||
|
||||
kvfree(fpl->edges);
|
||||
unix_free_vertices(fpl);
|
||||
}
|
||||
|
||||
DEFINE_SPINLOCK(unix_gc_lock);
|
||||
unsigned int unix_tot_inflight;
|
||||
static LIST_HEAD(gc_candidates);
|
||||
static LIST_HEAD(gc_inflight_list);
|
||||
|
Reference in New Issue
Block a user