[Devel] [PATCH hci-8.1 0/2] mm, binfmt_elf: two fixes also posted to mainline
Konstantin Khorenko
khorenko at virtuozzo.com
Wed Oct 14 20:03:18 MSK 2026
Two independent fixes for bugs that mainline has as well. Both are
posted to mainline; the backport notes name what differs in this tree.
The MADV_DOEXEC series that follow (2 to 5) apply on top of this one,
and series 4 relies on patch 1.
Mainline
========
- Patch 1 is patch 1/2 of "mm/huge_memory: fix the copy of special
PMDs at fork", v3:
https://lore.kernel.org/r/20261014-b4-prep-vstor-149847-copy_huge_pmd-recheck-special-pmd-v3-0-85eaec8417f6@virtuozzo.com
Sashiko review:
https://sashiko.dev/#/patchset/20261014-b4-prep-vstor-149847-copy_huge_pmd-recheck-special-pmd-v3-0-85eaec8417f6@virtuozzo.com
The Sashiko review of v1 found the ppc64 issue that patch 2/2 there
fixes:
https://sashiko.dev/#/patchset/20261014-b4-prep-vstor-149847-copy_huge_pmd-recheck-special-pmd-v1-1-d225c60bc316@virtuozzo.com
- Patch 2 is "binfmt_elf: return the real error when program headers
cannot be loaded", v1:
https://lore.kernel.org/r/20261014-b4-prep-vstor-149847-binfmt_elf-enomem-load-elf-phdrs-v1-1-f39c74d55e42@virtuozzo.com
Sashiko review:
https://sashiko.dev/#/patchset/20261014-b4-prep-vstor-149847-binfmt_elf-enomem-load-elf-phdrs-v1-1-f39c74d55e42@virtuozzo.com
Patch 1: copy_huge_pmd() recheck
================================
fork copies a special (PFN) huge PMD from a value read without the page
table lock. vfio-pci zaps its BAR mappings with unmap_mapping_range(),
which takes no mmap lock, when device memory is disabled or the device
is reset. A fork that races with that zap leaves the child with a BAR
mapping the zap removed: the child keeps reaching MMIO that vfio-pci has
disabled instead of getting SIGBUS. Series 4 copies these PMDs on every
live-update exec.
In mainline this patch is now the first of two. The second deposits a
page table for the copied PMD on ppc64 in hash mode; it is not needed
here, as this tree has no huge pfnmap support on ppc64.
Patch 2: the real error from load_elf_phdrs()
=============================================
load_elf_phdrs() returns NULL on any failure, and load_elf_binary()
turns that into a fixed error: -ENOEXEC for the executable, -ELIBBAD
for its interpreter. The real cause is lost:
cause really execve() returns
------------------------------------- ---------- ----------------
bad e_phentsize, e_phnum 0 or too big bad file ENOEXEC (right)
file ends before the header table bad file ENOEXEC (right)
kmalloc() of the table fails ENOMEM ENOEXEC (wrong)
kernel_read() fails EIO/ENOMEM ENOEXEC (wrong)
For the interpreter every row gives ELIBBAD.
ENOEXEC says the file is not an executable, which is a property of the
file, not a transient failure:
- glibc's execvp() and execlp() take ENOEXEC as "not a binary" and run
the ELF file with /bin/sh, which reports a binary it cannot execute
instead of the ENOMEM or EIO.
- search_binary_handler() tries the next binary format instead of
returning the error.
- For the interpreter the caller gets ELIBBAD, "Accessing a corrupted
shared library", about an ld.so that is fine.
When it happens here: this tree lacks the v6.17 change that lets the
table grow past ELF_MIN_ALIGN, so the allocation fails in practice only
under fault injection; the read fails with an I/O error, or with
-ENOMEM when the page cache cannot take the pages.
Where it came from:
- Until commit 6a8d38945cf4 ("binfmt_elf: Hoist ELF program header
loading to a function") load_elf_binary() returned -ENOMEM and the
read error. That commit documented an ERR_PTR return but returned
NULL; commit cfc46ca4fdca later fixed the comment, not the code.
- Commit a9d9ef133f44 ("binfmt_elf: load interpreter program headers
earlier") gave the interpreter's headers the same NULL check, and with
it -ELIBBAD.
Testing
=======
On a KVM guest with the whole stack (series 1 to 5), release and debug
kernels:
- a fork-vs-revoke reproducer on a vfio-pci BAR (QXL in no-IOMMU mode):
no stale child mapping; on mainline the same reproducer with a delay
in the window shows the bug and that the patch closes it;
- failslab sweep over one execve(): ENOMEM instead of ENOEXEC/ELIBBAD,
execvp() no longer falls back to /bin/sh; a header table whose second
page fails to read gives EIO; truncated tables and e_phoff = 2^63
still give ENOEXEC/ELIBBAD;
- selftests/exec, the MADV_DOEXEC selftests, gup_test, a Container
start/exec/stop.
https://virtuozzo.atlassian.net/browse/VSTOR-150144
---
Konstantin Khorenko (2):
mm/huge_memory: recheck a special PMD under the lock in copy_huge_pmd()
binfmt_elf: return the real error when program headers cannot be loaded
fs/binfmt_elf.c | 51 +++++++++++++++++++++++++++++++++------------------
mm/huge_memory.c | 23 ++++++++++++++++-------
2 files changed, 49 insertions(+), 25 deletions(-)
---
base-commit: defa968cbe8c38f1ff0231042dee07f00d27b2c9
change-id: 20261014-b4-prep-khorenko-vstor-149847-1-upstream-fixes-4bb22a75a079
Best regards,
--
Konstantin Khorenko <khorenko at virtuozzo.com>
More information about the Devel
mailing list