qilingframework / qilingframework/qiling
statx returns a byte-swapped struct on big-endian guests (directories misdetected)
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 6.1k
- Forks
- 798
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 9
Description
statx returns a byte-swapped struct on big-endian guests (directories misdetected)
Describe the bug
ql_syscall_statx (qiling/os/posix/syscall/stat.py) always serializes the result with the little-endian Statx32 / Statx64 structs (ctypes.Structure), regardless of the guest's endianness:
if ql.arch.bits == 32:
Statx = Statx32
else:
Statx = Statx64
Every other stat-family struct in this file already has an explicit big-endian variant (LinuxMips64EBStat, LinuxARMEBStat, …) selected via ql.arch.endian. statx is the only one missing it, so on a big-endian guest (MIPS/MIPS64 EB, ARM EB, PPC, …) every field of struct statx is byte-swapped.
The most visible consequence is stx_mode: for a directory the type bits land in the wrong byte, so S_ISDIR is false on a big-endian guest. A directory is reported as a non-directory, and e.g. busybox ls / prints just the path name instead of listing the directory. Because modern glibc routes stat/fstatat through statx, this affects ordinary directory operations, not only direct statx callers.
Repro (pure Python, no guest binary)
This serializes the exact struct Qiling emits and shows it reads back wrong on a big-endian guest:
import os
from qiling.os.posix.syscall.stat import Statx64
st_mode = os.stat('/').st_mode # a directory: 0o40755
raw = bytes(Statx64(stx_mode=st_mode))
off = Statx64.stx_mode.offset
field = raw[off:off+2]
print('host / st_mode = 0o%o (S_IFDIR=%s)' % (st_mode, (st_mode & 0o170000) == 0o040000))
print('stx_mode bytes = %s (offset %d)' % (field.hex(), off))
le = int.from_bytes(field, 'little')
be = int.from_bytes(field, 'big')
print('LE guest reads mode = 0x%04x -> type 0o%o' % (le, le & 0o170000))
print('BE guest reads mode = 0x%04x -> type 0o%o' % (be, be & 0o170000))
Output:
host / st_mode = 0o40755 (S_IFDIR=True)
stx_mode bytes = ed41 (offset 28)
LE guest reads mode = 0x41ed -> type 0o40000 # S_IFDIR -> directory
BE guest reads mode = 0xed41 -> type 0o160000 # not S_IFDIR -> "not a directory"
S_IFDIR is 0o40000. The struct is written little-endian (ed 41), so a little-endian guest reads 0x41ed (correct) while a big-endian guest reads 0xed41 → 0o160000, which is not a directory type.
Expected behavior
statx should serialize the struct in the guest's byte order, so stx_mode (and every other field) reads back correctly. S_ISDIR should be true for a directory on big-endian guests, exactly as it already is on little-endian ones.
End-to-end confirmation
A static MIPS64 big-endian binary that calls statx(AT_FDCWD, "/", 0, STATX_BASIC_STATS, &stx) and prints DIR iff S_ISDIR(stx.stx_mode) (source: examples/src/linux/statx.c):
- Before the fix:
NOTDIR mode=176501(the byte-swapped mode) - After the fix:
DIR
Why it was never caught
statx is only emitted by recent glibc; the big-endian binaries in the test rootfs are older/static and use stat/fstat/lstat, which already have big-endian struct variants. And no existing test inspects a directory's mode via statx on a big-endian guest. (Same bug class as the already-fixed #521, where bind unpacked sin_family with the wrong endianness on MIPS MSB.)
Environment
- Qiling:
dev(reproduced through current HEAD); the Python repro above reproduces on any host. - Affected guests: any big-endian Linux target (MIPS/MIPS64 EB, ARM EB, PPC, …).
- Not arch-specific in Qiling code — it's the shared
statxsyscall handler.
Proposed fix
Add big-endian variants Statx32EB / Statx64EB (and StatxTimestamp32EB / StatxTimestamp64EB) as ctypes.BigEndianStructure, reusing the little-endian field layouts, and select them in ql_syscall_statx when ql.arch.endian == QL_ENDIAN.EB. The little-endian path is byte-for-byte unchanged.
Implemented on retrocpugeek:feature/mips64be:
- Fix:
Fix statx byte order on big-endian guests(commit7550983b) - Regression test:
test_elf.ELFTest.test_elf_linux_mips64eb_statx(assertsstatxreports/asDIR) +examples/src/linux/statx.c
Happy to open a PR against dev.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in qiling/os/posix/syscall/stat.py, focusing on ql_syscall_statx and the existing endian-specific stat structures. Compare the proposed Statx32EB/Statx64EB approach with the layouts already used there, then run test_elf.ELFTest.test_elf_linux_mips64eb_statx and examples/src/linux/statx.c. Done means the big-endian test reports / as DIR while the little-endian path remains unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- operating-systems, testing-qa
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100