mirror of
https://github.com/qilingframework/qiling.git
synced 2026-10-12 02:21:40 +00:00
Merge pull request #1363 from chinggg/ipc-msg
feat(os): add posix message queue syscalls
This commit is contained in:
@@ -1025,6 +1025,15 @@ SHMMNI = 4096 # max num of segs system wide
|
|||||||
HUGETLB_FLAG_ENCODE_SHIFT = 26
|
HUGETLB_FLAG_ENCODE_SHIFT = 26
|
||||||
HUGETLB_FLAG_ENCODE_MASK = 0x3f
|
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
|
# ipc syscall
|
||||||
SEMOP = 1
|
SEMOP = 1
|
||||||
SEMGET = 2
|
SEMGET = 2
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from collections import deque
|
||||||
from inspect import signature, Parameter
|
from inspect import signature, Parameter
|
||||||
from typing import Dict, TextIO, Tuple, Union, Callable, IO, List, Optional
|
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.const import QL_ARCH, QL_OS, QL_INTERCEPT
|
||||||
from qiling.exception import QlErrorSyscallNotFound
|
from qiling.exception import QlErrorSyscallNotFound
|
||||||
from qiling.os.os import QlOs
|
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
|
from qiling.utils import ql_get_module, ql_get_module_function
|
||||||
|
|
||||||
SYSCALL_PREF: str = f'ql_syscall_'
|
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)
|
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):
|
class QlOsPosix(QlOs):
|
||||||
|
|
||||||
def __init__(self, ql: Qiling):
|
def __init__(self, ql: Qiling):
|
||||||
@@ -203,6 +251,7 @@ class QlOsPosix(QlOs):
|
|||||||
self.stderr = self._stderr
|
self.stderr = self._stderr
|
||||||
|
|
||||||
self._shm = QlShm()
|
self._shm = QlShm()
|
||||||
|
self._msq = QlMsq()
|
||||||
|
|
||||||
def __get_syscall_mapper(self, archtype: QL_ARCH):
|
def __get_syscall_mapper(self, archtype: QL_ARCH):
|
||||||
qlos_path = f'.os.{self.type.name.lower()}.map_syscall'
|
qlos_path = f'.os.{self.type.name.lower()}.map_syscall'
|
||||||
@@ -397,4 +446,8 @@ class QlOsPosix(QlOs):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def shm(self):
|
def shm(self):
|
||||||
return self._shm
|
return self._shm
|
||||||
|
|
||||||
|
@property
|
||||||
|
def msq(self):
|
||||||
|
return self._msq
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from .fcntl import *
|
|||||||
from .futex import *
|
from .futex import *
|
||||||
from .ioctl import *
|
from .ioctl import *
|
||||||
from .mman import *
|
from .mman import *
|
||||||
|
from .msg import *
|
||||||
from .net import *
|
from .net import *
|
||||||
from .personality import *
|
from .personality import *
|
||||||
from .poll import *
|
from .poll import *
|
||||||
|
|||||||
@@ -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'
|
||||||
|
]
|
||||||
@@ -7,6 +7,7 @@ from qiling import Qiling
|
|||||||
from qiling.os.posix.const import *
|
from qiling.os.posix.const import *
|
||||||
|
|
||||||
from .shm 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):
|
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:
|
def __call_shmget(*args: int) -> int:
|
||||||
return ql_syscall_shmget(ql, args[0], args[1], args[2])
|
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 = {
|
ipc_call = {
|
||||||
SHMAT: __call_shmat,
|
SHMAT: __call_shmat,
|
||||||
SHMDT: __call_shmdt,
|
SHMDT: __call_shmdt,
|
||||||
SHMGET: __call_shmget
|
SHMGET: __call_shmget,
|
||||||
|
MSGGET: __call_msgget,
|
||||||
|
MSGSND: __call_msgsnd,
|
||||||
|
MSGRCV: __call_msgrcv
|
||||||
}
|
}
|
||||||
|
|
||||||
if call not in ipc_call:
|
if call not in ipc_call:
|
||||||
|
|||||||
Reference in New Issue
Block a user