We need to ensure that pidfs dentry is allocated when we meet any struct pid for the first time. This will allows us to open pidfd even after the task it corresponds to is reaped. Basically, we need to identify all places where we fill skb/scm_cookie with struct pid reference for the first time and call pidfs_register_pid(). Tricky thing here is that we have a few places where this happends depending on what userspace is doing: - [__scm_replace_pid()] explicitly sending an SCM_CREDENTIALS message and specified pid in a numeric format - [unix_maybe_add_creds()] enabled SO_PASSCRED/SO_PASSPIDFD but didn't send SCM_CREDENTIALS explicitly - [scm_send()] force_creds is true. Netlink case, we don't need to touch it. Cc: linux-kernel@vger.kernel.org Cc: netdev@vger.kernel.org Cc: "David S. Miller" Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: Simon Horman Cc: Leon Romanovsky Cc: Arnd Bergmann Cc: Christian Brauner Cc: Kuniyuki Iwashima Cc: Lennart Poettering Cc: Luca Boccassi Cc: David Rheinsberg Signed-off-by: Alexander Mikhalitsyn --- v2: - renamed __skb_set_pid() -> unix_set_pid_to_skb() [ as Kuniyuki suggested ] - get rid of extra helper (__scm_set_cred()) I've introduced before [ as Kuniyuki suggested ] - s/__inline__/inline/ for functions I touched [ as Kuniyuki suggested ] - get rid of chunk in unix_destruct_scm() with NULLifying UNIXCB(skb).pid [ as Kuniyuki suggested ] - added proper error handling in scm_send() for scm_set_cred() return value [ found by me during rework ] v3: - don't stash pidfs dentry for netlink case - splited whitespace changes - removed unix_set_pid_to_skb() to simplify changes --- net/core/scm.c | 7 +++++++ net/unix/af_unix.c | 11 ++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/net/core/scm.c b/net/core/scm.c index 045ab5bdac7d..358a4e04d46c 100644 --- a/net/core/scm.c +++ b/net/core/scm.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -147,9 +148,15 @@ EXPORT_SYMBOL(__scm_destroy); static inline int scm_replace_pid(struct scm_cookie *scm, struct pid *pid) { + int err; + /* drop all previous references */ scm_destroy_cred(scm); + err = pidfs_register_pid(pid); + if (unlikely(err)) + return err; + scm->pid = pid; scm->creds.pid = pid_vnr(pid); return 0; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 323e4fc85d4b..d52811321fce 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1971,6 +1971,7 @@ static void unix_skb_to_scm(struct sk_buff *skb, struct scm_cookie *scm) * We include credentials if source or destination socket * asserted SOCK_PASSCRED. * + * Context: May sleep. * Return: On success zero, on error a negative error code is returned. */ static int unix_maybe_add_creds(struct sk_buff *skb, const struct sock *sk, @@ -1980,7 +1981,15 @@ static int unix_maybe_add_creds(struct sk_buff *skb, const struct sock *sk, return 0; if (unix_may_passcred(sk) || unix_may_passcred(other)) { - UNIXCB(skb).pid = get_pid(task_tgid(current)); + struct pid *pid; + int err; + + pid = task_tgid(current); + err = pidfs_register_pid(pid); + if (unlikely(err)) + return err; + + UNIXCB(skb).pid = get_pid(pid); current_uid_gid(&UNIXCB(skb).uid, &UNIXCB(skb).gid); } -- 2.43.0