[PATCH] Kernel 7.2: fix for crash during init of thunderbolt driver
From: Woody Suwalski
Date: Sat Jul 11 2026 - 18:51:30 EST
Kernel 7.2 now includes the following patch from Mika:
=====
commit f5cc545f59699549adbaa4084149f8247865a51d
Author: Mika Westerberg <mika.westerberg@xxxxxxxxxxxxxxx>
Date: Wed Nov 19 12:53:58 2025 +0200
thunderbolt: Wait for tb_domain_release() to complete when driver is removed
=====
However as testing on older MacBookPro and MacBook Air, this patch is causing a crash on driver insmod.
/...
[ 10.570223] [ T2604] ACPI: bus type thunderbolt registered
[ 10.580666] [ T2604] BUG: unable to handle page fault for address: fffffffffffffff8
[ 10.580745] [ T2604] #PF: supervisor read access in kernel mode
[ 10.580796] [ T2604] #PF: error_code(0x0000) - not-present page
[ 10.580844] [ T2604] PGD 2ec19067 P4D 2ec19067 PUD 2ec1b067 PMD 0
[ 10.580915] [ T2604] Oops: Oops: 0000 [#1] SMP NOPTI
[ 10.580929] [ T2603] acpi ACPI0001:00: SBS HC: offset = 0x20, query_bit = 0x10
[ 10.580968] [ T2604] CPU: 3 UID: 0 PID: 2604 Comm: (udev-worker) Tainted: G S 7.2-pingu #0~rc1 PREEMPT(full) 9f8aaa247129d19aebb6b0d19f7b648b1f9f7f36
[ 10.581126] [ T2604] Tainted: [S]=CPU_OUT_OF_SPEC
[ 10.581165] [ T2604] Hardware name: Apple Inc. MacBookPro8,1/Mac-94245B3640C91C81, BIOS MBP81.88Z.0050.B00.1804101331 04/10/18
[ 10.581248] [ T2604] RIP: 0010:complete+0x3a/0x70
[ 10.581301] [ T2604] Code: 61 c8 00 48 89 c5 8b 03 83 f8 ff 74 05 83 c0 01 89 03 48 8b 53 10 48 8d 43 10 48 39 c2 74 29 48 8b 5b 10 31 d2 be 03 00 00 00 <48> 8b 7b f8 e8 4d e9 fd ff 48 8b 13 48 8b 43 08 48 89 42 08 48 89
[ 10.581441] [ T2604] RSP: 0018:ffff92720020ba98 EFLAGS: 00010046
[ 10.581496] [ T2604] RAX: ffff8c82c6a2b3b0 RBX: 0000000000000000 RCX: 000000000000000c
[ 10.581559] [ T2604] RDX: 0000000000000000 RSI: 0000000000000003 RDI: ffff8c82c6a2b3a8
[ 10.581621] [ T2604] RBP: 0000000000000282 R08: 0000000000000000 R09: 0000000000000000
[ 10.581681] [ T2604] R10: 0000000000000001 R11: ffff8c832ba20d80 R12: ffff8c82c6a2b3a8
[ 10.581744] [ T2604] R13: ffff8c82c81f02b0 R14: 0000000000000000 R15: ffff8c82c6e53148
[ 10.581808] [ T2604] FS: 00007ffaf52c99c0(0000) GS:ffff8c839ad88000(0000) knlGS:0000000000000000
[ 10.581881] [ T2604] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 10.581935] [ T2604] CR2: fffffffffffffff8 CR3: 0000000105596002 CR4: 00000000000606f0
[ 10.581999] [ T2604] Call Trace:
[ 10.582032] [ T2604] <TASK>
[ 10.582061] [ T2604] device_release+0x3a/0x90
[ 10.582112] [ T2604] kobject_put+0x8a/0x230
[ 10.582166] [ T2604] icm_probe+0xf2/0x550 [thunderbolt 2f2c536023810e0da91d9b6d6a63a9f9bf553e73]
[ 10.582415] [ T2604] nhi_probe+0x196/0x390 [thunderbolt 2f2c536023810e0da91d9b6d6a63a9f9bf553e73]
[ 10.582620] [ T2604] local_pci_probe+0x3c/0x90
[ 10.582668] [ T2604] pci_device_probe+0xb0/0x1d0
/...
The fix is to check if tb->cm_ops->complete has been initialized before using it.
I have also added couple of NULL pointer checks before calling a function.
I think that another issue could have been caused by a use-after-free of the tb structure after kfree() call.
I have tested it OK on my older hardware...
Signed-off-by: Woody Suwalski <terraluna977@xxxxxxxxx>
---
--- a/drivers/thunderbolt/domain.c 2026-06-23 07:35:46.060061342 -0400
+++ b/drivers/thunderbolt/domain.c 2026-07-11 15:09:12.748107785 -0400
@@ -321,13 +321,19 @@ static void tb_domain_release(struct dev
struct tb *tb = container_of(dev, struct tb, dev);
struct tb_nhi *nhi = tb->nhi;
- tb_ctl_free(tb->ctl);
- destroy_workqueue(tb->wq);
+ if (tb->ctl)
+ tb_ctl_free(tb->ctl);
+
+ if (tb->wq)
+ destroy_workqueue(tb->wq);
+
ida_free(&tb_domain_ida, tb->index);
+
+ if (tb->cm_ops && tb->cm_ops->complete)
+ complete(&nhi->domain_released);
+
mutex_destroy(&tb->lock);
kfree(tb);
-
- complete(&nhi->domain_released);
}
const struct device_type tb_domain_type = {
@@ -405,6 +411,8 @@ struct tb *tb_domain_alloc(struct tb_nhi
if (!tb->ctl)
goto err_destroy_wq;
+ tb->cm_ops = NULL;
+
tb->dev.parent = nhi->dev;
tb->dev.bus = &tb_bus_type;
tb->dev.type = &tb_domain_type;
@@ -600,7 +608,7 @@ int tb_domain_thaw_noirq(struct tb *tb)
void tb_domain_complete(struct tb *tb)
{
- if (tb->cm_ops->complete)
+ if (tb && tb->cm_ops && tb->cm_ops->complete)
tb->cm_ops->complete(tb);
}
--- a/drivers/thunderbolt/domain.c 2026-06-23 07:35:46.060061342 -0400
+++ b/drivers/thunderbolt/domain.c 2026-07-11 15:09:12.748107785 -0400
@@ -321,13 +321,19 @@ static void tb_domain_release(struct dev
struct tb *tb = container_of(dev, struct tb, dev);
struct tb_nhi *nhi = tb->nhi;
- tb_ctl_free(tb->ctl);
- destroy_workqueue(tb->wq);
+ if (tb->ctl)
+ tb_ctl_free(tb->ctl);
+
+ if (tb->wq)
+ destroy_workqueue(tb->wq);
+
ida_free(&tb_domain_ida, tb->index);
+
+ if (tb->cm_ops && tb->cm_ops->complete)
+ complete(&nhi->domain_released);
+
mutex_destroy(&tb->lock);
kfree(tb);
-
- complete(&nhi->domain_released);
}
const struct device_type tb_domain_type = {
@@ -405,6 +411,8 @@ struct tb *tb_domain_alloc(struct tb_nhi
if (!tb->ctl)
goto err_destroy_wq;
+ tb->cm_ops = NULL;
+
tb->dev.parent = nhi->dev;
tb->dev.bus = &tb_bus_type;
tb->dev.type = &tb_domain_type;
@@ -600,7 +608,7 @@ int tb_domain_thaw_noirq(struct tb *tb)
void tb_domain_complete(struct tb *tb)
{
- if (tb->cm_ops->complete)
+ if (tb && tb->cm_ops && tb->cm_ops->complete)
tb->cm_ops->complete(tb);
}