qiling icon indicating copy to clipboard operation
qiling copied to clipboard

cannot pack inode in ql_syscall_fstat when rootfs on NTFS

Open alfink opened this issue 5 years ago • 8 comments

ql_syscall_fstat relies on os.fstat. On a WSL2 host, it returns a 64 bit inode if the rootfs is located on a NTFS partition. Qiling will crash when packing it into 32 bit.

Traceback (most recent call last):
  File "tendaac_httpd.py", line 76, in <module>
    my_sandbox(["rootfs/bin/httpd"], "rootfs")
  File "tendaac_httpd.py", line 70, in my_sandbox
    ql.run()
  File "/home/alfink/.local/lib/python3.8/site-packages/qiling-1.1.post1-py3.8.egg/qiling/core.py", line 198, in run
    self.os.run()
  File "/home/alfink/.local/lib/python3.8/site-packages/qiling-1.1.post1-py3.8.egg/qiling/os/linux/linux.py", line 130, in run
    raise self.ql.internal_exception
  File "/home/alfink/.local/lib/python3.8/site-packages/qiling-1.1.post1-py3.8.egg/qiling/utils.py", line 19, in wrapper
    return func(*args, **kw)
  File "/home/alfink/.local/lib/python3.8/site-packages/qiling-1.1.post1-py3.8.egg/qiling/core_hooks.py", line 121, in _hook_intr_cb
    ret = h.call(ql, intno)
  File "/home/alfink/.local/lib/python3.8/site-packages/qiling-1.1.post1-py3.8.egg/qiling/core_hooks.py", line 36, in call
    return self.callback(ql, *args)
  File "/home/alfink/.local/lib/python3.8/site-packages/qiling-1.1.post1-py3.8.egg/qiling/os/linux/linux.py", line 65, in hook_syscall
    return self.load_syscall(intno)
  File "/home/alfink/.local/lib/python3.8/site-packages/qiling-1.1.post1-py3.8.egg/qiling/os/posix/posix.py", line 167, in load_syscall
    self.syscall_map(self.ql, self.get_func_arg()[0], self.get_func_arg()[1], self.get_func_arg()[2], self.get_func_arg()[3], self.get_func_arg()[4], self.get_func_arg()[5])
  File "/home/alfink/.local/lib/python3.8/site-packages/qiling-1.1.post1-py3.8.egg/qiling/os/posix/syscall/stat.py", line 270, in ql_syscall_fstat
    fstat_buf += ql.pack(fstat_info.st_ino)
  File "/home/alfink/.local/lib/python3.8/site-packages/qiling-1.1.post1-py3.8.egg/qiling/core_struct.py", line 102, in pack
    return self.pack32(data)
  File "/home/alfink/.local/lib/python3.8/site-packages/qiling-1.1.post1-py3.8.egg/qiling/core_struct.py", line 63, in pack32
    return struct.pack('I', x)
struct.error: 'I' format requires 0 <= number <= 4294967295

alfink avatar Jul 26 '20 09:07 alfink

not too sure is a NTFS causing python to break ?

xwings avatar Jul 31 '20 14:07 xwings

its not python what breaks, maybe it was confusing that i said it crashes. it just terminates with an exception.

The problem is that ql_syscall_fstat packs the inode as a 32bit int (depending on the emulated arch). On NTFS, inodes are 64bit, so the inode that os.fstat() returns can be too large to fit into a 32bit int.

alfink avatar Jul 31 '20 14:07 alfink

maybe try set_syscall in the script from fstat to fstat64, and see what will happen ?

ucgJhe avatar Jul 31 '20 15:07 ucgJhe

we sort of fix this issue by wrapping fstat. will you be able to try it.

as usual. pull the latest dev branch.

xwings avatar Aug 13 '20 09:08 xwings

I "fixed" everything by moving the rootfs to another partition that is not NTFS, so it's not much of a problem for me anymore. However, pulling the latest dev branch broke my previously working scripts. I'm currently looking for the bugs, so expect some issues or PRs soon.

To give you some feedback: I tried it again on NTFS again and it seems not to work, it seems there is still the same problem as before in fstat and stat (struct.error: 'I' format requires 0 <= number <= 4294967295). I also tried the set_syscall thing, but the binary didn't really like using stat64 instead of stat.

alfink avatar Aug 14 '20 14:08 alfink

