]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
keys: fix out-of-bounds read in keyring_get_key_chunk()
authorMichael Bommarito <michael.bommarito@gmail.com>
Sun, 19 Jul 2026 16:15:03 +0000 (12:15 -0400)
committerJarkko Sakkinen <jarkko@kernel.org>
Thu, 23 Jul 2026 15:23:32 +0000 (18:23 +0300)
For description-level chunks keyring_get_key_chunk() advances the read
pointer by level * sizeof(long) past the inline prefix but only
bounds-checks the prefix, so a long enough key description is read past
its kmemdup(desc, desc_len + 1) allocation.  Compute the full byte
offset and bounds-check the description against it before reading.

The walk only reaches a description-level chunk when two keys collide
through the hash, x, type and domain_tag chunks, so this is reached from
an unprivileged add_key(2) with a crafted pair of same-type keys whose
index hashes collide; KASAN reports a slab-out-of-bounds read.

Fixes: f771fde82051 ("keys: Simplify key description management")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Tested-by: Jarkko Sakkinen <jarkko@kernel.org>
Link: https://lore.kernel.org/r/20260719161505.2423935-2-michael.bommarito@gmail.com
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
security/keys/keyring.c

index 7a2ee0ded7c931f96bcf6c7acc98342e157361b3..085f7a743354c04fb6215001ec2b91b65b87e1fe 100644 (file)
@@ -271,6 +271,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
        unsigned long chunk = 0;
        const u8 *d;
        int desc_len = index_key->desc_len, n = sizeof(chunk);
+       unsigned int offset;
 
        level /= ASSOC_ARRAY_KEY_CHUNK_SIZE;
        switch (level) {
@@ -284,12 +285,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)
                return (unsigned long)index_key->domain_tag;
        default:
                level -= 4;
-               if (desc_len <= sizeof(index_key->desc))
+               offset = sizeof(index_key->desc) + level * sizeof(long);
+               if (desc_len <= offset)
                        return 0;
 
-               d = index_key->description + sizeof(index_key->desc);
-               d += level * sizeof(long);
-               desc_len -= sizeof(index_key->desc);
+               d = index_key->description + offset;
+               desc_len -= offset;
                if (desc_len > n)
                        desc_len = n;
                do {