unix_skb_parms has to fit into the 48 bytes of skb->cb. With kuid_t and
kgid_t growing to hold UUIDs and GUIDs the two of them alone would take
40 bytes.

Store classic ids as 32 bit values as before, and only for a UUID or
GUID allocate a small struct unix_skb_ids for both, freed together with
the pid reference in unix_destruct_scm(). AF_UNIX skbs carrying a pid
reference are never cloned, so the pointer has a single owner.
Senders with classic ids see no change and no allocation.

Assisted-by: Claude:claude-opus-5-5

Signed-off-by: Jan Koester <[email protected]>
---
 net/unix/af_unix.c | 50 +++++++++++++++++++++++++++++++++++++++-------
 net/unix/af_unix.h | 11 ++++++++--
 2 files changed, 52 insertions(+), 9 deletions(-)

diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 42cffeafc8..2b17310534 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1969,11 +1969,46 @@ static void unix_peek_fds(struct scm_cookie *scm, 
struct sk_buff *skb)
        unix_peek_fpl(scm->fp);
 }
 
+/*
+ * The sender's ids, in the skb itself for legacy ids; UUIDs and GUIDs don't
+ * fit in skb->cb and come from an allocation, freed with the pid reference
+ * in unix_destruct_scm().
+ */
+static int unix_skb_set_ids(struct sk_buff *skb, kuid_t uid, kgid_t gid)
+{
+       struct unix_skb_ids *ids = NULL;
+
+       if (!uid_is_legacy(uid) || !gid_is_legacy(gid)) {
+               ids = kmalloc_obj(*ids, GFP_KERNEL_ACCOUNT);
+               if (!ids)
+                       return -ENOMEM;
+               ids->uid = uid;
+               ids->gid = gid;
+       }
+       kfree(UNIXCB(skb).ids);
+       UNIXCB(skb).ids = ids;
+       UNIXCB(skb).uid = __kuid_val(uid);
+       UNIXCB(skb).gid = __kgid_val(gid);
+       return 0;
+}
+
+static kuid_t unix_skb_uid(const struct sk_buff *skb)
+{
+       return UNIXCB(skb).ids ? UNIXCB(skb).ids->uid : 
KUIDT_INIT(UNIXCB(skb).uid);
+}
+
+static kgid_t unix_skb_gid(const struct sk_buff *skb)
+{
+       return UNIXCB(skb).ids ? UNIXCB(skb).ids->gid : 
KGIDT_INIT(UNIXCB(skb).gid);
+}
+
 static void unix_destruct_scm(struct sk_buff *skb)
 {
        struct scm_cookie scm = {};
 
        swap(scm.pid, UNIXCB(skb).pid);
+       kfree(UNIXCB(skb).ids);
+       UNIXCB(skb).ids = NULL;
 
        if (UNIXCB(skb).fp)
                unix_detach_fds(&scm, skb);
@@ -1992,20 +2027,21 @@ static int unix_scm_to_skb(struct scm_cookie *scm, 
struct sk_buff *skb, bool sen
        int err = 0;
 
        UNIXCB(skb).pid = get_pid(scm->pid);
-       UNIXCB(skb).uid = scm->creds.uid;
-       UNIXCB(skb).gid = scm->creds.gid;
        UNIXCB(skb).fp = NULL;
+       skb->destructor = unix_wfree;
+       err = unix_skb_set_ids(skb, scm->creds.uid, scm->creds.gid);
+       if (err)
+               return err;
        unix_get_secdata(scm, skb);
        if (scm->fp && send_fds)
                err = unix_attach_fds(scm, skb);
 
-       skb->destructor = unix_wfree;
        return err;
 }
 
 static void unix_skb_to_scm(struct sk_buff *skb, struct scm_cookie *scm)
 {
-       scm_set_cred(scm, UNIXCB(skb).pid, UNIXCB(skb).uid, UNIXCB(skb).gid);
+       scm_set_cred(scm, UNIXCB(skb).pid, unix_skb_uid(skb), 
unix_skb_gid(skb));
        unix_set_secdata(scm, skb);
 }
 
@@ -2039,7 +2075,7 @@ static int unix_maybe_add_creds(struct sk_buff *skb, 
const struct sock *sk,
                        return err;
 
                UNIXCB(skb).pid = get_pid(pid);
-               current_uid_gid(&UNIXCB(skb).uid, &UNIXCB(skb).gid);
+               return unix_skb_set_ids(skb, current_uid(), current_gid());
        }
 
        return 0;
@@ -2049,8 +2085,8 @@ static bool unix_skb_scm_eq(struct sk_buff *skb,
                            struct scm_cookie *scm)
 {
        return UNIXCB(skb).pid == scm->pid &&
-              uid_eq(UNIXCB(skb).uid, scm->creds.uid) &&
-              gid_eq(UNIXCB(skb).gid, scm->creds.gid) &&
+              uid_eq(unix_skb_uid(skb), scm->creds.uid) &&
+              gid_eq(unix_skb_gid(skb), scm->creds.gid) &&
               unix_secdata_eq(scm, skb);
 }
 
diff --git a/net/unix/af_unix.h b/net/unix/af_unix.h
index 8119dbeef3..e861a430cf 100644
--- a/net/unix/af_unix.h
+++ b/net/unix/af_unix.h
@@ -10,10 +10,17 @@
 
 struct sock *unix_peer_get(struct sock *sk);
 
-struct unix_skb_parms {
-       struct pid              *pid;           /* skb credentials      */
+/* The sender's ids, when either is a UUID or GUID */
+struct unix_skb_ids {
        kuid_t                  uid;
        kgid_t                  gid;
+};
+
+struct unix_skb_parms {
+       struct pid              *pid;           /* skb credentials      */
+       struct unix_skb_ids     *ids;           /* uid and gid, or NULL */
+       uid_t                   uid;            /* for ids NULL: legacy */
+       gid_t                   gid;            /* uid and gid          */
        struct scm_fp_list      *fp;            /* Passed files         */
 #ifdef CONFIG_SECURITY_NETWORK
        u32                     secid;          /* Security ID          */
-- 
2.56.0


Reply via email to