Hi Tvrtko,
On 2026-09-25 at 09:01:10 +0100, Tvrtko Ursulin wrote:
>
> On 17/09/2026 18:01, Kamil Konieczny wrote:
> > Hi Tvrtko,
> > On 2026-09-17 at 11:12:03 +0100, Tvrtko Ursulin wrote:
> > > A new test to roughly simulate the VK CTS suite where
> > > dEQP-VK.wsi.android.swapchain.render.* is hitting an use after free when a
> > > sync file is accessed after the xe submission queue has been destroyed.
> > >
> > > Abbreviated KASAN report:
> > >
> > > [IGT] xe_sync_file: starting subtest sync_file_race
> > > ==================================================================
> > > BUG: KASAN: slab-use-after-free in
> > > drm_sched_fence_get_timeline_name+0xa1/0xb0 [gpu_sched]
> > > Read of size 8 at addr ffff888126726020 by task xe_sync_file/2931
> > > ...
> > > Call Trace:
> > > <TASK>
> > > kasan_report+0xeb/0x130
> > > drm_sched_fence_get_timeline_name+0xa1/0xb0 [gpu_sched]
> > > sync_file_ioctl+0x3cb/0xb00
> > > ...
> > > Allocated by task 2931:
> > > __kmalloc_cache_noprof+0x1c2/0x410
> > > guc_exec_queue_init+0x1a8/0x1240 [xe]
> > > xe_exec_queue_create+0xe72/0x13b0 [xe]
> > > xe_exec_queue_create_ioctl+0x10d9/0x1770 [xe]
> > > drm_ioctl_kernel+0x179/0x300
> > > drm_ioctl+0x58f/0xcf0
> > > xe_drm_ioctl+0xe8/0x140 [xe]
> > > ...
> > > Freed by task 1689:
> > > kfree+0x106/0x3e0
> > > __guc_exec_queue_fini_async+0x144/0x2d0 [xe]
> > > process_one_work+0x610/0xdf0
> > > worker_thread+0x7c8/0x14b0
> > >
> > > Without KASAN this of course turns into a plain null pointer dereference.
> > >
> > > Signed-off-by: Tvrtko Ursulin <[email protected]>
> > > Cc: Lucas De Marchi <[email protected]>
> > > Cc: Matthew Brost <[email protected]>
> > > Cc: Rodrigo Vivi <[email protected]>
> > > Cc: Thomas Hellström <[email protected]>
> > > Cc: Christian König <[email protected]>
> > > Cc: [email protected]
> > > Reviewed-by: Rodrigo Vivi <[email protected]> # v2
> > LGTM
> > Acked-by: Kamil Konieczny <[email protected]>
>
> Thank you - okay if I push or Intel will do it?
Let me find someone for review first.
Regards,
Kamil
>
> Regards,
>
> Tvrtko
>
> >
> > Regards,
> > Kamil
> >
> > > ---
> > > v2:
> > > - igt_main and igt_fixture tweaks. (Rodrigo)
> > >
> > > v3:
> > > - Add driver name checks.
> > >
> > > v4:
> > > - Restore alphabetical ordering in the makefile. (Kamil)
> > > ---
> > > tests/intel/xe_sync_file.c | 154 +++++++++++++++++++++++++++++++++++++
> > > tests/meson.build | 1 +
> > > 2 files changed, 155 insertions(+)
> > > create mode 100644 tests/intel/xe_sync_file.c
> > >
> > > diff --git a/tests/intel/xe_sync_file.c b/tests/intel/xe_sync_file.c
> > > new file mode 100644
> > > index 000000000000..842c3856a525
> > > --- /dev/null
> > > +++ b/tests/intel/xe_sync_file.c
> > > @@ -0,0 +1,154 @@
> > > +// SPDX-License-Identifier: MIT
> > > +/*
> > > + * Copyright © 2025 Google, Inc.
> > > + */
> > > +
> > > +/**
> > > + * TEST: Tests for sync file functionality
> > > + * Category: Core
> > > + * Mega feature: General Core features
> > > + * Sub-category: CMD submission
> > > + * Functionality: fences
> > > + */
> > > +
> > > +#include "igt.h"
> > > +#include "sync_file.h"
> > > +
> > > +#include "lib/igt_syncobj.h"
> > > +#include "lib/intel_reg.h"
> > > +
> > > +#include "xe_drm.h"
> > > +#include "xe/xe_ioctl.h"
> > > +#include "xe/xe_query.h"
> > > +
> > > +static int sync_file_get_status(int sync_file, char **driver_name)
> > > +{
> > > + struct sync_fence_info fence = { };
> > > + struct sync_file_info info = {
> > > + .num_fences = 1,
> > > + .sync_fence_info = to_user_pointer(&fence),
> > > + };
> > > +
> > > + do_ioctl(sync_file, SYNC_IOC_FILE_INFO, &info);
> > > + igt_assert_eq(info.num_fences, 1);
> > > +
> > > + igt_debug("'%s'/'%s' = %d\n",
> > > + fence.driver_name, fence.obj_name, fence.status);
> > > +
> > > + *driver_name = strdup(fence.driver_name);
> > > +
> > > + return fence.status;
> > > +}
> > > +
> > > +/**
> > > + * SUBTEST: sync_file_race
> > > + * Description: Check that we can safely query an exported sync file fd
> > > + * Test category: functionality test
> > > + */
> > > +static void test_race(int xe, struct drm_xe_engine_class_instance *eci)
> > > +{
> > > + uint32_t vm, bo, syncobj, bind_syncobj, *batch;
> > > + struct drm_xe_sync sync[2] = {
> > > + { .type = DRM_XE_SYNC_TYPE_SYNCOBJ,
> > > + .flags = DRM_XE_SYNC_FLAG_SIGNAL,
> > > + },
> > > + { .type = DRM_XE_SYNC_TYPE_SYNCOBJ,
> > > + .flags = DRM_XE_SYNC_FLAG_SIGNAL,
> > > + },
> > > + };
> > > + const uint32_t bbend = MI_BATCH_BUFFER_END;
> > > + struct drm_xe_exec exec = {
> > > + .address = 0x100000,
> > > + .num_batch_buffer = 1,
> > > + .num_syncs = 2,
> > > + .syncs = to_user_pointer(sync),
> > > + };
> > > + char *driver_name;
> > > + int sync_fence;
> > > + size_t size;
> > > +
> > > + vm = xe_vm_create(xe, 0, 0);
> > > +
> > > + size = xe_bb_size(xe, sizeof(bbend));
> > > + bo = xe_bo_create(xe, vm, size, vram_if_possible(xe, eci->gt_id),
> > > + DRM_XE_GEM_CREATE_FLAG_NEEDS_VISIBLE_VRAM);
> > > +
> > > + batch = xe_bo_map(xe, bo, size);
> > > + *batch = bbend;
> > > +
> > > + exec.exec_queue_id = xe_exec_queue_create(xe, vm, eci, 0);
> > > +
> > > + syncobj = syncobj_create(xe, 0);
> > > + bind_syncobj = syncobj_create(xe, 0);
> > > +
> > > + sync[0].handle = bind_syncobj;
> > > + xe_vm_bind_async(xe, vm, 0, bo, 0, exec.address, size, sync, 1);
> > > +
> > > + sync[0].flags &= ~DRM_XE_SYNC_FLAG_SIGNAL;
> > > + sync[0].handle = bind_syncobj;
> > > + sync[1].flags |= DRM_XE_SYNC_FLAG_SIGNAL;
> > > + sync[1].handle = syncobj;
> > > + xe_exec(xe, &exec);
> > > +
> > > + /* Export a sync file fence. */
> > > + sync_fence = syncobj_handle_to_fd(xe, sync[1].handle,
> > > +
> > > DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE);
> > > +
> > > + igt_assert(syncobj_wait(xe, &syncobj, 1, INT64_MAX, 0, NULL));
> > > + igt_assert(syncobj_wait(xe, &bind_syncobj, 1, INT64_MAX, 0, NULL));
> > > +
> > > + driver_name = NULL;
> > > + igt_assert_eq(sync_file_get_status(sync_fence, &driver_name), 1);
> > > + igt_assert(driver_name);
> > > + igt_assert(!strcmp(driver_name, "drm_sched"));
> > > + free(driver_name);
> > > +
> > > + sync[0].flags |= DRM_XE_SYNC_FLAG_SIGNAL;
> > > + syncobj_reset(xe, &sync[0].handle, 1);
> > > + xe_vm_unbind_async(xe, vm, 0, 0, exec.address, size, sync, 1);
> > > + igt_assert(syncobj_wait(xe, &sync[0].handle, 1, INT64_MAX, 0, NULL));
> > > +
> > > + syncobj_destroy(xe, syncobj);
> > > + xe_exec_queue_destroy(xe, exec.exec_queue_id);
> > > +
> > > + munmap(batch, size);
> > > + gem_close(xe, bo);
> > > +
> > > + syncobj_destroy(xe, bind_syncobj);
> > > + xe_vm_destroy(xe, vm);
> > > +
> > > + /* Give any delayed freeing time to run. */
> > > + sleep(1);
> > > +
> > > + /* This should still work and not crash the kernel. */
> > > + driver_name = NULL;
> > > + igt_assert_eq(sync_file_get_status(sync_fence, &driver_name), 1);
> > > +
> > > + /* And must not read the original timeline/driver name either! */
> > > + igt_assert(driver_name);
> > > + igt_assert(!strcmp(driver_name, "detached-driver"));
> > > + free(driver_name);
> > > +
> > > + close(sync_fence);
> > > +}
> > > +
> > > +int igt_main()
> > > +{
> > > + struct drm_xe_engine_class_instance *eci;
> > > + int xe;
> > > +
> > > + igt_fixture() {
> > > + xe = drm_open_driver(DRIVER_XE);
> > > + }
> > > +
> > > + igt_subtest("sync_file_race") {
> > > + xe_for_each_engine(xe, eci) {
> > > + test_race(xe, eci);
> > > + break;
> > > + }
> > > + }
> > > +
> > > + igt_fixture() {
> > > + drm_close_driver(xe);
> > > + }
> > > +}
> > > diff --git a/tests/meson.build b/tests/meson.build
> > > index 1ac89bab7e15..43236b19b3a9 100644
> > > --- a/tests/meson.build
> > > +++ b/tests/meson.build
> > > @@ -347,6 +347,7 @@ intel_xe_progs = [
> > > 'xe_survivability',
> > > 'xe_sriov_vram',
> > > 'xe_svm_usrptr_madvise',
> > > + 'xe_sync_file',
> > > 'xe_sysfs_defaults',
> > > 'xe_sysfs_preempt_timeout',
> > > 'xe_sysfs_scheduler',
> > > --
> > > 2.55.0
> > >
>