mirror of
https://github.com/torvalds/linux.git
synced 2025-10-30 00:06:59 +02:00
syzkaller discovered the following crash: (kernel BUG) [ 44.607039] ------------[ cut here ]------------ [ 44.607422] kernel BUG at mm/userfaultfd.c:2067! [ 44.608148] Oops: invalid opcode: 0000 [#1] SMP DEBUG_PAGEALLOC KASAN NOPTI [ 44.608814] CPU: 1 UID: 0 PID: 2475 Comm: reproducer Not tainted 6.16.0-rc6 #1 PREEMPT(none) [ 44.609635] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014 [ 44.610695] RIP: 0010:userfaultfd_release_all+0x3a8/0x460 <snip other registers, drop unreliable trace> [ 44.617726] Call Trace: [ 44.617926] <TASK> [ 44.619284] userfaultfd_release+0xef/0x1b0 [ 44.620976] __fput+0x3f9/0xb60 [ 44.621240] fput_close_sync+0x110/0x210 [ 44.622222] __x64_sys_close+0x8f/0x120 [ 44.622530] do_syscall_64+0x5b/0x2f0 [ 44.622840] entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 44.623244] RIP: 0033:0x7f365bb3f227 Kernel panics because it detects UFFD inconsistency during userfaultfd_release_all(). Specifically, a VMA which has a valid pointer to vma->vm_userfaultfd_ctx, but no UFFD flags in vma->vm_flags. The inconsistency is caused in ksm_madvise(): when user calls madvise() with MADV_UNMEARGEABLE on a VMA that is registered for UFFD in MINOR mode, it accidentally clears all flags stored in the upper 32 bits of vma->vm_flags. Assuming x86_64 kernel build, unsigned long is 64-bit and unsigned int and int are 32-bit wide. This setup causes the following mishap during the &= ~VM_MERGEABLE assignment. VM_MERGEABLE is a 32-bit constant of type unsigned int, 0x8000'0000. After ~ is applied, it becomes 0x7fff'ffff unsigned int, which is then promoted to unsigned long before the & operation. This promotion fills upper 32 bits with leading 0s, as we're doing unsigned conversion (and even for a signed conversion, this wouldn't help as the leading bit is 0). & operation thus ends up AND-ing vm_flags with 0x0000'0000'7fff'ffff instead of intended 0xffff'ffff'7fff'ffff and hence accidentally clears the upper 32-bits of its value. Fix it by changing `VM_MERGEABLE` constant to unsigned long, using the BIT() macro. Note: other VM_* flags are not affected: This only happens to the VM_MERGEABLE flag, as the other VM_* flags are all constants of type int and after ~ operation, they end up with leading 1 and are thus converted to unsigned long with leading 1s. Note 2: After commit31defc3b01("userfaultfd: remove (VM_)BUG_ON()s"), this is no longer a kernel BUG, but a WARNING at the same place: [ 45.595973] WARNING: CPU: 1 PID: 2474 at mm/userfaultfd.c:2067 but the root-cause (flag-drop) remains the same. [akpm@linux-foundation.org: rust bindgen wasn't able to handle BIT(), from Miguel] Link: https://lore.kernel.org/oe-kbuild-all/202510030449.VfSaAjvd-lkp@intel.com/ Link: https://lkml.kernel.org/r/20251001090353.57523-2-acsjakub@amazon.de Fixes:7677f7fd8b("userfaultfd: add minor fault registration mode") Signed-off-by: Jakub Acs <acsjakub@amazon.de> Signed-off-by: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com> Acked-by: David Hildenbrand <david@redhat.com> Acked-by: SeongJae Park <sj@kernel.org> Tested-by: Alice Ryhl <aliceryhl@google.com> Tested-by: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com> Cc: Xu Xin <xu.xin16@zte.com.cn> Cc: Chengming Zhou <chengming.zhou@linux.dev> Cc: Peter Xu <peterx@redhat.com> Cc: Axel Rasmussen <axelrasmussen@google.com> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
117 lines
4 KiB
C
117 lines
4 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Header that contains the code (mostly headers) for which Rust bindings
|
|
* will be automatically generated by `bindgen`.
|
|
*
|
|
* Sorted alphabetically.
|
|
*/
|
|
|
|
/*
|
|
* First, avoid forward references to `enum` types.
|
|
*
|
|
* This workarounds a `bindgen` issue with them:
|
|
* <https://github.com/rust-lang/rust-bindgen/issues/3179>.
|
|
*
|
|
* Without this, the generated Rust type may be the wrong one (`i32`) or
|
|
* the proper one (typically `c_uint`) depending on how the headers are
|
|
* included, which in turn may depend on the particular kernel configuration
|
|
* or the architecture.
|
|
*
|
|
* The alternative would be to use casts and likely an
|
|
* `#[allow(clippy::unnecessary_cast)]` in the Rust source files. Instead,
|
|
* this approach allows us to keep the correct code in the source files and
|
|
* simply remove this section when the issue is fixed upstream and we bump
|
|
* the minimum `bindgen` version.
|
|
*
|
|
* This workaround may not be possible in some cases, depending on how the C
|
|
* headers are set up.
|
|
*/
|
|
#include <linux/hrtimer_types.h>
|
|
|
|
#include <linux/acpi.h>
|
|
#include <drm/drm_device.h>
|
|
#include <drm/drm_drv.h>
|
|
#include <drm/drm_file.h>
|
|
#include <drm/drm_gem.h>
|
|
#include <drm/drm_ioctl.h>
|
|
#include <kunit/test.h>
|
|
#include <linux/auxiliary_bus.h>
|
|
#include <linux/bitmap.h>
|
|
#include <linux/blk-mq.h>
|
|
#include <linux/blk_types.h>
|
|
#include <linux/blkdev.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/completion.h>
|
|
#include <linux/configfs.h>
|
|
#include <linux/cpu.h>
|
|
#include <linux/cpufreq.h>
|
|
#include <linux/cpumask.h>
|
|
#include <linux/cred.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/device/faux.h>
|
|
#include <linux/dma-direction.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/errname.h>
|
|
#include <linux/ethtool.h>
|
|
#include <linux/fdtable.h>
|
|
#include <linux/file.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/jump_label.h>
|
|
#include <linux/mdio.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/phy.h>
|
|
#include <linux/pid_namespace.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pm_opp.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/property.h>
|
|
#include <linux/random.h>
|
|
#include <linux/refcount.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/security.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/task_work.h>
|
|
#include <linux/tracepoint.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/xarray.h>
|
|
#include <trace/events/rust_sample.h>
|
|
|
|
#if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
|
|
// Used by `#[export]` in `drivers/gpu/drm/drm_panic_qr.rs`.
|
|
#include <drm/drm_panic.h>
|
|
#endif
|
|
|
|
/* `bindgen` gets confused at certain things. */
|
|
const size_t RUST_CONST_HELPER_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN;
|
|
const size_t RUST_CONST_HELPER_ARCH_KMALLOC_MINALIGN = ARCH_KMALLOC_MINALIGN;
|
|
const size_t RUST_CONST_HELPER_PAGE_SIZE = PAGE_SIZE;
|
|
const gfp_t RUST_CONST_HELPER_GFP_ATOMIC = GFP_ATOMIC;
|
|
const gfp_t RUST_CONST_HELPER_GFP_KERNEL = GFP_KERNEL;
|
|
const gfp_t RUST_CONST_HELPER_GFP_KERNEL_ACCOUNT = GFP_KERNEL_ACCOUNT;
|
|
const gfp_t RUST_CONST_HELPER_GFP_NOWAIT = GFP_NOWAIT;
|
|
const gfp_t RUST_CONST_HELPER___GFP_ZERO = __GFP_ZERO;
|
|
const gfp_t RUST_CONST_HELPER___GFP_HIGHMEM = ___GFP_HIGHMEM;
|
|
const gfp_t RUST_CONST_HELPER___GFP_NOWARN = ___GFP_NOWARN;
|
|
const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIONAL = BLK_FEAT_ROTATIONAL;
|
|
const fop_flags_t RUST_CONST_HELPER_FOP_UNSIGNED_OFFSET = FOP_UNSIGNED_OFFSET;
|
|
|
|
const xa_mark_t RUST_CONST_HELPER_XA_PRESENT = XA_PRESENT;
|
|
|
|
const gfp_t RUST_CONST_HELPER_XA_FLAGS_ALLOC = XA_FLAGS_ALLOC;
|
|
const gfp_t RUST_CONST_HELPER_XA_FLAGS_ALLOC1 = XA_FLAGS_ALLOC1;
|
|
const vm_flags_t RUST_CONST_HELPER_VM_MERGEABLE = VM_MERGEABLE;
|
|
|
|
#if IS_ENABLED(CONFIG_ANDROID_BINDER_IPC_RUST)
|
|
#include "../../drivers/android/binder/rust_binder.h"
|
|
#include "../../drivers/android/binder/rust_binder_events.h"
|
|
#include "../../drivers/android/binder/page_range_helper.h"
|
|
#endif
|