glibc-compat,uclibc-compat: dual-ABI mmap shim (fix musl-caller EINVAL)#2119
Merged
widgetii merged 1 commit intoMay 21, 2026
Merged
Conversation
The static mmap shims from OpenIPC#2000 (uclibc) and OpenIPC#2110 (glibc) use the signature `void *mmap(..., uint32_t offset)`. That's correct for vendor blobs built with a 32-bit off_t but silently breaks for musl-built callers that get linked into the same executable. ARM EABI places the 6th mmap arg in different stack slots depending on off_t width: vendor 32-bit off_t: offset at SP+4 (slot right after `fd`) musl 64-bit off_t: SP+4..7 is 8-byte alignment padding, offset_lo at SP+8, offset_hi at SP+12 A `uint32_t offset` formal reads SP+4 — for musl callers that's uninitialised stack padding, so the kernel rejects the resulting pgoff with EINVAL. Invisible for minimal consumers like majestic (which only delegates to libmpi); bites the moment a consumer mixes vendor MPP with a non- trivial musl-static library that issues mmap from user code. The canonical reproducer is a dvr_home demo that statically links LVGL — HI_MPI_VENC_CreateChn succeeds (vendor path) but lv_linux_fbdev fails to mmap /dev/fb0 ("failed to map framebuffer device to memory: Invalid argument") even though hifb_demo's bare-mmap path in the exact same rootfs works fine. Fix: declare mmap/mmap64 variadic, va_arg-peek BOTH candidate slots (SP+4 and SP+8) as uint32_t — uint32_t avoids the 8-byte alignment that va_arg(ap, uint64_t) would apply — then pick the slot whose value is page-aligned. For the common offset=0 case from either ABI it resolves to 0 cleanly; for vendor MMZ mmaps with a non-zero page-aligned PA at SP+4 the heuristic still picks SP+4. Failure mode: uninit padding happens to be page-aligned by coincidence (~1/4096 calls). Kernel still validates the resulting mapping, so worst case is a one-off EINVAL at startup — never silent corruption. Applied identically to both shims; the stat() translators in uclibc-compat-static.c are untouched. Refs: OpenIPC#1992 (audit), OpenIPC#1993 (cv100 stat shim), OpenIPC#2000 (cv100 mmap split), OpenIPC#2110 (V2 SoC port). Bisected against dvr_home on hi3520dv200; PROBE inside lv_hifb_disp_init showed mmap(/dev/fb0, ..., 0) returning EINVAL with the old shim and the same call returning a valid mapping once the dual-ABI peek was in place.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The static mmap shims from #2000 (uclibc) and #2110 (glibc) use the signature
void *mmap(..., uint32_t offset). That's correct for vendor blobs built with a 32-bitoff_tbut silently breaks for musl-built callers that get linked into the same executable. ARM EABI places the 6th mmap arg in different stack slots depending onoff_twidth:off_tfd)off_tA
uint32_t offsetformal reads SP+4 — for musl callers that's uninitialised stack padding, so the kernel rejects the resulting pgoff withEINVAL.Symptom
Invisible for minimal consumers like majestic (which only delegates into
libmpi.so); bites the moment a consumer mixes vendor MPP with a non-trivial musl-static library that issuesmmapfrom user code. The canonical reproducer is a small dvr_home demo that statically links LVGL —HI_MPI_VENC_CreateChnsucceeds (vendor path, shim works as intended), butlv_linux_fbdevfails to mmap/dev/fb0:even though
hifb_demo's bare-mmap path in the exact same rootfs works fine — becausehifb_demodoesn't link the static shim and goes through musl's realmmap.Bisected by dropping an inline probe inside
lv_hifb_disp_initthat opens/dev/fb0and callsmmap(0, smem_len, ..., fd, 0):mmap FAILED errno=22 (Invalid argument)mmap OK at 0x407d9000VENC bring-up (
HI_MPI_VENC_CreateGroup/CreateChn/RegisterChn) all stay at0x0on all four channels — vendor path is unaffected.Fix
Declare
mmap/mmap64variadic andva_arg-peek both candidate slots (SP+4 and SP+8) asuint32_t—uint32_tavoids the 8-byte alignment thatva_arg(ap, uint64_t)would apply on ARM EABI — then pick the slot whose value is page-aligned. For the commonoffset=0case from either ABI it resolves to 0 cleanly; for vendor MMZ mmaps with a non-zero page-aligned PA at SP+4 the heuristic still picks SP+4.Failure mode: uninit padding happens to be page-aligned by coincidence (~1/4096 calls). The kernel still validates the resulting mapping, so worst case is a one-off
EINVALat startup — never silent corruption.Applied identically to both shims; the
stat()translators inuclibc-compat-static.care untouched.Test plan
make BOARD=hi3520dv200_litebuilds clean (cross-compile shim, link into demo binary).HI_MPI_VENC_CreateGroup/CreateChn/RegisterChnall return0x0for chns 0..3 (vendor path regression check).lv_linux_fbdev_createsuccessfully mmaps/dev/fb0(the original bug).uclibc-compat-static.ais hi3516cv100's primary consumer).Refs
🤖 Generated with Claude Code