P Puranam, Ananta Srikar #
Message details and related content
Hi, I'm Ananta Srikar Puranam, a PhD student at Washington State University. I'm reporting this bug on behalf of my team: Dr. Swarn Priya and Dr. Basavesh Ammanaghatta Shivakumar (both Cc'd). The module loader accepts an allocated ELF section whose sh_addralign is not zero, one, or a power of two, and then places the section at an address that does not satisfy the declared alignment. ELF requires sh_addralign to be 0, 1, or a positive power of two, but nothing in the section-header validation checks it. Trigger ======= A matched pair built from one minimal external module whose only data is an 8-byte-aligned u64 in .data: control.ko .data sh_addralign = 8 (as built) invalid.ko .data sh_addralign = 5 (only that header field changed; the files differ in one byte) The module's init function prints the live address of the object and its remainder modulo 5 and modulo 8. Observed (current mainline, x86_64) =================================== control (sh_addralign=8): module_align_repro: address=ffffffffc03f6010 remainder_mod_5=2 remainder_mod_8=0 value=7 invalid (sh_addralign=5): insmod exit status: 0 module_align_repro: address=ffffffffc03f6010 remainder_mod_5=2 remainder_mod_8=0 value=7 (each message is one line in dmesg; wrapped here) The invalid module loads, and its section address is not a multiple of the declared alignment. There is no oops, warning, or other kernel message. Tested ====== - Mainline a91337fb5e8e ("Merge tag 'configfs-7.4-rc2' ..."), 7.4.0-rc1-00590-ga91337fb5e8e, x86_64 under KVM: invalid twin accepted with the alignment not honored in 3/3 loads; control loaded 8-byte aligned in 3/3 - Earlier validation on the same commit (separate build and guest): invalid twin accepted at ffffffffa0201010, remainder 4 modulo 5. Cause ===== The section layout in module_get_offset_and_type() does: offset = ALIGN(mod->mem[type].size, sechdr->sh_addralign ?: 1); ALIGN() is mask-based and assumes a power of two: ((x) + (a - 1)) & ~(a - 1). For a = 5 this is (x + 4) & ~4, which only clears bit 2, so the result is not, in general, a multiple of 5. elf_validity_cache_sechdrs() validates section types, offsets and sizes, but never checks sh_addralign, so the value reaches ALIGN() unchecked. The same unchecked value is also used for the per-cpu section (percpu_modalloc()), the kallsyms layout (kernel/module/kallsyms.c), and codetag sections. I have only exercised the ordinary .data layout path. This behavior is already present in the initial git import, 1da177e4c3f4 ("Linux-2.6.12-rc2"). Impact ====== Triggering requires CONFIG_MODULES=y and permission to load a module (CAP_SYS_MODULE). I observed a successful load with an alignment the loader does not honor, not a crash, memory corruption, or an unprivileged boundary crossing. Per Documentation/process/threat-model.rst I am treating this as an ELF validation and loader correctness bug, not a vulnerability. Standard toolchains do not emit such values; the input is a malformed module. Setup ===== Host: Ubuntu 26.04.1 LTS, x86_64 Toolchain: Ubuntu GCC 15.2.0 / GNU ld 2.46 (built with O=) Config: x86_64_defconfig plus kernel/configs/kvm_guest.config, resolved with olddefconfig; no other changes (full .config available) Module: out-of-tree, built with M= against the same build tree; the invalid twin is the built .ko with only .data's sh_addralign rewritten from 8 to 5 QEMU: qemu-system-x86_64 10.2.1 with KVM, -smp 16 -m 8G Guest: Ubuntu 26.04 amd64 rootfs (debootstrap); modules loaded with insmod over SSH The source reproducer (module and header-patching script) is available privately on request. Per Documentation/process/security-bugs.rst I have not posted it to the list. Fix === A proposed patch rejects a nonzero sh_addralign that is not a power of two in elf_validity_cache_sechdrs(), before any layout or allocation, with -ENOEXEC and: Invalid ELF section alignment 5 in section 9 On a91337fb5e8e with the patch applied (x86_64 KVM), the control still loaded and the invalid twin was rejected with ENOEXEC, with no oops or warning. I will send it as a separate patch. Tool transparency ================= The root-cause analysis, original reproducer and proposed fix were prepared with the help of an AI coding assistant (OpenAI Codex). The QEMU/KVM reproduction environment and the reproducer used for the results above were set up with Anthropic's Claude Code. I re-ran the reproduction myself and have checked the statements in this report. Thanks, Srikar