Skip to content

Commit 6b4f5c8

Browse files
authored
Merge pull request #1272 from owl129/dev
Feature/Fix some error in syscall fcntl and getsockopt
2 parents 8585558 + 3ce6eb8 commit 6b4f5c8

File tree

7 files changed

+160
-4
lines changed

7 files changed

+160
-4
lines changed

qiling/os/posix/const.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,45 @@
107107
"IP_MULTICAST_ALL" : 0x0031,
108108
}
109109

110+
# https://github.com/torvalds/linux/blob/master/include/uapi/linux/tcp.h
111+
linux_socket_tcp_options = {
112+
"TCP_NODELAY" : 0x1,
113+
"TCP_MAXSEG" : 0x2,
114+
"TCP_CORK" : 0x3,
115+
"TCP_KEEPIDLE" : 0x4,
116+
"TCP_KEEPINTVL" : 0x5,
117+
"TCP_KEEPCNT" : 0x6,
118+
"TCP_SYNCNT" : 0x7,
119+
"TCP_LINGER2" : 0x8,
120+
"TCP_DEFER_ACCEPT" : 0x9,
121+
"TCP_WINDOW_CLAMP" : 0xa,
122+
"TCP_INFO" : 0xb,
123+
"TCP_QUICKACK" : 0xc,
124+
"TCP_CONGESTION" : 0xd,
125+
"TCP_MD5SIG" : 0xe,
126+
"TCP_THIN_LINEAR_TIMEOUTS" : 0x10,
127+
"TCP_THIN_DUPACK" : 0x11,
128+
"TCP_USER_TIMEOUT" : 0x12,
129+
"TCP_REPAIR" : 0x13,
130+
"TCP_REPAIR_QUEUE" : 0x14,
131+
"TCP_QUEUE_SEQ" : 0x15,
132+
"TCP_REPAIR_OPTIONS" : 0x16,
133+
"TCP_FASTOPEN" : 0x17,
134+
"TCP_TIMESTAMP" : 0x18,
135+
"TCP_NOTSENT_LOWAT" : 0x19,
136+
"TCP_CC_INFO" : 0x1a,
137+
"TCP_SAVE_SYN" : 0x1b,
138+
"TCP_SAVED_SYN" : 0x1c,
139+
"TCP_REPAIR_WINDOW" : 0x1d,
140+
"TCP_FASTOPEN_CONNECT" : 0x1e,
141+
"TCP_ULP" : 0x1f,
142+
"TCP_MD5SIG_EXT" : 0x20,
143+
"TCP_FASTOPEN_KEY" : 0x21,
144+
"TCP_FASTOPEN_NO_COOKIE" : 0x22,
145+
"TCP_ZEROCOPY_RECEIVE" : 0x23,
146+
"TCP_INQ" : 0x24,
147+
"TCP_TX_DELAY" : 0x25,
148+
}
110149

