Linux driver-core infrastructure
 help / color / mirror / Atom feed
From: Priya Bala Govindasamy <pgovind2@uci.edu>
To: dakr@kernel.org, abdiel.janulgue@gmail.com,
	daniel.almeida@collabora.com, robin.murphy@arm.com,
	a.hindborg@kernel.org, ojeda@kernel.org, boqun@kernel.org,
	gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org,
	aliceryhl@google.com, tmgross@umich.edu, tamird@kernel.org,
	acourbot@nvidia.com, work@onurozkan.dev, lyude@redhat.com
Cc: driver-core@lists.linux.dev, rust-for-linux@vger.kernel.org,
	ardalan@uci.edu, zhiyunq@cs.ucr.edu, dzueck@uci.edu,
	pgovind2@uci.edu
Subject: [PATCH 0/1] rust: scatterlist: pin `Owned::sgt` to prevent invalidating `DmaMappedSgt::sgt`
Date: Wed, 11 Nov 2026 18:41:37 +0000	[thread overview]
Message-ID: <cover.1793652314.git.pgovind2@uci.edu> (raw)

Dear Linux kernel maintainers,

We are developing a tool called FerroLens to detect potential
unsound behavior in Rust code in the Linux kernel. The tool
internally uses an LLM to detect bugs.
We then perform manual analysis to verify these reports.
FerroLens reported the following bug in rust/kernel/scatterlist.rs:

`Owned<P>` contains an indirect self-reference. During pinned
initialization, `DmaMappedSgt::sgt` is set to the address of `Owned::sgt`.
`DmaMappedSgt` is then owned through `Owned::dma`.

At the moment, only `Owned::dma` is marked as structurally pinned.
The `#[pin_data]` attribute therefore only considers `Devres<DmaMappedSgt>`
in its `Unpin` implementation.
Since `Devres<DmaMappedSgt>` implements `Unpin`, `Owned<P>`
is `Unpin` and can be moved.

Moving an `Owned<P>` instance could also move the `sgt` field.
This would cause `DmaMappedSgt::sgt` to now point at an invalid location.
Using this pointer would then lead to undefined behavior.

Below is a PoC kernel module that demonstrates the bug. It shows
safe code calling `Pin::into_inner()` on a
`Pin<KBox<SGTable<Owned<P>>>>` and then `KBox::into_inner()`, which moves
the value. `DmaMappedSgt::sgt` now points to a stale location.
When `DmaMappedSgt` is dropped, `dma_unmap_sgtable()` dereferences
the stale pointer, resulting in a use-after-free.

// SPDX-License-Identifier: GPL-2.0

use kernel::{
    bindings, device, dma, error, faux, page,
    prelude::*,
    scatterlist::{Owned, SGTable},
};

module! {
    type: ScatterListPoC,
    name: "scatterlist_poc",
    authors: ["Priya Govindasamy"],
    description: "ScatterList PoC kernel module",
    license: "GPL",
}

struct ScatterListPoC {
    _dev: faux::Registration,
}

impl kernel::Module for ScatterListPoC {
    fn init(_module: &'static ThisModule) -> Result<Self> {

        let dev = faux::Registration::new(c"scatterlist-poc", None)?;
        let generic_dev: &device::Device<device::Bound> = dev.as_ref().as_ref();

        let raw_dev = core::ptr::from_ref(generic_dev)
            .cast_mut()
            .cast::<bindings::device>();

        // A faux device does not provide a DMA mask. Point its streaming mask
        // at its coherent mask storage and configure both before creating any 
        // DMA mappings.
        //
        // SAFETY: `raw_dev` points to the registered faux device, whose
        // allocation is stable for the lifetime of `dev`.
        unsafe {
            (*raw_dev).dma_mask = core::ptr::addr_of_mut!((*raw_dev).coherent_dma_mask);
            error::to_result(bindings::dma_set_mask_and_coherent(raw_dev, u64::MAX))?;
        }

        let pages = VVec::<u8>::with_capacity(4 * page::PAGE_SIZE, GFP_KERNEL)?;
        let sgt: Pin<KBox<SGTable<Owned<VVec<u8>>>>> = KBox::pin_init(SGTable::new(
                generic_dev,
                pages,
                dma::DataDirection::Bidirectional,
                GFP_KERNEL,
            ),
            GFP_KERNEL
        )?;

        let boxed = Pin::into_inner(sgt);
        let table = KBox::into_inner(boxed);
        drop(table);

        Ok(Self {
            _dev: dev,
        })
    }
}

