Skip to content

Commit 0e48da4

Browse files
retrocpugeekclaude
andcommitted
Add regression test for getdents64 record alignment
Calls ql_syscall_getdents64 on a directory and walks the returned linux_dirent64 records, asserting every record (and thus its leading u64 d_ino) starts on an 8-byte boundary. Before the fix d_reclen was not rounded up, so records were misaligned and a strict-alignment guest (e.g. MIPS) faulted with an unaligned load. Self-contained; runs on stock unicorn. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 012726f commit 0e48da4

1 file changed

Lines changed: 37 additions & 0 deletions

File tree

tests/test_elf.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,43 @@ def test_elf_linux_x86_getdents64(self):
725725

726726
del ql
727727

728+
# Regression for getdents64 record alignment. Each linux_dirent64 record
729+
# must be padded so the next record's leading u64 d_ino stays 8-byte
730+
# aligned; otherwise a strict-alignment guest (e.g. MIPS) faults with an
731+
# unaligned load while walking the buffer. x86 tolerates the unaligned read,
732+
# which is why it was never caught.
733+
def test_linux_getdents64_alignment(self):
734+
from qiling.const import QL_ENDIAN
735+
from qiling.os.posix.syscall.fcntl import ql_syscall_open
736+
from qiling.os.posix.syscall.unistd import ql_syscall_getdents64
737+
738+
ql = Qiling(code=b"\x00\x00\x00\x00", archtype=QL_ARCH.MIPS, ostype=QL_OS.LINUX,
739+
endian=QL_ENDIAN.EB, rootfs="../examples/rootfs/mips32_linux",
740+
verbose=QL_VERBOSE.OFF)
741+
742+
base = 0x100000
743+
ql.mem.map(base, 0x4000)
744+
path_ptr, buf_ptr = base, base + 0x100
745+
ql.mem.write(path_ptr, b"/\x00")
746+
747+
fd = ql_syscall_open(ql, path_ptr, 0, 0) # O_RDONLY on a directory
748+
self.assertGreaterEqual(fd, 0)
749+
750+
nbytes = ql_syscall_getdents64(ql, fd, buf_ptr, 0x2000)
751+
self.assertGreater(nbytes, 0)
752+
753+
# walk the linux_dirent64 records; every record (hence its leading u64
754+
# d_ino) must start at an 8-byte boundary. d_reclen is a u16 at offset 16.
755+
buf = bytes(ql.mem.read(buf_ptr, nbytes))
756+
off = 0
757+
while off < nbytes:
758+
self.assertEqual(off % 8, 0, f"dirent at offset {off} is not 8-byte aligned")
759+
d_reclen = int.from_bytes(buf[off + 16:off + 18], "big")
760+
self.assertNotEqual(d_reclen, 0)
761+
off += d_reclen
762+
763+
del ql
764+
728765
def test_memory_search(self):
729766
ql = Qiling(code=b"\xCC", archtype=QL_ARCH.X8664, ostype=QL_OS.LINUX, verbose=QL_VERBOSE.DEBUG)
730767

0 commit comments

Comments
 (0)