Merge pull request #1363 from chinggg/ipc-msg

feat(os): add posix message queue syscalls
This commit is contained in:
xwings
2023-07-04 09:58:26 +08:00
committed by GitHub
5 changed files with 253 additions and 3 deletions
+9
View File
@@ -1025,6 +1025,15 @@ SHMMNI = 4096 # max num of segs system wide
HUGETLB_FLAG_ENCODE_SHIFT = 26
HUGETLB_FLAG_ENCODE_MASK = 0x3f
# see: https://elixir.bootlin.com/linux/v5.19.17/source/include/uapi/linux/msg.h
MSG_NOERROR = 0o10000 # no error if message is too big
MSG_EXCEPT = 0o20000 # recv any msg except of specified type
MSG_COPY = 0o40000 # copy (not remove) all queue messages
MSGMNI = 32000 # <= IPCMNI, max # of msg queue identifiers
MSGMAX = 8192 # <= INT_MAX, max size of message (bytes)
MSGMNB = 16384 # <= INT_MAX, default max size of a message queue
# ipc syscall
SEMOP = 1
SEMGET = 2
+55 -2
View File
@@ -3,6 +3,7 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from collections import deque
from inspect import signature, Parameter
from typing import Dict, TextIO, Tuple, Union, Callable, IO, List, Optional
@@ -26,7 +27,7 @@ from qiling.cc import QlCC, intel, arm, mips, riscv, ppc
from qiling.const import QL_ARCH, QL_OS, QL_INTERCEPT
from qiling.exception import QlErrorSyscallNotFound
from qiling.os.os import QlOs
from qiling.os.posix.const import NR_OPEN, errors
from qiling.os.posix.const import MSGMNB, NR_OPEN, errors
from qiling.utils import ql_get_module, ql_get_module_function
SYSCALL_PREF: str = f'ql_syscall_'
@@ -136,6 +137,53 @@ class QlShm:
return next((shmobj for shmobj in self.__shm.values() if shmobj.attach.count(shmaddr) > 0), None)
class QlMsgBuf:
def __init__(self, mtype: int, mtext: bytes) -> None:
self.mtype = mtype
self.mtext = mtext
# vaguely reflects a msqid64_ds structure
class QlMsqId:
def __init__(self, key: int, uid: int, gid: int, mode: int) -> None:
# ipc64_perm
self.key = key
self.uid = uid
self.gid = gid
self.mode = mode
self.queue = deque(maxlen=MSGMNB)
def __len__(self):
return len(self.queue)
class QlMsq:
def __init__(self) -> None:
self.__msq: Dict[int, QlMsqId] = {}
self.__id: int = 0x0F000000
def __len__(self) -> int:
return len(self.__msq)
def add(self, msq: QlMsqId) -> int:
msqid = self.__id
self.__msq[msqid] = msq
self.__id += 0x1000
return msqid
def remove(self, msqid: int) -> None:
del self.__msq[msqid]
def get_by_key(self, key: int) -> Tuple[int, Optional[QlMsqId]]:
return next(((msqid, msqobj) for msqid, msqobj in self.__msq.items() if msqobj.key == key), (-1, None))
def get_by_id(self, msqid: int) -> Optional[QlMsqId]:
return self.__msq.get(msqid, None)
class QlOsPosix(QlOs):
def __init__(self, ql: Qiling):
@@ -203,6 +251,7 @@ class QlOsPosix(QlOs):
self.stderr = self._stderr
self._shm = QlShm()
self._msq = QlMsq()
def __get_syscall_mapper(self, archtype: QL_ARCH):
qlos_path = f'.os.{self.type.name.lower()}.map_syscall'
@@ -397,4 +446,8 @@ class QlOsPosix(QlOs):
@property
def shm(self):
return self._shm
return self._shm
@property
def msq(self):
return self._msq
+1
View File
@@ -6,6 +6,7 @@ from .fcntl import *
from .futex import *
from .ioctl import *
from .mman import *
from .msg import *
from .net import *
from .personality import *
from .poll import *
+163
View File
@@ -0,0 +1,163 @@
from typing import Optional
from qiling import Qiling
from qiling.os.posix.const import *
from qiling.os.posix.posix import QlMsqId, QlMsgBuf
def __find_msg(msq: QlMsqId, msgtyp: int, msgflg: int) -> Optional[QlMsgBuf]:
# peek at a specific queue item
if msgflg & MSG_COPY:
if msgtyp >= len(msq.queue):
return -1 # ENOMSG
return msg.queue[msgtyp]
if msgtyp == 0:
predicate = lambda msg: True
elif msgtype > 0:
if msgflg & MSG_EXCEPT:
predicate = lambda msg: msg.mtype != msgtyp
else:
predicate = lambda msg: msg.mtype == msgtyp
elif msgtype < 0:
predicate = lambda msg: msg.mtype <= -msgtyp
return next((msg for msg in msq.queue if predicate(msg)), None)
def __perms(ql: Qiling, msq: QlMsqId, flag: int) -> int:
"""
# see: https://elixir.bootlin.com/linux/v5.19.17/source/ipc/util.c#L553
# check whether the user has permissions to access this message queue
# FIXME: should probably also use cuid and (c)gid, but we don't support it yet
# TODO: other ipc mechanisms like shm can also reuse this
"""
request_mode = (flag >> 6) | (flag >> 3) | flag
granted_mode = msq.mode
if ql.os.uid == msq.uid:
granted_mode >>= 6
# is there some bit set in requested_mode but not in granted_mode?
if request_mode & ~granted_mode & 0o007:
return -1 # EACCES
return 0
def ql_syscall_msgget(ql: Qiling, key: int, msgflg: int):
def __create_msq(key: int, flags: int) -> int:
"""Create a new message queue for the specified key.
Returns: msqid of the newly created message queue, -1 if an error has occurred
"""
if len(ql.os.msq) >= MSGMNI:
return -1 # ENOSPC
mode = flags & ((1 << 9) - 1)
msqid = ql.os.msq.add(QlMsqId(key, ql.os.uid, ql.os.gid, mode))
ql.log.debug(f'created a new msg queue: key = {key:#x}, mode = 0{mode:o}. assigned id: {msqid:#x}')
return msqid
# create new message queue
if key == IPC_PRIVATE:
msqid = __create_msq(key, msgflg)
else:
msqid, msq = ql.os.msq.get_by_key(key)
# a message queue with the specified key does not exist
if msq is None:
# the user asked to create a new one?
if msgflg & IPC_CREAT:
msqid = __create_msq(key, msgflg)
else:
return -1 # ENOENT
# a message queue with the specified key exists
else:
# the user asked to create a new one?
if msgflg & (IPC_CREAT | IPC_EXCL):
return -1 # EEXIST
if __perms(ql, msq, msgflg):
return -1 # EACCES
return msqid
def ql_syscall_msgsnd(ql: Qiling, msqid: int, msgp: int, msgsz: int, msgflg: int):
msq = ql.os.msq.get_by_id(msqid)
if msq is None:
return -1 # EINVAL
# Check if the user has write permissions for the message queue
if __perms(ql, msq, 0o222): # S_IWUGO
return -1 # EACCES
msg_type = ql.mem.read_ptr(msgp)
msg_text = ql.mem.read(msgp + ql.arch.pointersize, msgsz)
while True:
if len(msq.queue) < msq.queue.maxlen:
break
if msgflg & IPC_NOWAIT:
return -1 # EAGAIN
msq.queue.append(QlMsgBuf(msg_type, bytes(msg_text)))
return 0 # Success
def ql_syscall_msgrcv(ql: Qiling, msqid: int, msgp: int, msgsz: int, msgtyp: int, msgflg: int):
msq = ql.os.msq.get_by_id(msqid)
if msq is None:
return -1 # EINVAL
if msgflg & MSG_COPY:
if msgflg & MSG_EXCEPT or not (msgflg & IPC_NOWAIT):
return -1 # EINVAL
# Check if the user has read permissions for the message queue
if __perms(ql, msq, 0o444): # S_IRUGO
return -1 # EACCES
while True:
msg = __find_msg(msq, msgtyp, msgflg)
if msg is not None:
break
if msgflg & IPC_NOWAIT:
return -1 # ENOMSG
if not (msgflg & MSG_COPY):
msq.queue.remove(msg)
if msgsz < len(msg.mtext):
if not (msgflg & MSG_NOERROR):
return -1 # E2BIG
else:
sz = msgsz
else:
sz = len(msg.mtext)
ql.mem.write_ptr(msgp, msg.mtype)
ql.mem.write(msgp + ql.arch.pointersize, msg.mtext[:sz])
return sz # Success
__all__ = [
'ql_syscall_msgget',
'ql_syscall_msgsnd',
'ql_syscall_msgrcv'
]
+25 -1
View File
@@ -7,6 +7,7 @@ from qiling import Qiling
from qiling.os.posix.const import *
from .shm import *
from .msg import *
def ql_syscall_ipc(ql: Qiling, call: int, first: int, second: int, third: int, ptr: int, fifth: int):
@@ -27,11 +28,34 @@ def ql_syscall_ipc(ql: Qiling, call: int, first: int, second: int, third: int, p
def __call_shmget(*args: int) -> int:
return ql_syscall_shmget(ql, args[0], args[1], args[2])
def __call_msgget(*args: int) -> int:
return ql_syscall_msgget(ql, args[0], args[1])
def __call_msgsnd(*args: int) -> int:
return ql_syscall_msgsnd(ql, args[0], args[3], args[1], args[2])
def __call_msgrcv(*args: int) -> int:
if version == 0:
if args[3] == 0:
return -1 # EINVAL
msgp = ql.mem.read_ptr(args[3])
msgtyp = ql.mem.read_ptr(args[3] + ql.arch.pointersize)
else:
msgp = args[3]
msgtyp = args[4]
return ql_syscall_msgrcv(ql, args[0], msgp, args[1], msgtyp, args[2])
ipc_call = {
SHMAT: __call_shmat,
SHMDT: __call_shmdt,
SHMGET: __call_shmget
SHMGET: __call_shmget,
MSGGET: __call_msgget,
MSGSND: __call_msgsnd,
MSGRCV: __call_msgrcv
}
if call not in ipc_call: