[Bug] WARNING in pt_iommu_amdv1_init

1 view
Skip to first unread message

Sam Sun

unread,
Feb 21, 2026, 1:53:50 AM (2 days ago) Feb 21
to j...@ziepe.ca, kevin...@intel.com, jo...@8bytes.org, wi...@kernel.org, robin....@arm.com, io...@lists.linux.dev, linux-...@vger.kernel.org, syzk...@googlegroups.com
Dear developers and maintainers,

We encountered a bug in the iommufd selftest framework that triggers a
kernel warning, which can lead to a kernel panic when panic_on_warn is
enabled. The issue was observed on kernel version 6.19.0 (commit
2961f841b025), using syzbot kernel config to compile
(https://syzkaller.appspot.com/text?tag=KernelConfig&x=e2f061f80b102378).

We analyzed the root cause of this bug. In
drivers/iommu/iommufd/selftest.c, the mock_domain_alloc_pgtable()
function configures the mock AMDv1 domain with the PT_FEAT_DYNAMIC_TOP
feature flag. However, the generic page table framework strictly
requires any driver requesting this feature to implement specific
hardware callbacks in driver_ops (specifically change_top and
get_top_lock).

Since the mock driver is only for testing and does not provide these
driver_ops callbacks, the sanity check in pt_iommu_amdv1_init fails
and triggers the warning. A possible fix for this issue is that the
PT_FEAT_DYNAMIC_TOP flag should simply be removed from its
configuration.

Here is the proposed patch to fix the issue:

--- a/drivers/iommu/iommufd/selftest.c
+++ b/drivers/iommu/iommufd/selftest.c
@@ -483,8 +483,7 @@ mock_domain_alloc_pgtable(struct device *dev,

cfg.common.hw_max_vasz_lg2 = 64;
cfg.common.hw_max_oasz_lg2 = 52;
- cfg.common.features = BIT(PT_FEAT_DYNAMIC_TOP) |
- BIT(PT_FEAT_AMDV1_ENCRYPT_TABLES) |
+ cfg.common.features = BIT(PT_FEAT_AMDV1_ENCRYPT_TABLES) |
BIT(PT_FEAT_AMDV1_FORCE_COHERENCE);
cfg.starting_level = 2;
mock->domain.ops = &amdv1_ops;

Reproducer:
The issue can be reproduced using the following syzkaller program:

# {Threaded:false Repeat:false RepeatTimes:0 Procs:1 Slowdown:1
Sandbox: SandboxArg:0 Leak:false NetInjection:false NetDevices:false
NetReset:false Cgroups:false BinfmtMisc:false CloseFDs:false
KCSAN:false DevlinkPCI:false NicVF:false USB:false VhciInjection:false
Wifi:false IEEE802154:false Sysctl:false Swap:false UseTmpDir:false
HandleSegv:false Trace:false CallComments:true
LegacyOptions:{Collide:false Fault:false FaultCall:0 FaultNth:0}}
open_by_handle_at(0xffffffffffffffff,
&(0x7f0000000240)=@reiserfs_2={0x8, 0x2, {0xb, 0xb}}, 0x0)
r0 = openat$iommufd(0xffffffffffffff9c, &(0x7f0000000080), 0x0, 0x0)
ioctl$IOMMU_IOAS_ALLOC(r0, 0x3b81, &(0x7f00000000c0)={0xc, 0x0, <r1=>0x0})
ioctl$IOMMU_TEST_OP_MOCK_DOMAIN(r0, 0x3ba0, &(0x7f0000000100)={0x48,
0x2, r1, 0x0, 0x0, 0x0, <r2=>0x0})
ioctl$IOMMU_HWPT_ALLOC$TEST(r0, 0x3b89, &(0x7f0000000200)={0x28, 0x2,
r2, r1, 0x0, 0x0, 0xdead, 0x8, &(0x7f0000000240)})

C reproducer:
// autogenerated by syzkaller (https://github.com/google/syzkaller)

#define _GNU_SOURCE

#include <endian.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

uint64_t r[3] = {0xffffffffffffffff, 0x0, 0x0};

int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000ul,
/*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul,
/*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000ul,
/*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul,
/*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul,
/*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000ul,
/*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul,
/*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
*(uint32_t*)0x200000000240 = 8;
*(uint32_t*)0x200000000244 = 2;
*(uint32_t*)0x200000000248 = 0xb;
*(uint32_t*)0x20000000024c = 0xb;
syscall(__NR_open_by_handle_at, /*mountdirfd=*/(intptr_t)-1,
/*handle=*/0x200000000240ul, /*flags=*/0ul);
memcpy((void*)0x200000000080, "/dev/iommu\000", 11);
res = syscall(__NR_openat, /*fd=*/0xffffffffffffff9cul,
/*file=*/0x200000000080ul, /*flags=*/0, /*mode=*/0);
if (res != -1)
r[0] = res;
*(uint32_t*)0x2000000000c0 = 0xc;
*(uint32_t*)0x2000000000c4 = 0;
res = syscall(__NR_ioctl, /*fd=*/r[0], /*cmd=*/0x3b81,
/*arg=*/0x2000000000c0ul);
if (res != -1)
r[1] = *(uint32_t*)0x2000000000c8;
*(uint32_t*)0x200000000100 = 0x48;
*(uint32_t*)0x200000000104 = 2;
*(uint32_t*)0x200000000108 = r[1];
*(uint32_t*)0x20000000010c = 0;
res = syscall(__NR_ioctl, /*fd=*/r[0], /*cmd=*/0x3ba0,
/*arg=*/0x200000000100ul);
if (res != -1)
r[2] = *(uint32_t*)0x200000000118;
*(uint32_t*)0x200000000200 = 0x28;
*(uint32_t*)0x200000000204 = 2;
*(uint32_t*)0x200000000208 = r[2];
*(uint32_t*)0x20000000020c = r[1];
*(uint32_t*)0x200000000214 = 0;
*(uint32_t*)0x200000000218 = 0xdead;
*(uint32_t*)0x20000000021c = 8;
*(uint64_t*)0x200000000220 = 0x200000000240;
*(uint32_t*)0x200000000240 = 0xbadbeef;
syscall(__NR_ioctl, /*fd=*/r[0], /*cmd=*/0x3b89, /*arg=*/0x200000000200ul);
return 0;
}

If you have any other questions, please let me know.

Best regards,
Yue
Reply all
Reply to author
Forward
0 new messages