From 9e21eb9d6699ede45382939039c2d04b3635dae2 Mon Sep 17 00:00:00 2001 From: retrocpugeek Date: Thu, 25 Jun 2026 22:38:50 +1000 Subject: [PATCH] Fix MIPS socket option (SO_*) values linux_mips_socket_options used generic/incorrect values. MIPS has its own SO_* numbering (arch/mips/include/uapi/asm/socket.h) with the buffer/timeout/ type options in the 0x1000 range. The table listed SO_SNDBUF=0x01, SO_RCVBUF=0x02 (should be 0x1001/0x1002), SO_SND/RCVLOWAT and SO_SND/RCVTIMEO_OLD all wrong, SO_RCVLOWAT=0x04 silently collided with SO_REUSEADDR (Enum alias), and SO_OOBINLINE/SO_REUSEPORT were 0x00. A guest setsockopt(SOL_SOCKET, SO_RCVBUF, ...) (e.g. busybox ping) therefore raised 'Could not convert emulated socket option 4098'. Correct all values to the MIPS uapi and add the missing SO_TYPE/SO_ERROR/SO_ACCEPTCONN/SO_PROTOCOL/ SO_DOMAIN. Add test_elf.ELFTest.test_setsockopt_mips_so_rcvbuf: opens a socket on a MIPS guest and asserts setsockopt(SOL_SOCKET, SO_RCVBUF) succeeds. Self-contained, runs on stock unicorn; fails on dev, passes with the fix. Co-Authored-By: Claude Opus 4.8 (1M context) --- qiling/os/posix/const.py | 44 ++++++++++++++++++++++++---------------- tests/test_elf.py | 27 ++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/qiling/os/posix/const.py b/qiling/os/posix/const.py index df4c5b587..535cf6d79 100644 --- a/qiling/os/posix/const.py +++ b/qiling/os/posix/const.py @@ -380,24 +380,32 @@ class linux_mips_socket_level(Enum): # https://docs.huihoo.com/doxygen/linux/kernel/3.7/arch_2mips_2include_2uapi_2asm_2socket_8h_source.html # https://github.com/torvalds/linux/blob/master/arch/mips/include/uapi/asm/socket.h class linux_mips_socket_options(Enum): - SO_DEBUG = 0x01 - SO_REUSEADDR = 0x04 - SO_KEEPALIVE = 0x08 - SO_DONTROUTE = 0x10 - SO_BINDTODEVICE = 0x19 - SO_BROADCAST = 0x20 - SO_LINGER = 0x80 - SO_OOBINLINE = 0x00 - SO_REUSEPORT = 0x00 - SO_SNDBUF = 0x01 - SO_RCVBUF = 0x02 - SO_SNDLOWAT = 0x03 - SO_RCVLOWAT = 0x04 - SO_SNDTIMEO_OLD = 0x05 - SO_RCVTIMEO_OLD = 0x06 - SO_TIMESTAMP_OLD = 0x1d - # SO_TIMESTAMPNS_OLD = 0x23 - # SO_TIMESTAMPING_OLD = 0x25 + # MIPS uses its own SO_* numbering (arch/mips/include/uapi/asm/socket.h); + # the buffer/timeout/type options live in the 0x1000 range, unlike the + # generic asm-generic values. + SO_DEBUG = 0x0001 + SO_REUSEADDR = 0x0004 + SO_KEEPALIVE = 0x0008 + SO_DONTROUTE = 0x0010 + SO_BINDTODEVICE = 0x0019 + SO_BROADCAST = 0x0020 + SO_LINGER = 0x0080 + SO_OOBINLINE = 0x0100 + SO_REUSEPORT = 0x0200 + SO_TYPE = 0x1008 + SO_ERROR = 0x1007 + SO_SNDBUF = 0x1001 + SO_RCVBUF = 0x1002 + SO_SNDLOWAT = 0x1003 + SO_RCVLOWAT = 0x1004 + SO_SNDTIMEO_OLD = 0x1005 + SO_RCVTIMEO_OLD = 0x1006 + SO_ACCEPTCONN = 0x1009 + SO_PROTOCOL = 0x1028 + SO_DOMAIN = 0x1029 + SO_TIMESTAMP_OLD = 0x001d + # SO_TIMESTAMPNS_OLD = 0x0023 + # SO_TIMESTAMPING_OLD = 0x0025 SO_TIMESTAMP_NEW = 0x3f SO_TIMESTAMPNS_NEW = 0x40 SO_TIMESTAMPING_NEW = 0x41 diff --git a/tests/test_elf.py b/tests/test_elf.py index 2798028b7..36287bffc 100644 --- a/tests/test_elf.py +++ b/tests/test_elf.py @@ -404,6 +404,33 @@ def my_puts(ql: Qiling): ql.run() del ql + def test_setsockopt_mips_so_rcvbuf(self): + # MIPS uses its own SO_* numbering (SO_RCVBUF = 0x1002, not the generic + # 0x08). The MIPS socket-option table had the wrong values, so e.g. + # busybox ping's setsockopt(SOL_SOCKET, SO_RCVBUF, ...) raised + # NotImplementedError ("Could not convert emulated socket option 4098"). + from qiling.const import QL_ENDIAN + from qiling.os.posix.syscall.socket import ql_syscall_socket, ql_syscall_setsockopt + + 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) + + AF_INET, SOCK_DGRAM = 2, 2 + SOL_SOCKET, SO_RCVBUF = 0xffff, 0x1002 # MIPS values + + fd = ql_syscall_socket(ql, AF_INET, SOCK_DGRAM, 0) + self.assertGreaterEqual(fd, 0) + + base = 0x100000 + ql.mem.map(base, 0x1000) + ql.mem.write_ptr(base, 16384, 4) # requested SO_RCVBUF size + + # must map SO_RCVBUF (0x1002) to the host option and succeed, not raise + self.assertEqual(ql_syscall_setsockopt(ql, fd, SOL_SOCKET, SO_RCVBUF, base, 4), 0) + + del ql + def test_elf_linux_arm_static(self): ql = Qiling(["../examples/rootfs/arm_linux/bin/arm_hello_static"], "../examples/rootfs/arm_linux", verbose=QL_VERBOSE.DEFAULT) all_mem = ql.mem.save()