there are some more missing stat related syscall. i guess we cover abit more this time and not too sure did i miss out any. again, there are similar issue in macos too.

xwings avatar Aug 30 '20 05:08 xwings

Maybe its a solution to create fake inodes for the guest and map them to the host-inodes, similar to what is already done with the file descriptors

alfink avatar Aug 30 '20 19:08 alfink

I've experienced this same problem when using an ARM ELF (32 bit) binary stored a NTFS partition with the latest Qiling 1.3-dev.

Like @alfink mentioned, NTFS partitions always use 64 bit inode numbers. Currently ql_syscall_fstat64 unpacks the stat struct based on the value of ql.archtype (ARM 32 in my case) but being on a NTFS partition the resulting fstat64_info.st_ino value is 64 bit and doing fstat64_buf += ql.pack32(fstat64_info.st_ino) will crash.

The current workaround solution is to avoid runing 32 bit executables on NTFS partitions.


$ file int_overflow
int_overflow: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 3.2.0,
$ stat -c %i int_overflow
6473924465859801
Full Qiling traceback
  [x] [posix.py:181]
Traceback (most recent call last):
  File "/home/thezero/.local/lib/python3.6/site-packages/qiling/os/posix/posix.py", line 171, in load_syscall
    ret = self.syscall_map(self.ql, self.get_func_arg()[0], self.get_func_arg()[1], self.get_func_arg()[2], self.get_func_arg()[3], self.get_func_arg()[4], self.get_func_arg()[5])
  File "/home/thezero/.local/lib/python3.6/site-packages/qiling/os/posix/syscall/stat.py", line 167, in ql_syscall_fstat64
    fstat64_buf += ql.pack32(fstat64_info.st_ino)
  File "/home/thezero/.local/lib/python3.6/site-packages/qiling/core_struct.py", line 60, in pack32
    return struct.pack(self._fmt32, x)
struct.error: 'I' format requires 0 
    my_sandbox(["./int_overflow"], ".")
  File "example.py", line 7, in my_sandbox
    ql.run()
  File "/home/thezero/.local/lib/python3.6/site-packages/qiling/core.py", line 765, in run
    self.os.run()
  File "/home/thezero/.local/lib/python3.6/site-packages/qiling/os/linux/linux.py", line 149, in run
    self.ql.emu_start(self.ql.loader.elf_entry, self.exit_point, self.ql.timeout, self.ql.count)
  File "/home/thezero/.local/lib/python3.6/site-packages/qiling/core.py", line 957, in emu_start
    raise self._internal_exception
  File "/home/thezero/.local/lib/python3.6/site-packages/qiling/utils.py", line 157, in wrapper
    return func(*args, **kw)
  File "/home/thezero/.local/lib/python3.6/site-packages/qiling/core_hooks.py", line 119, in _hook_intr_cb
    ret = h.call(ql, intno)
  File "/home/thezero/.local/lib/python3.6/site-packages/qiling/core_hooks.py", line 35, in call
    return self.callback(ql, *args)
  File "/home/thezero/.local/lib/python3.6/site-packages/qiling/os/linux/linux.py", line 71, in hook_syscall
    return self.load_syscall(intno)
  File "/home/thezero/.local/lib/python3.6/site-packages/qiling/os/posix/posix.py", line 183, in load_syscall
    raise e
  File "/home/thezero/.local/lib/python3.6/site-packages/qiling/os/posix/posix.py", line 171, in load_syscall
    ret = self.syscall_map(self.ql, self.get_func_arg()[0], self.get_func_arg()[1], self.get_func_arg()[2], self.get_func_arg()[3], self.get_func_arg()[4], self.get_func_arg()[5])
  File "/home/thezero/.local/lib/python3.6/site-packages/qiling/os/posix/syscall/stat.py", line 167, in ql_syscall_fstat64
    fstat64_buf += ql.pack32(fstat64_info.st_ino)
  File "/home/thezero/.local/lib/python3.6/site-packages/qiling/core_struct.py", line 60, in pack32
    return struct.pack(self._fmt32, x)
struct.error: 'I' format requires 0 

TheZ3ro avatar Jan 13 '21 13:01 TheZ3ro

I guess this is a "unsolveable" issue. We strongly suggest WSL is a proper way to go.

xwings avatar Oct 06 '22 03:10 xwings