impl Drop for ScatterListPoC {
    fn drop(&mut self) {
        pr_info!("scatterlist_poc: module unloaded\n");
    }
}

Output:
[  550.191296] ==================================================================
[  550.191312] BUG: KASAN: slab-use-after-free in rust_helper_dma_unmap_sgtable+0x3d/0xa0
[  550.191357] Read of size 8 at addr ffff8882f4db57a8 by task insmod/5029
[  550.191370]
[  550.191399] CPU: 1 UID: 0 PID: 5029 Comm: insmod Tainted: G           OE    N   7.3.0+ #71 PREEMPT(lazy)
[  550.191416] Tainted: [O]=OOT_MODULE, [E]=UNSIGNED_MODULE, [N]=TEST
[  550.191420] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1ubuntu2 04/01/2014
[  550.191430] Call Trace:
[  550.191439]  <TASK>
[  550.191459]  dump_stack_lvl+0x97/0xe0
[  550.191485]  print_report+0x175/0x6f0
[  550.191498]  ? __virt_addr_valid+0x18e/0x3b0
[  550.191515]  ? __virt_addr_valid+0x345/0x3b0
[  550.191525]  ? rust_helper_dma_unmap_sgtable+0x3d/0xa0
[  550.191533]  ? kasan_complete_mode_report_info+0x75/0x230
[  550.191546]  ? rust_helper_dma_unmap_sgtable+0x3d/0xa0
[  550.191553]  kasan_report+0xfa/0x130
[  550.191564]  ? rust_helper_dma_unmap_sgtable+0x3d/0xa0
[  550.191580]  __asan_report_load8_noabort+0x18/0x20
[  550.191588]  rust_helper_dma_unmap_sgtable+0x3d/0xa0
[  550.191599]  _RNvXCseDCAewP9wfK_15scatterlist_pocNtB2_14ScatterListPoCNtNtCsbuTvttuFvbr_6kernel6module6Module4init+0xac0/0xdf0 [scatterlist_poc]
[  550.191642]  init_module+0x15/0xff0 [scatterlist_poc]
[  550.191654]  do_one_initcall+0x119/0x4f0
[  550.191685]  ? do_init_module+0x63/0x740
[  550.191694]  ? __pfx_init_module+0x10/0x10 [scatterlist_poc]
[  550.191705]  ? __se_sys_finit_module+0x39d/0x5f0
[  550.191714]  ? __x64_sys_finit_module+0x7f/0x90
[  550.191723]  ? x64_sys_call+0xa66/0x2600
[  550.191732]  ? do_syscall_64+0x11c/0x3f0
[  550.191739]  ? entry_SYSCALL_64_after_hwframe+0x76/0x7e
[  550.191747]  ? kvm_sched_clock_read+0x15/0x40
[  550.191763]  ? __lock_acquire+0xeeb/0x1040
[  550.191783]  ? kvm_sched_clock_read+0x15/0x40
[  550.191796]  ? debug_smp_processor_id+0x1b/0x30
[  550.191806]  ? _raw_spin_unlock_irqrestore+0x38/0x90
[  550.191815]  ? __this_cpu_preempt_check+0x17/0x20
[  550.191823]  ? lockdep_hardirqs_on+0xdc/0x160
[  550.191832]  ? debug_smp_processor_id+0x1b/0x30
[  550.191842]  ? kasan_save_alloc_info+0x3a/0x50
[  550.191852]  ? __kasan_kmalloc+0x94/0xb0
[  550.191863]  ? __kmalloc_cache_noprof+0x356/0x6c0
[  550.191876]  ? do_init_module+0x63/0x740
[  550.191885]  ? __kmalloc_cache_noprof+0x160/0x6c0
[  550.191899]  ? kasan_unpoison+0x4c/0x70
[  550.191914]  ? kasan_poison+0x44/0x60
[  550.191939]  do_init_module+0x23f/0x740
[  550.191975]  load_module+0x32d1/0x3f20
[  550.192009]  __se_sys_finit_module+0x39d/0x5f0
[  550.192045]  __x64_sys_finit_module+0x7f/0x90
[  550.192057]  x64_sys_call+0xa66/0x2600
[  550.192066]  do_syscall_64+0x11c/0x3f0
[  550.192072]  ? entry_SYSCALL_64_after_hwframe+0x76/0x7e
[  550.192080]  ? __this_cpu_preempt_check+0x17/0x20
[  550.192088]  ? lockdep_hardirqs_on+0xdc/0x160
[  550.192095]  ? do_syscall_64+0x217/0x3f0
[  550.192108]  ? debug_smp_processor_id+0x1b/0x30
[  550.192118]  ? entry_SYSCALL_64_after_hwframe+0x76/0x7e
[  550.192125]  ? __this_cpu_preempt_check+0x17/0x20
[  550.192132]  ? lockdep_hardirqs_on+0xdc/0x160
[  550.192140]  ? do_syscall_64+0x217/0x3f0
[  550.192148]  ? __this_cpu_preempt_check+0x17/0x20
[  550.192160]  ? debug_smp_processor_id+0x1b/0x30
[  550.192171]  ? debug_smp_processor_id+0x1b/0x30
[  550.192177]  ? rcu_is_watching+0x1a/0xc0
[  550.192190]  ? trace_irq_disable+0x3c/0x130
[  550.192205]  ? do_syscall_64+0x6d/0x3f0
[  550.192211]  ? entry_SYSCALL_64_after_hwframe+0x76/0x7e
[  550.192222]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
[  550.192238] RIP: 0033:0x7fdbc511e9dd
[  550.192259] Code: 5b 41 5c c3 66 0f 1f 84 00 00 00 00 00 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 23 b4 0f 00 f7 d8 64 89 01 48
[  550.192270] RSP: 002b:00007ffd59690678 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[  550.192292] RAX: ffffffffffffffda RBX: 000055627cd6e7d0 RCX: 00007fdbc511e9dd
[  550.192298] RDX: 0000000000000000 RSI: 000055627be2ccd2 RDI: 0000000000000003
[  550.192303] RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
[  550.192308] R10: 0000000000000003 R11: 0000000000000246 R12: 000055627be2ccd2
[  550.192312] R13: 000055627cd6e790 R14: 000055627be2b888 R15: 000055627cd6e8e0
[  550.192332]  </TASK>
[  550.192336]
[  550.192338] Allocated by task 5029:
[  550.192480]
[  550.192482] Freed by task 5029:
[  550.192597]
[  550.192600] The buggy address belongs to the object at ffff8882f4db5780
[  550.192600]  which belongs to the cache kmalloc-64 of size 64
[  550.192613] The buggy address is located 40 bytes inside of
[  550.192613]  freed 64-byte region [ffff8882f4db5780, ffff8882f4db57c0)
[  550.192620]
[  550.192623] The buggy address belongs to the physical page:
[  550.192713]
[  550.192716] Memory state around the buggy address:
[  550.192725]  ffff8882f4db5680: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
[  550.192730]  ffff8882f4db5700: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
[  550.192734] >ffff8882f4db5780: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
[  550.192738]                                   ^
[  550.192743]  ffff8882f4db5800: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
[  550.192747]  ffff8882f4db5880: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
[  550.192751] ==================================================================

