All of lore.kernel.org
 help / color / mirror / Atom feed
From: Mika Westerberg <mika.westerberg@linux.intel.com>
To: Michael Bommarito <michael.bommarito@gmail.com>
Cc: linux-usb@vger.kernel.org, Mika Westerberg <westeri@kernel.org>,
	Andreas Noever <andreas.noever@gmail.com>,
	Yehezkel Bernat <YehezkelShB@gmail.com>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	linux-kernel@vger.kernel.org, stable@vger.kernel.org
Subject: Re: [PATCH v2 4/4] thunderbolt: test: add KUnit regression tests for XDomain property parser
Date: Mon, 27 Apr 2026 07:40:56 +0200	[thread overview]
Message-ID: <20260427054056.GL557136@black.igk.intel.com> (raw)
In-Reply-To: <20260415123221.225149-5-michael.bommarito@gmail.com>

On Wed, Apr 15, 2026 at 08:32:20AM -0400, Michael Bommarito wrote:
> Add three KUnit cases that exercise the defects fixed by the sibling
> commits in this series by feeding crafted XDomain property blocks to
> tb_property_parse_dir():
> 
>   tb_test_property_parse_u32_wrap - entry->value = 0xFFFFFF00 and
>     entry->length = 0x100 so their u32 sum 0x100000000 wraps to 0
>     under the block_len guard; without the fix the subsequent
>     parse_dwdata() reads attacker-directed OOB memory.
> 
>   tb_test_property_parse_recursion - two DIRECTORY entries pointing
>     at each other, driving __tb_property_parse_dir() recursion;
>     without the fix the kernel stack is exhausted.
> 
>   tb_test_property_parse_dir_len_underflow - a DIRECTORY entry with
>     length < 4 so non-root content_len = dir_len - 4 wraps size_t;
>     without the fix nentries is huge and the entry walk runs OOB.
> 
> Each test asserts tb_property_parse_dir() returns NULL on the
> crafted input.  With CONFIG_KASAN=y, running these on the pre-fix
> kernel reproduces an oops inside __tb_property_parse_dir (KASAN
> shadow-memory fault for the u32_wrap case, stack-guard trip for
> recursion, OOB read past block for dir_len underflow).  Post-fix
> they pass cleanly.
> 
> Run with:
>   ./tools/testing/kunit/kunit.py run --arch=x86_64 \\
>     --kconfig_add CONFIG_PCI=y --kconfig_add CONFIG_NVMEM=y \\
>     --kconfig_add CONFIG_USB4=y --kconfig_add CONFIG_USB4_KUNIT_TEST=y \\
>     --kconfig_add CONFIG_KASAN=y 'thunderbolt.tb_test_property_parse_*'
> 
> Assisted-by: Claude:claude-opus-4-6
> Assisted-by: Codex:gpt-5-4
> Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
> ---
>  drivers/thunderbolt/test.c | 127 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 127 insertions(+)
> 
> diff --git a/drivers/thunderbolt/test.c b/drivers/thunderbolt/test.c
> index 1f4318249c22..22f4107fcb8d 100644
> --- a/drivers/thunderbolt/test.c
> +++ b/drivers/thunderbolt/test.c
> @@ -2852,7 +2852,134 @@ static void tb_test_property_copy(struct kunit *test)
>  	tb_property_free_dir(src);
>  }
>  
> +/*
> + * Reproducers for three memory-safety defects in
> + * drivers/thunderbolt/property.c reached from a crafted XDomain
> + * PROPERTIES_RESPONSE payload.  Without the fix these trip KASAN or
> + * smash the kernel stack; with the fix each returns NULL cleanly.
> + */
> +static void tb_test_property_parse_u32_wrap(struct kunit *test)
> +{
> +	u32 *block = kunit_kzalloc(test, 500 * sizeof(u32), GFP_KERNEL);
> +	struct tb_property_dir *dir;
> +	struct {
> +		u32 key_hi, key_lo;
> +		u16 length;
> +		u8 reserved;
> +		u8 type;
> +		u32 value;
> +	} *e;

This is same as tb_property_entry so probably should use that or better do
it like we have in that root_directory as array of u32.

At least do not duplicate it all over.

> +
> +	/* Root header: magic + length=6 (single entry body of 4 dwords +
> +	 * 2 slack, keeps walk within block[]). */

The block comment format is

	/*
	 * ..
	 * ..
	 */

Ditto everywhre.


> +	block[0] = 0x55584401;
> +	block[1] = 6;
> +
> +	/* Crafted DATA entry at block[2..5]: value = 0xFFFFFF00 and
> +	 * length = 0x100 are u32/u16 such that the u32 sum 0x100000000
> +	 * wraps to 0, passing the sum <= block_len guard even though
> +	 * the real offset is block + 0xFFFFFF00 * 4 (~16 GiB past the
> +	 * block).  The subsequent parse_dwdata() at property.c:132
> +	 * copies entry->length*4 = 1024 bytes from that wild address
> +	 * into a fresh kcalloc buffer.
> +	 */
> +	e = (void *)&block[2];
> +	e->key_hi = 0x61616161;
> +	e->key_lo = 0x61616161;
> +	e->length = 0x100;
> +	e->type   = 0x64;		/* TB_PROPERTY_TYPE_DATA */

This can use TB_PROPERtY_TYPE_DATA.

> +	e->value  = 0xFFFFFF00;
> +
> +	dir = tb_property_parse_dir(block, 500);
> +	/* With the fix this returns NULL; without it, KASAN splats in
> +	 * be32_to_cpu_array() / memcpy reading block + value*4 out of
> +	 * bounds.  Assert on the safe outcome: a NULL dir. */
> +	KUNIT_EXPECT_NULL(test, dir);
> +	tb_property_free_dir(dir);
> +}
> +
> +static void tb_test_property_parse_recursion(struct kunit *test)
> +{
> +	u32 *block = kunit_kzalloc(test, 500 * sizeof(u32), GFP_KERNEL);
> +	struct tb_property_dir *dir;
> +	struct entry {
> +		u32 key_hi, key_lo;
> +		u16 length;
> +		u8 reserved;
> +		u8 type;
> +		u32 value;
> +	} *e, *child_e;
> +
> +	block[0] = 0x55584401;
> +	block[1] = 4;		/* rootdir length = one entry */
> +
> +	/* DIRECTORY entry pointing at dir_offset=2 with length=16.
> +	 * When parsed as non-root: content_offset = 6, content_len = 12,
> +	 * nentries = 3.  The child's first entry at block[6] is also
> +	 * DIRECTORY pointing at 2, so the recursion oscillates between
> +	 * two dir_offsets until the kernel stack is exhausted.
> +	 */
> +	e = (void *)&block[2];
> +	e->key_hi = 0x61616161;
> +	e->key_lo = 0x61616161;
> +	e->length = 16;
> +	e->type   = 0x44;		/* TB_PROPERTY_TYPE_DIRECTORY */

This can use TB_PROPERTY_TYPE_DIRECTORY.

> +	e->value  = 2;
> +
> +	child_e = (void *)&block[6];
> +	child_e->key_hi = 0x62626262;
> +	child_e->key_lo = 0x62626262;
> +	child_e->length = 16;
> +	child_e->type   = 0x44;
> +	child_e->value  = 2;
> +
> +	dir = tb_property_parse_dir(block, 500);
> +	/* With the fix this returns NULL at TB_PROPERTY_MAX_DEPTH (8).
> +	 * Without it, the kernel stack-guard fires ~50-80 frames in
> +	 * and the kunit thread oopses. */
> +	KUNIT_EXPECT_NULL(test, dir);
> +	tb_property_free_dir(dir);
> +}
> +
> +static void tb_test_property_parse_dir_len_underflow(struct kunit *test)
> +{
> +	u32 *block = kunit_kzalloc(test, 500 * sizeof(u32), GFP_KERNEL);
> +	struct tb_property_dir *dir;
> +	struct entry {
> +		u32 key_hi, key_lo;
> +		u16 length;
> +		u8 reserved;
> +		u8 type;
> +		u32 value;
> +	} *e;
> +
> +	block[0] = 0x55584401;
> +	block[1] = 4;
> +
> +	/* DIRECTORY entry with length=3.  When parsed as non-root,
> +	 * content_len = dir_len - 4 underflows size_t to ~SIZE_MAX,
> +	 * nentries = SIZE_MAX/4.  The for-loop walks entries past the
> +	 * block, reading OOB on each iteration.
> +	 */
> +	e = (void *)&block[2];
> +	e->key_hi = 0x61616161;
> +	e->key_lo = 0x61616161;
> +	e->length = 3;
> +	e->type   = 0x44;

Thi can use TB_PROPERTY_TYPE_DIRECTORY.

> +	e->value  = 6;
> +
> +	dir = tb_property_parse_dir(block, 500);
> +	/* With the fix: NULL.  Without: KASAN splat on
> +	 * block[content_offset + i*4] for i > 124 (past the 500-dword
> +	 * block). */
> +	KUNIT_EXPECT_NULL(test, dir);
> +	tb_property_free_dir(dir);
> +}
> +
>  static struct kunit_case tb_test_cases[] = {
> +	KUNIT_CASE(tb_test_property_parse_u32_wrap),
> +	KUNIT_CASE(tb_test_property_parse_recursion),
> +	KUNIT_CASE(tb_test_property_parse_dir_len_underflow),
>  	KUNIT_CASE(tb_test_path_basic),
>  	KUNIT_CASE(tb_test_path_not_connected_walk),
>  	KUNIT_CASE(tb_test_path_single_hop_walk),
> -- 
> 2.53.0

  reply	other threads:[~2026-04-27  5:40 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-15  3:23 [PATCH 0/2] thunderbolt: harden XDomain property parser Michael Bommarito
2026-04-15  3:23 ` [PATCH 1/2] thunderbolt: property: harden XDomain property parser against crafted peer Michael Bommarito
2026-04-15  4:52   ` Mika Westerberg
2026-04-15 11:41     ` Michael Bommarito
2026-04-15  3:23 ` [PATCH 2/2] thunderbolt: test: add KUnit regression tests for XDomain property parser Michael Bommarito
2026-04-15 12:32 ` [PATCH v2 0/4] thunderbolt: harden " Michael Bommarito
2026-04-15 12:32   ` [PATCH v2 1/4] thunderbolt: property: reject u32 wrap in tb_property_entry_valid() Michael Bommarito
2026-04-27  5:35     ` Mika Westerberg
2026-05-02 17:55       ` Michael Bommarito
2026-04-15 12:32   ` [PATCH v2 2/4] thunderbolt: property: reject dir_len < 4 to prevent size_t underflow Michael Bommarito
2026-04-15 12:32   ` [PATCH v2 3/4] thunderbolt: property: cap recursion depth in __tb_property_parse_dir() Michael Bommarito
2026-04-15 12:32   ` [PATCH v2 4/4] thunderbolt: test: add KUnit regression tests for XDomain property parser Michael Bommarito
2026-04-27  5:40     ` Mika Westerberg [this message]
2026-05-03 14:15   ` [PATCH v3 0/4] thunderbolt: harden " Michael Bommarito
2026-05-03 14:15     ` [PATCH v3 1/4] thunderbolt: property: reject u32 wrap in tb_property_entry_valid() Michael Bommarito
2026-05-04  8:57       ` Andy Shevchenko
2026-05-03 14:15     ` [PATCH v3 2/4] thunderbolt: property: reject dir_len < 4 to prevent size_t underflow Michael Bommarito
2026-05-04  8:59       ` Andy Shevchenko
2026-05-03 14:15     ` [PATCH v3 3/4] thunderbolt: property: cap recursion depth in __tb_property_parse_dir() Michael Bommarito
2026-05-04  9:01       ` Andy Shevchenko
2026-05-04 12:54         ` Michael Bommarito
2026-05-03 14:15     ` [PATCH v3 4/4] thunderbolt: test: add KUnit regression tests for XDomain property parser Michael Bommarito
2026-05-05 11:48       ` Mika Westerberg
2026-05-10 23:16     ` [PATCH v4 0/4] thunderbolt: harden " Michael Bommarito
2026-05-10 23:16       ` [PATCH v4 1/4] thunderbolt: property: reject u32 wrap in tb_property_entry_valid() Michael Bommarito
2026-05-10 23:16       ` [PATCH v4 2/4] thunderbolt: property: reject dir_len < 4 to prevent size_t underflow Michael Bommarito
2026-05-10 23:16       ` [PATCH v4 3/4] thunderbolt: property: cap recursion depth in __tb_property_parse_dir() Michael Bommarito
2026-05-10 23:16       ` [PATCH v4 4/4] thunderbolt: test: add KUnit regression tests for XDomain property parser Michael Bommarito
2026-05-11  9:37       ` [PATCH v4 0/4] thunderbolt: harden " Mika Westerberg

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260427054056.GL557136@black.igk.intel.com \
    --to=mika.westerberg@linux.intel.com \
    --cc=YehezkelShB@gmail.com \
    --cc=andreas.noever@gmail.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=michael.bommarito@gmail.com \
    --cc=stable@vger.kernel.org \
    --cc=westeri@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.