111150
macos_socket_ip_options = {
112151
"IP_TOS" : 0x0003,
@@ -338,6 +377,7 @@
338377
"SO_REUSEADDR" : 0x0004,
339378
"SO_KEEPALIVE" : 0x0008,
340379
"SO_DONTROUTE" : 0x0010,
380+
"SO_BINDTODEVICE" : 0x0019,
341381
"SO_BROADCAST" : 0x0020,
342382
"SO_LINGER" : 0x0080,
343383
"SO_OOBINLINE" : 0x0100,

qiling/os/posix/const_mapping.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,17 @@ def socket_domain_mapping(p: int, archtype: QL_ARCH, ostype: QL_OS) -> str:
154154
return _constant_mapping(p, socket_domain_map)
155155

156156

157+
def socket_tcp_option_mapping(t: int, archtype: QL_ARCH) -> str:
158+
socket_option_map = {
159+
QL_ARCH.X86: linux_socket_tcp_options,
160+
QL_ARCH.X8664: linux_socket_tcp_options,
161+
QL_ARCH.ARM: linux_socket_tcp_options,
162+
QL_ARCH.ARM64: linux_socket_tcp_options,
163+
QL_ARCH.MIPS: linux_socket_tcp_options,
164+
}[archtype]
165+
return _constant_mapping(t, socket_option_map)
166+
167+
157168
def socket_level_mapping(t: int, archtype: QL_ARCH) -> str:
158169
socket_level_map = {
159170
QL_ARCH.X86: linux_x86_socket_level,

qiling/os/posix/filestruct.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
44
#
55
import os
6-
from socket import socket, AddressFamily, SocketKind
6+
from socket import socket, AddressFamily, SocketKind, socketpair
77
from typing import Union
88

99
try:
@@ -40,6 +40,12 @@ def open(cls, domain: Union[AddressFamily, int], socktype: Union[SocketKind, int
4040

4141
return cls(s)
4242

43+
@classmethod
44+
def socketpair(cls, domain: Union[AddressFamily, int], socktype: Union[SocketKind, int], protocol: int):
45+
a, b = socketpair(domain, socktype, protocol)
46+
47+
return cls(a), cls(b)
48+
4349
def read(self, length: int) -> bytes:
4450
return os.read(self.__fd, length)
4551

qiling/os/posix/structs.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,16 @@ class iovec(Struct):
113113
)
114114

115115
return iovec
116+
117+
118+
def make_pollfd(archbits: int, endian: QL_ENDIAN):
119+
Struct = struct.get_aligned_struct(archbits, endian)
120+
121+
class pollfd(Struct):
122+
_fields_ = (
123+
('fd', ctypes.c_int32),
124+
('events', ctypes.c_int16),
125+
('revents', ctypes.c_int16)
126+
)
127+
128+
return pollfd

qiling/os/posix/syscall/fcntl.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ def ql_syscall_fcntl(ql: Qiling, fd: int, cmd: int, arg: int):
150150
regreturn = f.fcntl(cmd, arg)
151151

152152
elif cmd == F_SETFL:
153-
f.fcntl(cmd, arg)
153+
flags = ql_open_flag_mapping(ql, arg)
154+
f.fcntl(cmd, flags)
154155
regreturn = 0
155156

156157
else:

qiling/os/posix/syscall/poll.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,41 @@
44
#
55

66
from qiling import Qiling
7+
from qiling.const import QL_OS, QL_ENDIAN
8+
from qiling.os.posix.structs import *
9+
import select
10+
import ctypes
11+
712

813
def ql_syscall_poll(ql: Qiling, fds: int, nfds: int, timeout: int):
9-
return 0
14+
pollfd = make_pollfd(ql.arch.bits, ql.arch.endian)
15+
16+
if ql.host.os == QL_OS.LINUX:
17+
fn_map = {}
18+
try:
19+
p = select.poll()
20+
for i in range(nfds):
21+
with pollfd.ref(ql.mem, fds + ctypes.sizeof(pollfd) * i) as pf:
22+
# clear revents field
23+
pf.revents = 0
24+
25+
ql.log.debug(f"register poll fd {pf.fd}, event {pf.events}")
26+
fileno = ql.os.fd[pf.fd].fileno()
27+
fn_map[fileno] = i
28+
p.register(fileno, pf.events)
29+
30+
res_list = p.poll(timeout)
31+
regreturn = len(res_list)
32+
33+
for fn, revent in res_list:
34+
with pollfd.ref(ql.mem, fds + ctypes.sizeof(pollfd) * fn_map[fn]) as pf:
35+
ql.log.debug(f"receive event on fd {pf.fd}, revent {revent}")
36+
pf.revents = revent
37+
except Exception as e:
38+
ql.log.error(f'{e} {fds=}, {nfds=}, {timeout=}')
39+
regreturn = -1
40+
41+
return regreturn
42+
else:
43+
ql.log.warning(f'syscall poll not implemented')
44+
return 0

qiling/os/posix/syscall/socket.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from qiling import Qiling
1111
from qiling.const import QL_ARCH, QL_VERBOSE
12-
from qiling.os.posix.const_mapping import socket_type_mapping, socket_level_mapping, socket_domain_mapping, socket_ip_option_mapping, socket_option_mapping
12+
from qiling.os.posix.const_mapping import socket_type_mapping, socket_level_mapping, socket_domain_mapping, socket_ip_option_mapping, socket_tcp_option_mapping, socket_option_mapping
1313
from qiling.os.posix.const import *
1414
from qiling.os.posix.filestruct import ql_socket
1515
from qiling.os.posix.structs import *
@@ -126,6 +126,52 @@ def ql_syscall_socket(ql: Qiling, domain: int, socktype: int, protocol: int):
126126
return idx
127127

128128

129+
def ql_syscall_socketpair(ql: Qiling, socket_domain, socket_type, socket_protocol, sv: int):
130+
idx_list = [i for i in range(NR_OPEN) if ql.os.fd[i] is None]
131+
if len(idx_list) > 1:
132+
idx1, idx2 = idx_list[:2]
133+
134+
emu_socket_value = socket_type
135+
136+
# ql_socket.open should use host platform based socket_type.
137+
try:
138+
emu_socket_type = socket_type_mapping(socket_type, ql.arch.type)
139+
except KeyError:
140+
ql.log.error(f'Cannot convert emu_socket_type {emu_socket_value} to host platform based socket_type')
141+
raise
142+
143+
try:
144+
socket_type = getattr(socket, emu_socket_type)
145+
except AttributeError:
146+
ql.log.error(f'Cannot convert emu_socket_type {emu_socket_type}:{emu_socket_value} to host platform based socket_type')
147+
raise
148+
149+
ql.log.debug(f'Convert emu_socket_type {emu_socket_type}:{emu_socket_value} to host platform based socket_type {emu_socket_type}:{socket_type}')
150+
151+
try:
152+
sock1, sock2 = ql_socket.socketpair(socket_domain, socket_type, socket_protocol)
153+
154+
# save sock to ql
155+
ql.os.fd[idx1] = sock1
156+
ql.os.fd[idx2] = sock2
157+
158+
# save fd to &sv
159+
ql.mem.write(sv, ql.pack32(idx1))
160+
ql.mem.write(sv+4, ql.pack32(idx2))
161+
regreturn = 0
162+
163+
# May raise error: Protocol not supported
164+
except OSError as e:
165+
ql.log.debug(f'{e}: {socket_domain=}, {socket_type=}, {socket_protocol=}, {sv=}')
166+
regreturn = -1
167+
168+
socket_type = socket_type_mapping(socket_type, ql.arch.type)
169+
socket_domain = socket_domain_mapping(socket_domain, ql.arch.type, ql.os.type)
170+
ql.log.debug("socketpair(%s, %s, %s, %d) = %d" % (socket_domain, socket_type, socket_protocol, sv, regreturn))
171+
172+
return regreturn
173+
174+
129175
def ql_syscall_connect(ql: Qiling, sockfd: int, addr: int, addrlen: int):
130176
if sockfd not in range(NR_OPEN):
131177
return -1
@@ -218,6 +264,8 @@ def ql_syscall_getsockopt(ql: Qiling, sockfd: int, level: int, optname: int, opt
218264
# emu_opt_name is based on level
219265
if vsock_level_name == 'IPPROTO_IP':
220266
vsock_opt_name = socket_ip_option_mapping(vsock_opt, ql.arch.type, ql.os.type)
267+
elif vsock_level_name == 'IPPROTO_TCP':
268+
vsock_opt_name = socket_tcp_option_mapping(vsock_opt, ql.arch.type)
221269
else:
222270
vsock_opt_name = socket_option_mapping(vsock_opt, ql.arch.type)
223271

@@ -288,6 +336,8 @@ def ql_syscall_setsockopt(ql: Qiling, sockfd: int, level: int, optname: int, opt
288336
# emu_opt_name is based on level
289337
if vsock_level_name == 'IPPROTO_IP':
290338
vsock_opt_name = socket_ip_option_mapping(vsock_opt, ql.arch.type, ql.os.type)
339+
elif vsock_level_name == 'IPPROTO_TCP':
340+
vsock_opt_name = socket_tcp_option_mapping(vsock_opt, ql.arch.type)
291341
else:
292342
vsock_opt_name = socket_option_mapping(vsock_opt, ql.arch.type)
293343

0 commit comments

Comments
 (0)