mirror of
https://github.com/torvalds/linux.git
synced 2026-06-04 04:23:35 +02:00
thunderbolt: property: Cap recursion depth in __tb_property_parse_dir()
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: cdae7c07e3 ("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>
This commit is contained in:
parent
de21b59c29
commit
928abe19fb
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user