Skip to content

Commit

Permalink
Add syscall implementation for sys_newfstatat (#2545)
Browse files Browse the repository at this point in the history
* add newfstatat syscall

* add newfstatat syscall tests
  • Loading branch information
lordidiot committed May 16, 2022
1 parent 2d0be73 commit f8ad2df
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 0 deletions.
79 changes: 79 additions & 0 deletions manticore/platforms/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -3253,6 +3253,85 @@ def execute(self):

# 64bit syscalls

def sys_newfstatat(self, dfd, filename, buf, flag):
"""
Determines information about a file based on a relative path and a directory file descriptor.
:rtype: int
:param dfd: directory file descriptor.
:param filename: relative path to file.
:param buf: a buffer where data about the file will be stored.
:param flag: flags to control the query.
:return: C{0} on success, negative on error
"""

AT_SYMLINK_NOFOLLOW = 0x100 # Do not follow symbolic links.
AT_EMPTY_PATH = 0x1000 # Allow empty relative pathname
dfd = ctypes.c_int32(dfd).value
flag = ctypes.c_int32(flag).value
filename_addr = filename
filename = self.current.read_string(filename, 4096)

# Absolute path or relative to current working directory
if os.path.isabs(filename) or dfd == self.FCNTL_FDCWD:
return self.sys_newstat(filename_addr, buf)

# If pathname is an empty string, operate on the file referred to by dirfd
if not len(filename) and flag & AT_EMPTY_PATH:
return self.sys_newfstat(dfd, buf)

try:
f = self._get_fdlike(dfd)
except FdError as e:
logger.info(f"sys_newfstatat: invalid fd ({dfd}), returning -{errorcode(e.err)}")
return -e.err

if not isinstance(f, Directory):
return -errno.EISDIR

follow = not (flag & AT_SYMLINK_NOFOLLOW)
try:
stat = convert_os_stat(os.stat(filename, dir_fd=f.fileno(), follow_symlinks=follow))
except OSError as e:
return -e.errno

def add(width, val):
fformat = {2: "H", 4: "L", 8: "Q"}[width]
return struct.pack("<" + fformat, val)

def to_timespec(width, ts):
"Note: this is a platform-dependent timespec (8 or 16 bytes)"
return add(width, int(ts)) + add(width, int(ts % 1 * 1e9))

# From linux/arch/x86/include/uapi/asm/stat.h
# Numerous fields are native width-wide
nw = self.current.address_bit_size // 8

bufstat = add(nw, stat.st_dev) # long st_dev
bufstat += add(nw, stat.st_ino) # long st_ino

if self.current.address_bit_size == 64:
bufstat += add(nw, stat.st_nlink) # long st_nlink
bufstat += add(4, stat.st_mode) # 32 mode
bufstat += add(4, stat.st_uid) # 32 uid
bufstat += add(4, stat.st_gid) # 32 gid
bufstat += add(4, 0) # 32 _pad
else:
bufstat += add(2, stat.st_mode) # 16 mode
bufstat += add(2, stat.st_nlink) # 16 st_nlink
bufstat += add(2, stat.st_uid) # 16 uid
bufstat += add(2, stat.st_gid) # 16 gid

bufstat += add(nw, stat.st_rdev) # long st_rdev
bufstat += add(nw, stat.st_size) # long st_size
bufstat += add(nw, stat.st_blksize) # long st_blksize
bufstat += add(nw, stat.st_blocks) # long st_blocks
bufstat += to_timespec(nw, stat.st_atime) # long st_atime, nsec;
bufstat += to_timespec(nw, stat.st_mtime) # long st_mtime, nsec;
bufstat += to_timespec(nw, stat.st_ctime) # long st_ctime, nsec;

self.current.write_bytes(buf, bufstat)
return 0

def sys_newfstat(self, fd, buf):
"""
Determines information about a file based on its file descriptor.
Expand Down
32 changes: 32 additions & 0 deletions tests/native/test_syscalls.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ def test_dir_stat(self):
self.assertEqual(res, 0)
res = self.linux.sys_stat64(0x1100, 0x1200)
self.assertEqual(res, 0)
res = self.linux.sys_newfstatat(fd, 0x1FFF, 0x1200, 0x1000)
self.assertEqual(res, 0)
res = self.linux.sys_newfstatat(-100, 0x1100, 0x1200, 0)
self.assertEqual(res, 0)
res = self.linux.sys_newfstat(fd, 0x1200)
self.assertEqual(res, 0)
res = self.linux.sys_fstat(fd, 0x1200)
Expand All @@ -147,7 +151,11 @@ def test_dir_stat(self):
self.assertLess(res, 0)
res = self.linux.sys_stat64(0x1100, 0x1200)
self.assertLess(res, 0)
res = self.linux.sys_newfstatat(-100, 0x1100, 0x1200, 0)
self.assertLess(res, 0)
# The file descriptor is still valid even though the directory is gone
res = self.linux.sys_newfstatat(fd, 0x1FFF, 0x1200, 0x1000)
self.assertEqual(res, 0)
res = self.linux.sys_newfstat(fd, 0x1200)
self.assertEqual(res, 0)
res = self.linux.sys_fstat(fd, 0x1200)
Expand All @@ -161,7 +169,11 @@ def test_dir_stat(self):
self.assertLess(res, 0)
res = self.linux.sys_stat64(0x1100, 0x1200)
self.assertLess(res, 0)
res = self.linux.sys_newfstatat(-100, 0x1100, 0x1200, 0)
self.assertLess(res, 0)
# The file descriptor is still valid even though the directory is gone
res = self.linux.sys_newfstatat(fd, 0x1FFF, 0x1200, 0x1000)
self.assertEqual(res, 0)
res = self.linux.sys_newfstat(fd, 0x1200)
self.assertEqual(res, 0)
res = self.linux.sys_fstat(fd, 0x1200)
Expand All @@ -175,6 +187,10 @@ def test_dir_stat(self):
self.assertLess(res, 0)
res = self.linux.sys_stat64(0x1100, 0x1200)
self.assertLess(res, 0)
res = self.linux.sys_newfstatat(-100, 0x1100, 0x1200, 0)
self.assertLess(res, 0)
res = self.linux.sys_newfstatat(fd, 0x1FFF, 0x1200, 0x1000)
self.assertLess(res, 0)
res = self.linux.sys_newfstat(fd, 0x1200)
self.assertLess(res, 0)
res = self.linux.sys_fstat(fd, 0x1200)
Expand All @@ -197,6 +213,10 @@ def test_file_stat(self):
self.assertEqual(res, 0)
res = self.linux.sys_stat64(0x1100, 0x1200)
self.assertEqual(res, 0)
res = self.linux.sys_newfstatat(fd, 0x1FFF, 0x1200, 0x1000)
self.assertEqual(res, 0)
res = self.linux.sys_newfstatat(-100, 0x1100, 0x1200, 0)
self.assertEqual(res, 0)
res = self.linux.sys_newfstat(fd, 0x1200)
self.assertEqual(res, 0)
res = self.linux.sys_fstat(fd, 0x1200)
Expand All @@ -211,6 +231,10 @@ def test_file_stat(self):
self.assertLess(res, 0)
res = self.linux.sys_stat64(0x1100, 0x1200)
self.assertLess(res, 0)
res = self.linux.sys_newfstatat(-100, 0x1100, 0x1200, 0)
self.assertLess(res, 0)
res = self.linux.sys_newfstatat(fd, 0x1FFF, 0x1200, 0x1000)
self.assertEqual(res, 0)
res = self.linux.sys_newfstat(fd, 0x1200)
self.assertEqual(res, 0)
res = self.linux.sys_fstat(fd, 0x1200)
Expand All @@ -224,6 +248,10 @@ def test_file_stat(self):
self.assertLess(res, 0)
res = self.linux.sys_stat64(0x1100, 0x1200)
self.assertLess(res, 0)
res = self.linux.sys_newfstatat(-100, 0x1100, 0x1200, 0)
self.assertLess(res, 0)
res = self.linux.sys_newfstatat(fd, 0x1FFF, 0x1200, 0x1000)
self.assertEqual(res, 0)
res = self.linux.sys_newfstat(fd, 0x1200)
self.assertEqual(res, 0)
res = self.linux.sys_fstat(fd, 0x1200)
Expand All @@ -237,6 +265,10 @@ def test_file_stat(self):
self.assertLess(res, 0)
res = self.linux.sys_stat64(0x1100, 0x1200)
self.assertLess(res, 0)
res = self.linux.sys_newfstatat(-100, 0x1100, 0x1200, 0)
self.assertLess(res, 0)
res = self.linux.sys_newfstatat(fd, 0x1FFF, 0x1200, 0x1000)
self.assertLess(res, 0)
res = self.linux.sys_newfstat(fd, 0x1200)
self.assertLess(res, 0)
res = self.linux.sys_fstat(fd, 0x1200)
Expand Down

0 comments on commit f8ad2df

Please sign in to comment.