Priya Bala Govindasamy (1):
  rust: scatterlist: pin `Owned::sgt` to prevent invalidating
    `DmaMappedSgt::sgt`

 rust/kernel/scatterlist.rs | 3 +++
 1 file changed, 3 insertions(+)

-- 
2.34.1


             reply	other threads:[~2026-11-11 18:41 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-11 18:41 Priya Bala Govindasamy [this message]
2026-11-11 18:41 ` [PATCH 1/1] rust: scatterlist: pin `Owned::sgt` to prevent invalidating `DmaMappedSgt::sgt` Priya Bala Govindasamy

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=cover.1793652314.git.pgovind2@uci.edu \
    --to=pgovind2@uci.edu \
    --cc=a.hindborg@kernel.org \
    --cc=abdiel.janulgue@gmail.com \
    --cc=acourbot@nvidia.com \
    --cc=aliceryhl@google.com \
    --cc=ardalan@uci.edu \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun@kernel.org \
    --cc=dakr@kernel.org \
    --cc=daniel.almeida@collabora.com \
    --cc=driver-core@lists.linux.dev \
    --cc=dzueck@uci.edu \
    --cc=gary@garyguo.net \
    --cc=lossin@kernel.org \
    --cc=lyude@redhat.com \
    --cc=ojeda@kernel.org \
    --cc=robin.murphy@arm.com \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=tamird@kernel.org \
    --cc=tmgross@umich.edu \
    --cc=work@onurozkan.dev \
    --cc=zhiyunq@cs.ucr.edu \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox