]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
thunderbolt: property: Cap recursion depth in __tb_property_parse_dir()
authorMichael Bommarito <michael.bommarito@gmail.com>
Sun, 10 May 2026 23:16:58 +0000 (19:16 -0400)
committerMika Westerberg <mika.westerberg@linux.intel.com>
Mon, 11 May 2026 09:32:03 +0000 (11:32 +0200)
A DIRECTORY entry's value field is used as the dir_offset for a
recursive call into __tb_property_parse_dir() with no depth counter.
A crafted peer that chains DIRECTORY entries into a back-reference
loop drives the parser until the kernel stack is exhausted and the
guard page fires.  Any untrusted XDomain peer (cable, dock, in-line
inspector, adjacent host) that reaches the PROPERTIES_REQUEST
control-plane exchange can trigger this without authentication.

Thread a depth counter through tb_property_parse() and
__tb_property_parse_dir(), and reject blocks that exceed
TB_PROPERTY_MAX_DEPTH = 8.  That is comfortably larger than any
observed legitimate XDomain layout.

Operators who do not need XDomain host-to-host discovery can disable
the path entirely with thunderbolt.xdomain=0 on the kernel command
line.

Fixes: cdae7c07e3e3 ("thunderbolt: Add support for XDomain properties")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-6
Assisted-by: Codex:gpt-5-4
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
drivers/thunderbolt/property.c

index 74c92f9801fff523910636b4927c908af4b65877..da2c59a17db5c38b821c571dfe9f7b98bcbfd1c8 100644 (file)
@@ -35,10 +35,11 @@ struct tb_property_dir_entry {
 };
 
 #define TB_PROPERTY_ROOTDIR_MAGIC      0x55584401
+#define TB_PROPERTY_MAX_DEPTH          8
 
 static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
        size_t block_len, unsigned int dir_offset, size_t dir_len,
-       bool is_root);
+       bool is_root, unsigned int depth);
 
 static inline void parse_dwdata(void *dst, const void *src, size_t dwords)
 {
@@ -97,7 +98,8 @@ tb_property_alloc(const char *key, enum tb_property_type type)
 }
 
 static struct tb_property *tb_property_parse(const u32 *block, size_t block_len,
-                                       const struct tb_property_entry *entry)
+                                       const struct tb_property_entry *entry,
+                                       unsigned int depth)
 {
        char key[TB_PROPERTY_KEY_SIZE + 1];
        struct tb_property *property;
@@ -118,7 +120,7 @@ static struct tb_property *tb_property_parse(const u32 *block, size_t block_len,
        switch (property->type) {
        case TB_PROPERTY_TYPE_DIRECTORY:
                dir = __tb_property_parse_dir(block, block_len, entry->value,
-                                             entry->length, false);
+                                             entry->length, false, depth + 1);
                if (!dir) {
                        kfree(property);
                        return NULL;
@@ -163,13 +165,17 @@ static struct tb_property *tb_property_parse(const u32 *block, size_t block_len,
 }
 
 static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
-       size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root)
+       size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root,
+       unsigned int depth)
 {
        const struct tb_property_entry *entries;
        size_t i, content_len, nentries;
        unsigned int content_offset;
        struct tb_property_dir *dir;
 
+       if (depth > TB_PROPERTY_MAX_DEPTH)
+               return NULL;
+
        dir = kzalloc_obj(*dir);
        if (!dir)
                return NULL;
@@ -200,7 +206,7 @@ static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
        for (i = 0; i < nentries; i++) {
                struct tb_property *property;
 
-               property = tb_property_parse(block, block_len, &entries[i]);
+               property = tb_property_parse(block, block_len, &entries[i], depth);
                if (!property) {
                        tb_property_free_dir(dir);
                        return NULL;
@@ -239,7 +245,7 @@ struct tb_property_dir *tb_property_parse_dir(const u32 *block,
                return NULL;
 
        return __tb_property_parse_dir(block, block_len, 0, rootdir->length,
-                                      true);
+                                      true, 0);
 }
 
 /**