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.
 -----BEGIN PGP SIGNATURE-----
 
 iQJUBAABCgA+FiEEVTdhRGBbNzLrSUBaAP2fSd+ZWKAFAmoMN6kgHG1pa2Eud2Vz
 dGVyYmVyZ0BsaW51eC5pbnRlbC5jb20ACgkQAP2fSd+ZWKA8Ag//ZxXcy8i84eU9
 VIFBtR4jxwQXWJ6aOkJl/y3sZQXbOAY3CfbB54+0zFMx4qxSfCx4P4bMLYDqK64U
 YjWF+sxlcFtLv+u9YMfBFzcutK8qA5K0YEFP894gWyYRqdLzA6gGoJt7Vn+zARxZ
 eWTvmW8DJjy9Azh4opnfd8yvdh3wxslaGbU1/PRX7FLOPWxU0skn+N+T1583pJ4m
 bUxNw+jyGRKz49a0Foo4Ql5RCHg2wTFSGikrzvJreWF7kpz9NFoL/bkzLBbj5sKy
 Eyg8fxNAV2BVFRlp3CSHJsjRFEpg4o5LVNiRLHdeAATXHztsgFvqr3xRhQnfbuwa
 P8rkse1rIW6uFCkrLfpMdL9cdmDfrTBKsEp1YmtmtRHGBri+c7umf0vrDSliX2SO
 p/PfLKGHWeaIo1d8BDsnbsHIs+BLR7JZcjMJ4cIoUcCqTYzdtXibWTBVeHbT/CWm
 vgXfPyQ0g9L1kLxvdNQ7UD5hIB+PvlEhGprU5xjteBhVkM2IwczGxo1rxKacPh0q
 1qwF+/yKorGhVB5vFcjE3uQSSOaRw7kkfAO1biidPdNnXWPydEJM3RGQsB462dCL
 XQRg46Ynes/K+Ya1n2TyI8hnnr+gz+qKl8hYqI4O99fDiiFGKgmpVP+0ap9q9nzZ
 itAOtz3N2RdSFX5LmUfP8Q7dUGaPHtw=
 =fKQQ
 -----END PGP SIGNATURE-----

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()
This commit is contained in:
Greg Kroah-Hartman 2026-05-19 12:23:10 +02:00
commit 8320de248d

View File

@ -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);
}
/**