# \[BUG\] module: non-power-of-two sh\_addralign is accepted and not honored

- From: Puranam, Ananta Srikar \<hidden\>
- Date: 2026-11-07 08:20:58 
- List: linux-modules
- Page: https://ratatoskr.run/linux-modules/2026/11/17883172
- Thread: https://ratatoskr.run/linux-modules/2026/11/17883172/t.md

```text
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
```
