Skip to content

Commit

Permalink
Add partial implementation of recvfrom syscall (#1514)
Browse files Browse the repository at this point in the history
* Add partial implementation of recvfrom syscall

This partial implementation does not handle src_addr and addrlen
arguments, which means this recvfrom acts like a recv syscall

* Combine shared logic between recv and recvfrom; add more checks

* Add test for recvfrom

* Add known failure of opening new socket after closing previous

* Blacken

* Check if buf + count is in current memory

* Follow logic of Linux source code more closely

https://elixir.bootlin.com/linux/v5.2.13/source/net/socket.c#L1988

* Use access_ok to check buffer validity

* Update test to EBADF
  • Loading branch information
ekilmer authored and Eric Hennenfent committed Sep 10, 2019
1 parent 19ba4b6 commit 612d40b
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 7 deletions.
56 changes: 49 additions & 7 deletions manticore/platforms/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -2115,21 +2115,36 @@ def sys_accept4(self, sockfd, addr, addrlen, flags):
fd = self._open(sock)
return fd

def sys_recv(self, sockfd, buf, count, flags):
def sys_recv(self, sockfd, buf, count, flags, trace_str="_recv"):
data: bytes = bytes()
if not self.current.memory.access_ok(slice(buf, buf + count), "w"):
logger.info("RECV: buf within invalid memory. Returning EFAULT")
return -errno.EFAULT

try:
sock = self.files[sockfd]
except IndexError:
return -errno.EINVAL
sock = self._get_fd(sockfd)
except FdError:
return -errno.EBADF

if not isinstance(sock, Socket):
return -errno.ENOTSOCK

data = sock.read(count)
self.syscall_trace.append((trace_str, sockfd, data))
self.current.write_bytes(buf, data)
self.syscall_trace.append(("_recv", sockfd, data))

return len(data)

def sys_recvfrom(self, sockfd, buf, count, flags, src_addr, addrlen):
if src_addr != 0:
logger.warning("sys_recvfrom: Unimplemented non-NULL src_addr")

if addrlen != 0:
logger.warning("sys_recvfrom: Unimplemented non-NULL addrlen")

# TODO Unimplemented src_addr and addrlen, so act like sys_recv
return self.sys_recv(sockfd, buf, count, flags, trace_str="_recvfrom")

def sys_send(self, sockfd, buf, count, flags):
try:
sock = self.files[sockfd]
Expand Down Expand Up @@ -2940,7 +2955,7 @@ def sys_write(self, fd, buf, count):

return super().sys_write(fd, buf, count)

def sys_recv(self, sockfd, buf, count, flags):
def sys_recv(self, sockfd, buf, count, flags, trace_str="_recv"):
if issymbolic(sockfd):
logger.debug("Ask to read from a symbolic file descriptor!!")
raise ConcretizeArgument(self, 0)
Expand All @@ -2959,6 +2974,33 @@ def sys_recv(self, sockfd, buf, count, flags):

return super().sys_recv(sockfd, buf, count, flags)

def sys_recvfrom(self, sockfd, buf, count, flags, src_addr, addrlen):
if issymbolic(sockfd):
logger.debug("Ask to read from a symbolic file descriptor!!")
raise ConcretizeArgument(self, 0)

if issymbolic(buf):
logger.debug("Ask to read to a symbolic buffer")
raise ConcretizeArgument(self, 1)

if issymbolic(count):
logger.debug("Ask to read a symbolic number of bytes ")
raise ConcretizeArgument(self, 2)

if issymbolic(flags):
logger.debug("Submitted a symbolic flags")
raise ConcretizeArgument(self, 3)

if issymbolic(src_addr):
logger.debug("Submitted a symbolic source address")
raise ConcretizeArgument(self, 4)

if issymbolic(addrlen):
logger.debug("Submitted a symbolic address length")
raise ConcretizeArgument(self, 5)

return super().sys_recvfrom(sockfd, buf, count, flags, src_addr, addrlen)

def sys_accept(self, sockfd, addr, addrlen):
# TODO(yan): Transmit some symbolic bytes as soon as we start.
# Remove this hack once no longer needed.
Expand Down Expand Up @@ -3079,7 +3121,7 @@ def make_chr(c):
solve_to_fd(data, out)
elif fd == 2:
solve_to_fd(data, err)
if name in ("_recv"):
if name in ("_recv", "_recvfrom"):
solve_to_fd(data, net)
if name in ("_receive", "_read") and fd == 0:
solve_to_fd(data, inn)
Expand Down
51 changes: 51 additions & 0 deletions tests/native/test_syscalls.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import random
import socket
import unittest

import os
Expand Down Expand Up @@ -141,6 +142,56 @@ def test_chmod(self):

self.assertEqual(-errno.EPERM, self.linux.sys_chown(0x1100, 0, 0))

def test_recvfrom(self):
self.linux.current.memory.mmap(0x1000, 0x1000, "rw ")

sock_fd = self.linux.sys_socket(socket.AF_INET, socket.SOCK_STREAM, 0)
self.assertEqual(sock_fd, 3)
# Unimplemented
# self.linux.current.write_int(0x1000, 1, size=8 * 4)
# self.linux.sys_setsockopt(sock_fd, socket.SOL_SOCKET, socket.SO_REUSEPORT, 0x1000, 4)
self.linux.sys_bind(sock_fd, None, None)
self.linux.sys_listen(sock_fd, None)
conn_fd = self.linux.sys_accept(sock_fd, None, 0)
self.assertEqual(conn_fd, 4)

sock_obj = self.linux.files[conn_fd]
init_len = len(sock_obj.buffer)
BYTES = 5
wrote = self.linux.sys_recvfrom(conn_fd, 0x1100, BYTES, 0x0, 0x0, 0x0)
self.assertEqual(wrote, BYTES)

wrote = self.linux.sys_recvfrom(conn_fd, 0x0, 100, 0x0, 0x0, 0x0)
self.assertEqual(wrote, -errno.EFAULT)

remain_len = init_len - BYTES
self.assertEqual(remain_len, len(sock_obj.buffer))

wrote = self.linux.sys_recvfrom(conn_fd, 0x1100, remain_len + 10, 0x0, 0x0, 0x0)
self.assertEqual(wrote, remain_len)

wrote = self.linux.sys_recvfrom(conn_fd, 0x1100, 10, 0x0, 0x0, 0x0)
self.assertEqual(wrote, 0)

self.linux.sys_close(conn_fd)
wrote = self.linux.sys_recvfrom(conn_fd, 0x1100, 10, 0x0, 0x0, 0x0)
self.assertEqual(wrote, -errno.EBADF)

@unittest.expectedFailure
def test_multiple_sockets(self):
sock_fd = self.linux.sys_socket(socket.AF_INET, socket.SOCK_STREAM, 0)
self.assertEqual(sock_fd, 3)
self.linux.sys_bind(sock_fd, None, None)
self.linux.sys_listen(sock_fd, None)
conn_fd = self.linux.sys_accept(sock_fd, None, 0)
self.assertEqual(conn_fd, 4)
self.linux.sys_close(conn_fd)

conn_fd = -1
# Fails with "Name socket4 already in use"
conn_fd = self.linux.sys_accept(sock_fd, None, 0)
self.assertEqual(conn_fd, 4)

def test_unimplemented(self):
stubs = linux_syscall_stubs.SyscallStubs(default_to_fail=False)

Expand Down

0 comments on commit 612d40b

Please sign in to comment.