[RFC PATCH v2 05/41] af_unix: keep sender ids in skb->cb only when they are classic ids

From: Jan Koester

Date: Tue Oct 20 2026 - 23:20:54 EST


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 <jan.koester@xxxxxxxxx>
---
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