Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions qiling/os/posix/syscall/stat.py
Original file line number Diff line number Diff line change
Expand Up @@ -1413,17 +1413,45 @@ class Statx64(ctypes.Structure):

_pack_ = 4

# Big-endian counterparts of the statx structs. The kernel statx layout is the
# same on every architecture, so the big-endian variants reuse the little-endian
# field lists verbatim and only change the ctypes base class (and the nested
# timestamp type, which must itself be big-endian). Without these, statx() byte-
# swaps every field on a big-endian guest (e.g. MIPS/MIPS64 EB), so stx_mode
# loses its S_IFDIR bit and tools like `ls` treat directories as plain files.
class StatxTimestamp32EB(ctypes.BigEndianStructure):
_fields_ = StatxTimestamp32._fields_

class StatxTimestamp64EB(ctypes.BigEndianStructure):
_fields_ = StatxTimestamp64._fields_

def _statx_fields_eb(fields):
swap = {StatxTimestamp32: StatxTimestamp32EB, StatxTimestamp64: StatxTimestamp64EB}
return [(name, swap.get(ftype, ftype)) for (name, ftype) in fields]

class Statx32EB(ctypes.BigEndianStructure):
_fields_ = _statx_fields_eb(Statx32._fields_)
_pack_ = 8

class Statx64EB(ctypes.BigEndianStructure):
_fields_ = _statx_fields_eb(Statx64._fields_)
_pack_ = 4

# int statx(int dirfd, const char *restrict pathname, int flags,
# unsigned int mask, struct statx *restrict statxbuf);
def ql_syscall_statx(ql: Qiling, dirfd: int, path: int, flags: int, mask: int, buf_ptr: int):
is_eb = ql.arch.endian == QL_ENDIAN.EB

def statx_convert_timestamp(tv_sec, tv_nsec):
tv_sec = struct.unpack('i', struct.pack('f', tv_sec))[0]
tv_nsec = struct.unpack('i', struct.pack('f', tv_nsec))[0]

if ql.arch.bits == 32:
return StatxTimestamp32(tv_sec=tv_sec, tv_nsec=tv_nsec)
Timestamp = StatxTimestamp32EB if is_eb else StatxTimestamp32
else:
return StatxTimestamp64(tv_sec=tv_sec, tv_nsec=tv_nsec)
Timestamp = StatxTimestamp64EB if is_eb else StatxTimestamp64

return Timestamp(tv_sec=tv_sec, tv_nsec=tv_nsec)


def major(dev):
Expand All @@ -1438,9 +1466,9 @@ def minor(dev):
st = Stat(real_path, fd)

if ql.arch.bits == 32:
Statx = Statx32
Statx = Statx32EB if is_eb else Statx32
else:
Statx = Statx64
Statx = Statx64EB if is_eb else Statx64

stx = Statx(
stx_mask = 0x07ff, # STATX_BASIC_STATS
Expand Down
31 changes: 31 additions & 0 deletions tests/test_elf.py
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,37 @@ def test_elf_linux_x86_getdents64(self):

del ql

# Regression for statx() byte order on big-endian guests: the statx struct
# must be serialized in the guest's endianness. Otherwise stx_mode is byte-
# swapped and a directory loses its S_IFDIR bit (so e.g. `ls` treats a
# directory as a regular file). Exercised on a big-endian MIPS context.
def test_linux_statx_bigendian(self):
import stat as _stat
from qiling.const import QL_ENDIAN
from qiling.os.posix.syscall.stat import ql_syscall_statx, Statx32

ql = Qiling(code=b"\x00\x00\x00\x00", archtype=QL_ARCH.MIPS, ostype=QL_OS.LINUX,
endian=QL_ENDIAN.EB, rootfs="../examples/rootfs/mips32_linux",
verbose=QL_VERBOSE.OFF)

base = 0x100000
ql.mem.map(base, 0x1000)
path_ptr, buf_ptr = base, base + 0x200
ql.mem.write(path_ptr, b"/\x00")

AT_FDCWD = -100 & 0xffffffff
STATX_BASIC_STATS = 0x07ff
ret = ql_syscall_statx(ql, AT_FDCWD, path_ptr, 0, STATX_BASIC_STATS, buf_ptr)
self.assertEqual(ret, 0)

# the guest is big-endian, so it reads stx_mode in big-endian byte order;
# it must come back as a directory (S_IFDIR). Before the fix the struct
# was emitted little-endian and the type bits were lost.
mode = int.from_bytes(ql.mem.read(buf_ptr + Statx32.stx_mode.offset, 2), 'big')
self.assertTrue(_stat.S_ISDIR(mode))

del ql

def test_memory_search(self):
ql = Qiling(code=b"\xCC", archtype=QL_ARCH.X8664, ostype=QL_OS.LINUX, verbose=QL_VERBOSE.DEBUG)

Expand Down