summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-05-19 13:23:10 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-05-19 13:23:10 +0300
commit8320de248d79d60ff2b701bc4676a0b863025add (patch)
tree8fd8cfabaa5006902493faca426a7456f05ebe18
parentaf8c5aa7a9c6f503d81f103d7ab4f8d759521de3 (diff)
parent928abe19fbf0127003abcb1ea69cabc1c897d0ab (diff)
downloadlinux-8320de248d79d60ff2b701bc4676a0b863025add.tar.xz
Merge tag 'thunderbolt-for-v7.1-rc5' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt into usb-linus
Mika writes: thunderbolt: Fixes for v7.1-rc5 This includes three patches that harden the XDomain property parser against possible malicious hosts. All these have been in linux-next with no reported issues. * tag 'thunderbolt-for-v7.1-rc5' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt: thunderbolt: property: Cap recursion depth in __tb_property_parse_dir() thunderbolt: property: Reject dir_len < 4 to prevent size_t underflow thunderbolt: property: Reject u32 wrap in tb_property_entry_valid()
-rw-r--r--drivers/thunderbolt/property.c32
1 files changed, 23 insertions, 9 deletions
diff --git a/drivers/thunderbolt/property.c b/drivers/thunderbolt/property.c
index 50cbfc92fe65..da2c59a17db5 100644
--- a/drivers/thunderbolt/property.c
+++ b/drivers/thunderbolt/property.c
@@ -8,6 +8,7 @@
*/
#include <linux/err.h>
+#include <linux/overflow.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/uuid.h>
@@ -34,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)
{
@@ -52,13 +54,16 @@ static inline void format_dwdata(void *dst, const void *src, size_t dwords)
static bool tb_property_entry_valid(const struct tb_property_entry *entry,
size_t block_len)
{
+ u32 end;
+
switch (entry->type) {
case TB_PROPERTY_TYPE_DIRECTORY:
case TB_PROPERTY_TYPE_DATA:
case TB_PROPERTY_TYPE_TEXT:
if (entry->length > block_len)
return false;
- if (entry->value + entry->length > block_len)
+ if (check_add_overflow(entry->value, entry->length, &end) ||
+ end > block_len)
return false;
break;
@@ -93,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;
@@ -114,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;
@@ -159,21 +165,31 @@ 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;
+ INIT_LIST_HEAD(&dir->properties);
+
if (is_root) {
content_offset = dir_offset + 2;
content_len = dir_len;
} else {
+ if (dir_len < 4) {
+ tb_property_free_dir(dir);
+ return NULL;
+ }
dir->uuid = kmemdup(&block[dir_offset], sizeof(*dir->uuid),
GFP_KERNEL);
if (!dir->uuid) {
@@ -187,12 +203,10 @@ static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
entries = (const struct tb_property_entry *)&block[content_offset];
nentries = content_len / (sizeof(*entries) / 4);
- INIT_LIST_HEAD(&dir->properties);
-
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;
@@ -231,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);
}
/**