From 593b8a6f07482ff06c07dc39ec6ec117f21cc3ce Mon Sep 17 00:00:00 2001 From: Sudipta Pandit Date: Thu, 9 Oct 2025 20:49:40 +0000 Subject: [PATCH 1/5] api: add the seccomp notify addfd support Add support for the seccomp notify addfd feature in the core API. It allows a caller to install fd in target's fd table. Signed-off-by: Sudipta Pandit --- doc/man/man3/seccomp_notify_addfd.3 | 1 + doc/man/man3/seccomp_notify_alloc.3 | 12 +++++++++++- include/seccomp.h.in | 30 +++++++++++++++++++++++++++++ src/api.c | 9 +++++++++ src/system.c | 23 ++++++++++++++++++++++ src/system.h | 7 +++++++ 6 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 doc/man/man3/seccomp_notify_addfd.3 diff --git a/doc/man/man3/seccomp_notify_addfd.3 b/doc/man/man3/seccomp_notify_addfd.3 new file mode 100644 index 00000000..a5da2513 --- /dev/null +++ b/doc/man/man3/seccomp_notify_addfd.3 @@ -0,0 +1 @@ +.so man3/seccomp_notify_alloc.3 \ No newline at end of file diff --git a/doc/man/man3/seccomp_notify_alloc.3 b/doc/man/man3/seccomp_notify_alloc.3 index cb1c0480..7ff78d1a 100644 --- a/doc/man/man3/seccomp_notify_alloc.3 +++ b/doc/man/man3/seccomp_notify_alloc.3 @@ -3,7 +3,7 @@ .SH NAME .\" ////////////////////////////////////////////////////////////////////////// seccomp_notify_alloc, seccomp_notify_free, seccomp_notify_receive, -seccomp_notify_respond, seccomp_notify_id_valid, seccomp_notify_fd \- Manage seccomp notifications +seccomp_notify_respond, seccomp_notify_id_valid, seccomp_notify_fd, seccomp_notify_addfd \- Manage seccomp notifications .\" ////////////////////////////////////////////////////////////////////////// .SH SYNOPSIS .\" ////////////////////////////////////////////////////////////////////////// @@ -16,6 +16,7 @@ seccomp_notify_respond, seccomp_notify_id_valid, seccomp_notify_fd \- Manage sec .BI "int seccomp_notify_respond(int " fd ", struct seccomp_notif_resp *" resp ")" .BI "int seccomp_notify_id_valid(int " fd ", uint64_t " id ")" .BI "int seccomp_notify_fd(const scmp_filter_ctx " ctx ")" +.BI "int seccomp_notify_addfd(int " fd ", struct seccomp_notif_addfd *" addfd ")" .sp Link with \fI\-lseccomp\fP. .fi @@ -54,6 +55,11 @@ race conditions. The .BR seccomp_notify_fd () returns the notification fd of a filter after it has been loaded. +.P +The +.BR seccomp_notify_addfd () +function enables the caller to install a file descriptor into the target's file descriptor table. +The id field of the struct should be the same as the id from the request. .\" ////////////////////////////////////////////////////////////////////////// .SH RETURN VALUE .\" ////////////////////////////////////////////////////////////////////////// @@ -67,6 +73,10 @@ The returns 0 if the id is valid, and -ENOENT if it is not. .P The +.BR seccomp_notify_addfd () +returns the installed fd number on success, and one of error codes mentioned below on failure. +.P +The .BR seccomp_notify_alloc (), .BR seccomp_notify_receive (), and diff --git a/include/seccomp.h.in b/include/seccomp.h.in index 38c50d29..eca947c6 100644 --- a/include/seccomp.h.in +++ b/include/seccomp.h.in @@ -405,6 +405,24 @@ struct seccomp_notif_resp { __s32 error; __u32 flags; }; + +#endif + +/* seccomp_notif_addfd and ADDFD_FLAG_SETFD was added in kernel v5.10 */ +#ifndef SECCOMP_ADDFD_FLAG_SETFD +#define SECCOMP_ADDFD_FLAG_SETFD (1UL << 0) +struct seccomp_notif_addfd { + __u64 id; + __u32 flags; + __u32 srcfd; + __u32 newfd; + __u32 newfd_flags; +}; +#endif + +/* Addfd and return it, atomically. ADDFD_FLAG_SEND was added in kernel 5.14 */ +#ifndef SECCOMP_ADDFD_FLAG_SEND +#define SECCOMP_ADDFD_FLAG_SEND (1UL << 1) #endif /* @@ -814,6 +832,18 @@ int seccomp_notify_id_valid(int fd, uint64_t id); */ int seccomp_notify_fd(const scmp_filter_ctx ctx); +/** + * Install a file descriptor into the target's process. + * @param fd the notification fd + * @param addfd the addfd structure + * + * This function enables the caller to install/add a fd into the + * target's fd table. Returns the installed fd number on success and, + * negative values on failure. + * + */ +int seccomp_notify_addfd(int fd, struct seccomp_notif_addfd *addfd); + /** * Generate seccomp Pseudo Filter Code (PFC) and export it to a file * @param ctx the filter context diff --git a/src/api.c b/src/api.c index 65a277a4..413cc81a 100644 --- a/src/api.c +++ b/src/api.c @@ -728,6 +728,15 @@ API int seccomp_notify_fd(const scmp_filter_ctx ctx) return _rc_filter(sys_notify_fd()); } +/* NOTE - function header comment in include/seccomp.h */ +API int seccomp_notify_addfd(int fd, struct seccomp_notif_addfd *addfd) +{ + /* force a runtime api level detection */ + _seccomp_api_update(); + + return _rc_filter(sys_notify_addfd(fd, addfd)); +} + /* NOTE - function header comment in include/seccomp.h */ API int seccomp_export_pfc(const scmp_filter_ctx ctx, int fd) { diff --git a/src/system.c b/src/system.c index f2709521..ba841c6c 100644 --- a/src/system.c +++ b/src/system.c @@ -573,3 +573,26 @@ int sys_notify_id_valid(int fd, uint64_t id) return -ENOENT; return 0; } + +/** + * Install a file descriptor into the target's process. + * @param fd the notification fd + * @param addfd the addfd structure + * + * Install a file descriptor into the target's process. Returns the + * installed fd number on success, negative values on failure. + * + */ +int sys_notify_addfd(int fd, struct seccomp_notif_addfd *addfd) +{ + int rc; + if (state.sup_user_notif <= 0) + return -EOPNOTSUPP; + + rc = ioctl(fd, SECCOMP_IOCTL_NOTIF_ADDFD, addfd); + if (rc < 0 && errno == EINVAL) + return -EOPNOTSUPP; + if (rc < 0) + return -ECANCELED; + return rc; +} diff --git a/src/system.h b/src/system.h index b2bc7749..e4b996f4 100644 --- a/src/system.h +++ b/src/system.h @@ -180,6 +180,12 @@ struct seccomp_notif_sizes { #endif /* SECCOMP_IOCTL_NOTIF_ID_VALID */ +/* SECCOMP_IOCTL_NOTIF_ADDFD was added in kernel v5.10 */ +#ifndef SECCOMP_IOCTL_NOTIF_ADDFD +#define SECCOMP_IOCTL_NOTIF_ADDFD SECCOMP_IOW(3, \ + struct seccomp_notif_addfd) +#endif + /* non-public ioctl number for backwards compat (see system.c) */ #define SECCOMP_IOCTL_NOTIF_ID_VALID_WRONG_DIR SECCOMP_IOR(2, __u64) @@ -202,4 +208,5 @@ int sys_notify_alloc(struct seccomp_notif **req, int sys_notify_receive(int fd, struct seccomp_notif *req); int sys_notify_respond(int fd, struct seccomp_notif_resp *resp); int sys_notify_id_valid(int fd, uint64_t id); +int sys_notify_addfd(int fd, struct seccomp_notif_addfd *addfd); #endif From 28089097edf33d751fd5f3a6298e92d128d83961 Mon Sep 17 00:00:00 2001 From: Sudipta Pandit Date: Thu, 9 Oct 2025 20:51:26 +0000 Subject: [PATCH 2/5] api: add python api for seccomp notify addfd Add seccomp notify addfd support to the Python API and update the receive_notify and respond_notify methods to accept user-provided notify fd's as an optional argument. Signed-off-by: Sudipta Pandit --- src/python/libseccomp.pxd | 8 ++ src/python/seccomp.pyx | 175 +++++++++++++++++++++++++++++++++++++- 2 files changed, 179 insertions(+), 4 deletions(-) diff --git a/src/python/libseccomp.pxd b/src/python/libseccomp.pxd index f2784881..ec5b0f70 100644 --- a/src/python/libseccomp.pxd +++ b/src/python/libseccomp.pxd @@ -117,6 +117,13 @@ cdef extern from "seccomp.h": int32_t error uint32_t flags + cdef struct seccomp_notif_addfd: + uint64_t id + uint32_t flags + uint32_t srcfd + uint32_t newfd + uint32_t newfd_flags + scmp_version *seccomp_version() unsigned int seccomp_api_get() @@ -165,6 +172,7 @@ cdef extern from "seccomp.h": void seccomp_notify_free(seccomp_notif *req, seccomp_notif_resp *resp) int seccomp_notify_receive(int fd, seccomp_notif *req) int seccomp_notify_respond(int fd, seccomp_notif_resp *resp) + int seccomp_notify_addfd(int fd, seccomp_notif_addfd *addfd) int seccomp_notify_id_valid(int fd, uint64_t id) int seccomp_notify_fd(scmp_filter_ctx ctx) diff --git a/src/python/seccomp.pyx b/src/python/seccomp.pyx index 7e03dc0e..eb065ad7 100644 --- a/src/python/seccomp.pyx +++ b/src/python/seccomp.pyx @@ -591,6 +591,139 @@ cdef class NotificationResponse: """ self._flags = value +cdef class NotificationAddfd: + """ Python object representing a seccomp notification addfd structure. + """ + cdef uint64_t _id + cdef uint32_t _flags + cdef uint32_t _srcfd + cdef uint32_t _newfd + cdef uint32_t _newfd_flags + + def __cinit__(self, notify, flags, srcfd, newfd = 0, newflags = 0): + """ Initialize the notification addfd structure. + + Arguments: + notify - a Notification object + srcfd - the source file descriptor + flags - notify addfd flags + newfd - 0 or desired file descriptor number in target + newflags - new flags to set on the target file descriptor + + Description: + Create a seccomp NotificationAddfd object. + """ + self._id = notify.id + self._flags = flags + self._srcfd = srcfd + self._newfd = newfd + self._newfd_flags = newflags + + @property + def id(self): + """ Get the seccomp notification request ID. + + Description: + Get the seccomp notification request ID. + """ + return self._id + + @id.setter + def id(self, value): + """ Set the seccomp notification request ID. + + Arguments: + id - the seccomp notification request ID + + Description: + Set the seccomp notification request ID. + """ + self._id = value + + @property + def flags(self): + """ Get the seccomp notification addfd flags. + + Description: + Get the seccomp notification addfd flags. + """ + return self._flags + + @flags.setter + def flags(self, value): + """ Set the seccomp notification addfd flags. + + Arguments: + flags - the notification addfd flags + + Description: + Set the seccomp notification addfd flags. + """ + self._flags = value + + @property + def srcfd(self): + """ Get the local file descriptor number. + + Description: + Get the local file descriptor number. + """ + return self._srcfd + + @srcfd.setter + def srcfd(self, value): + """ Set the local file descriptor number. + + Arguments: + srcfd - the local file descriptor number + + Description: + Set the local file descriptor number. + """ + self._srcfd = value + + @property + def newfd(self): + """ Get the target file descriptor number. + + Description: + Get the target file descriptor number. + """ + return self._newfd + + @newfd.setter + def newfd(self, value): + """ Set the target file descriptor number. + + Arguments: + newfd - the target file descriptor number + + Description: + Set the target file descriptor number. + """ + self._newfd = value + + @property + def newflags(self): + """ Get the new flags to set on the target file descriptor. + + Description: + Get the new flags to set on the target file descriptor. + """ + return self._newfd_flags + + @newflags.setter + def newflags(self, value): + """ Set the new flags to set on the target file descriptor. + + Arguments: + newflags - the new flags to set on the target file descriptor + + Description: + Set the new flags to set on the target file descriptor. + """ + self._newfd_flags = value + cdef class SyscallFilter: """ Python object representing a seccomp syscall filter. """ cdef int _defaction @@ -959,16 +1092,20 @@ cdef class SyscallFilter: if rc != 0: raise RuntimeError(str.format("Library error (errno = {0})", rc)) - def receive_notify(self): + def receive_notify(self, fd = None): """ Receive seccomp notifications. + Arguments: + fd - the notify file descriptor + Description: Receive a seccomp notification from the system, requires the use of the NOTIFY action. """ cdef libseccomp.seccomp_notif *req - fd = libseccomp.seccomp_notify_fd(self._ctx) + if fd is None: + fd = libseccomp.seccomp_notify_fd(self._ctx) if fd < 0: raise RuntimeError("Notifications not enabled/active") rc = libseccomp.seccomp_notify_alloc(&req, NULL) @@ -988,18 +1125,20 @@ cdef class SyscallFilter: free(req) return notify - def respond_notify(self, response): + def respond_notify(self, response, fd = None): """ Send a seccomp notification response. Arguments: response - the response to send to the system + fd - the notify file descriptor Description: Respond to a seccomp notification. """ cdef libseccomp.seccomp_notif_resp *resp - fd = libseccomp.seccomp_notify_fd(self._ctx) + if fd is None: + fd = libseccomp.seccomp_notify_fd(self._ctx) if fd < 0: raise RuntimeError("Notifications not enabled/active") rc = libseccomp.seccomp_notify_alloc(NULL, &resp) @@ -1026,6 +1165,34 @@ cdef class SyscallFilter: raise RuntimeError("Notifications not enabled/active") return fd + def notify_addfd(self, addfd, fd = None): + """Add a file descriptor to target + + Arguments: + addfd - the addfd object + fd - the notify file descriptor + + Description: + Add a file descriptor to the target process. + """ + if fd is None: + fd = libseccomp.seccomp_notify_fd(self._ctx) + if fd < 0: + raise RuntimeError("Notifications not enabled/active") + + cdef libseccomp.seccomp_notif_addfd _addfd + + _addfd.id = addfd.id + _addfd.flags = addfd.flags + _addfd.srcfd = addfd.srcfd + _addfd.newfd = addfd.newfd + _addfd.newfd_flags = addfd.newflags + + rc = libseccomp.seccomp_notify_addfd(fd, &_addfd) + if rc < 0: + raise RuntimeError(str.format("Library error (errno = {0})", rc)) + return rc + def export_pfc(self, file): """ Export the filter in PFC format. From 4188036dc89e59ad85eaf85e779af75a029520d6 Mon Sep 17 00:00:00 2001 From: Sudipta Pandit Date: Thu, 9 Oct 2025 20:53:49 +0000 Subject: [PATCH 3/5] tests: add test 63-live-notify_addfd Add live test for seccomp_notify_addfd() that verifies fd injection capabilities by intercepting openat() syscalls and replacing the target's file descriptor for it's C and Python API. Signed-off-by: Sudipta Pandit --- tests/.gitignore | 1 + tests/63-live-notify_addfd.c | 236 +++++++++++++++++++++++++++++++ tests/63-live-notify_addfd.py | 101 +++++++++++++ tests/63-live-notify_addfd.tests | 11 ++ tests/Makefile.am | 9 +- 5 files changed, 355 insertions(+), 3 deletions(-) create mode 100644 tests/63-live-notify_addfd.c create mode 100644 tests/63-live-notify_addfd.py create mode 100644 tests/63-live-notify_addfd.tests diff --git a/tests/.gitignore b/tests/.gitignore index c66e85f2..854271e2 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -70,3 +70,4 @@ util.pyc 60-sim-precompute 61-sim-transactions 62-sim-arch_transactions +63-live-notify_addfd diff --git a/tests/63-live-notify_addfd.c b/tests/63-live-notify_addfd.c new file mode 100644 index 00000000..f95985eb --- /dev/null +++ b/tests/63-live-notify_addfd.c @@ -0,0 +1,236 @@ +/** + * Seccomp Library test program + * + * Copyright (c) 2025 Microsoft Corporation + * Author: Sudipta Pandit + */ + +/* + * This library is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License as + * published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +int send_fd(int sock, int fd) +{ + struct iovec iov = {.iov_base = "F", .iov_len = 1}; + char buffer[CMSG_SPACE(sizeof(fd))]; + memset(buffer, 0, sizeof(buffer)); + + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = buffer, + .msg_controllen = sizeof(buffer) + }; + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); + + return sendmsg(sock, &msg, 0); +} + +int recv_fd(int sock) +{ + char m_buffer[1]; + struct iovec iov = {.iov_base = m_buffer, .iov_len = 1}; + + char buffer[CMSG_SPACE(sizeof(int))]; + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = buffer, + .msg_controllen = sizeof(buffer) + }; + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + + if (recvmsg(sock, &msg, 0) < 0) + return -1; + + int fd; + memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd)); + return fd; +} + +void child_process(scmp_filter_ctx ctx, int sock_fd) +{ + int rc; + int ret = -1; + int notify_fd = -1; + char buf[128]; + ssize_t bytes_read = -1; + + rc = seccomp_load(ctx); + if (rc < 0) + goto out; + + rc = seccomp_notify_fd(ctx); + if (rc < 0) + goto out; + notify_fd = rc; + + rc = send_fd(sock_fd, notify_fd); + if (rc < 0) { + rc = -errno; + goto out; + } + + ret = openat(AT_FDCWD, "/etc/hostname", O_RDONLY); + if (ret < 0) { + rc = -errno; + goto out; + } + + bytes_read = read(ret, buf, sizeof(buf)); + rc = bytes_read; + +out: + if (notify_fd >= 0) + close(notify_fd); + if (ret >= 0) + close(ret); + close(sock_fd); + exit(rc); +} + +int parent_process(int sock_fd) +{ + int rc; + int notify_fd = -1; + int new_fd = -1; + int installed_fd = -1; + struct seccomp_notif *req = NULL; + struct seccomp_notif_resp *resp = NULL; + struct seccomp_notif_addfd addfd = {0}; + + rc = recv_fd(sock_fd); + if (rc < 0) { + rc = -errno; + goto out; + } + notify_fd = rc; + + rc = seccomp_notify_alloc(&req, &resp); + if (rc) + goto out; + + rc = seccomp_notify_receive(notify_fd, req); + if (rc) + goto out; + if (req->data.nr != __NR_openat) { + rc = -EFAULT; + goto out; + } + + new_fd = openat(AT_FDCWD, "/dev/null", O_RDONLY); + if (new_fd < 0) { + rc = -errno; + goto out; + } + + memset(&addfd, 0, sizeof(addfd)); + addfd.id = req->id; + addfd.srcfd = new_fd; + addfd.newfd = 0; + addfd.flags = 0; + rc = seccomp_notify_addfd(notify_fd, &addfd); + if (rc < 0) + goto out; + installed_fd = rc; + + rc = seccomp_notify_id_valid(notify_fd, req->id); + if (rc) + goto out; + + resp->id = req->id; + resp->val = installed_fd; + resp->error = 0; + resp->flags = 0; + rc = seccomp_notify_respond(notify_fd, resp); + +out: + if (notify_fd >= 0) + close(notify_fd); + if (new_fd >= 0) + close(new_fd); + close(sock_fd); + seccomp_notify_free(req, resp); + return rc; +} + +int main(int argc, char *argv[]) +{ + int rc, status; + int sock_pair[2]; + scmp_filter_ctx ctx = NULL; + pid_t pid = 0; + + ctx = seccomp_init(SCMP_ACT_ALLOW); + if (ctx == NULL) + return ENOMEM; + + rc = seccomp_rule_add(ctx, SCMP_ACT_NOTIFY, SCMP_SYS(openat), 0, NULL); + if (rc) + goto out; + + /* set up socket pair for sending notify_fd */ + rc = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock_pair); + if (rc < 0) { + rc = -errno; + goto out; + } + + pid = fork(); + if (pid == 0) { + close(sock_pair[0]); /* close the parent's end */ + child_process(ctx, sock_pair[1]); + } else { + close(sock_pair[1]); /* close the child's end */ + rc = parent_process(sock_pair[0]); + + if (waitpid(pid, &status, 0) != pid) { + rc = -EFAULT; + goto out; + } + + if (!WIFEXITED(status)) { + rc = -EFAULT; + goto out; + } + if (WEXITSTATUS(status)) { + rc = -EFAULT; + goto out; + } + } + +out: + if (pid) + kill(pid, SIGKILL); + seccomp_release(ctx); + + if (rc != 0) + return (rc < 0 ? -rc : rc); + return 160; +} diff --git a/tests/63-live-notify_addfd.py b/tests/63-live-notify_addfd.py new file mode 100644 index 00000000..16eb357e --- /dev/null +++ b/tests/63-live-notify_addfd.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python + +# +# Seccomp Library test program +# +# Copyright (c) 2025 Microsoft Corporation +# Author: Sudipta Pandit + + +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of version 2.1 of the GNU Lesser General Public License as +# published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, see . +# + +import argparse +import ctypes +import ctypes.util +import os +import struct +import socket + +import util + +from seccomp import * + + +def send_fd(sock: socket.socket, fd: int): + sock.sendmsg([b' '], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, struct.pack('i', fd))]) + +def recv_fd(sock: socket.socket): + _msg, ancdata, _flags, _addr = sock.recvmsg(1, socket.CMSG_LEN(struct.calcsize('i'))) + for cmsg_level, cmsg_type, cmsg_data in ancdata: + if cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS: + return struct.unpack('i', cmsg_data)[0] + return None + +def test(): + f = SyscallFilter(ALLOW) + f.add_rule(NOTIFY, "openat") + + # Socketpair for sending file descriptors + p_socket, c_socket = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM) + + pid = os.fork() + if pid == 0: + # load seccomp filter + f.load() + notify_fd = f.get_notify_fd() + send_fd(c_socket, notify_fd) + + ret_fd = os.open("/etc/hostname", os.O_RDONLY) + if ret_fd < 0: + quit(ret_fd) + + ret_bytes = os.read(ret_fd, 128) + os.close(ret_fd) + if len(ret_bytes) != 0: + # /dev/null should be empty + quit(1) + + os.close(notify_fd) + c_socket.close() + quit(0) + else: + # get the notification fd from child + notify_fd = recv_fd(p_socket) + notify = f.receive_notify(fd=notify_fd) + + if notify.syscall != resolve_syscall(Arch(), "openat"): + raise RuntimeError("Notification failed") + + new_fd = os.open("/dev/null", os.O_RDONLY) + installed_fd = f.notify_addfd(NotificationAddfd(notify, 0, new_fd), fd=notify_fd) + f.respond_notify(NotificationResponse(notify, installed_fd, 0, 0), fd=notify_fd) + + # No longer need the fds + os.close(new_fd) + os.close(notify_fd) + p_socket.close() + + wpid, rc = os.waitpid(pid, 0) + if os.WIFEXITED(rc) == 0: + raise RuntimeError("Child process error") + if os.WEXITSTATUS(rc) != 0: + raise RuntimeError("Child process error") + + quit(160) + +test() + +# kate: syntax python; +# kate: indent-mode python; space-indent on; indent-width 4; mixedindent off; diff --git a/tests/63-live-notify_addfd.tests b/tests/63-live-notify_addfd.tests new file mode 100644 index 00000000..9e6d3e2b --- /dev/null +++ b/tests/63-live-notify_addfd.tests @@ -0,0 +1,11 @@ +# +# libseccomp regression test automation data +# +# Copyright (c) 2025 Microsoft Corporation +# Author: Sudipta Pandit +# + +test type: live + +# Testname API Result +63-live-notify_addfd 5 ALLOW diff --git a/tests/Makefile.am b/tests/Makefile.am index bbe71324..70d06105 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -97,7 +97,8 @@ check_PROGRAMS = \ 59-basic-empty_binary_tree \ 60-sim-precompute \ 61-sim-transactions \ - 62-sim-arch_transactions + 62-sim-arch_transactions \ + 63-live-notify_addfd EXTRA_DIST_TESTPYTHON = \ util.py \ @@ -160,7 +161,8 @@ EXTRA_DIST_TESTPYTHON = \ 59-basic-empty_binary_tree.py \ 60-sim-precompute.py \ 61-sim-transactions.py \ - 62-sim-arch_transactions.py + 62-sim-arch_transactions.py \ + 63-live-notify_addfd.py EXTRA_DIST_TESTCFGS = \ 01-sim-allow.tests \ @@ -224,7 +226,8 @@ EXTRA_DIST_TESTCFGS = \ 59-basic-empty_binary_tree.tests \ 60-sim-precompute.tests \ 61-sim-transactions.tests \ - 62-sim-arch_transactions.tests + 62-sim-arch_transactions.tests \ + 63-live-notify_addfd.tests EXTRA_DIST_TESTSCRIPTS = \ 38-basic-pfc_coverage.sh 38-basic-pfc_coverage.pfc \ From 5d222783df235a2d8696648248c4eaac0950978a Mon Sep 17 00:00:00 2001 From: Sudipta Pandit Date: Sun, 23 Nov 2025 16:14:29 +0000 Subject: [PATCH 4/5] api: rename newflags to newfd_flags in python api for addfd Signed-off-by: Sudipta Pandit --- src/python/seccomp.pyx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/python/seccomp.pyx b/src/python/seccomp.pyx index eb065ad7..ba8aa4fa 100644 --- a/src/python/seccomp.pyx +++ b/src/python/seccomp.pyx @@ -600,7 +600,7 @@ cdef class NotificationAddfd: cdef uint32_t _newfd cdef uint32_t _newfd_flags - def __cinit__(self, notify, flags, srcfd, newfd = 0, newflags = 0): + def __cinit__(self, notify, flags, srcfd, newfd = 0, newfd_flags = 0): """ Initialize the notification addfd structure. Arguments: @@ -608,7 +608,7 @@ cdef class NotificationAddfd: srcfd - the source file descriptor flags - notify addfd flags newfd - 0 or desired file descriptor number in target - newflags - new flags to set on the target file descriptor + newfd_flags - new flags to set on the target file descriptor Description: Create a seccomp NotificationAddfd object. @@ -617,7 +617,7 @@ cdef class NotificationAddfd: self._flags = flags self._srcfd = srcfd self._newfd = newfd - self._newfd_flags = newflags + self._newfd_flags = newfd_flags @property def id(self): @@ -704,7 +704,7 @@ cdef class NotificationAddfd: self._newfd = value @property - def newflags(self): + def newfd_flags(self): """ Get the new flags to set on the target file descriptor. Description: @@ -712,12 +712,12 @@ cdef class NotificationAddfd: """ return self._newfd_flags - @newflags.setter - def newflags(self, value): + @newfd_flags.setter + def newfd_flags(self, value): """ Set the new flags to set on the target file descriptor. Arguments: - newflags - the new flags to set on the target file descriptor + newfd_flags - the new flags to set on the target file descriptor Description: Set the new flags to set on the target file descriptor. @@ -1186,7 +1186,7 @@ cdef class SyscallFilter: _addfd.flags = addfd.flags _addfd.srcfd = addfd.srcfd _addfd.newfd = addfd.newfd - _addfd.newfd_flags = addfd.newflags + _addfd.newfd_flags = addfd.newfd_flags rc = libseccomp.seccomp_notify_addfd(fd, &_addfd) if rc < 0: From 3de75bbc27b01e5f191ca341033f96f04baf60e3 Mon Sep 17 00:00:00 2001 From: Sudipta Pandit Date: Sun, 23 Nov 2025 16:14:55 +0000 Subject: [PATCH 5/5] tests: fix comment in recv_fd test for clarity Signed-off-by: Sudipta Pandit --- tests/63-live-notify_addfd.c | 2 +- tests/63-live-notify_addfd.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/63-live-notify_addfd.c b/tests/63-live-notify_addfd.c index f95985eb..9afc75ca 100644 --- a/tests/63-live-notify_addfd.c +++ b/tests/63-live-notify_addfd.c @@ -54,6 +54,7 @@ int send_fd(int sock, int fd) int recv_fd(int sock) { + int fd; char m_buffer[1]; struct iovec iov = {.iov_base = m_buffer, .iov_len = 1}; @@ -69,7 +70,6 @@ int recv_fd(int sock) if (recvmsg(sock, &msg, 0) < 0) return -1; - int fd; memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd)); return fd; } diff --git a/tests/63-live-notify_addfd.py b/tests/63-live-notify_addfd.py index 16eb357e..3820fe16 100644 --- a/tests/63-live-notify_addfd.py +++ b/tests/63-live-notify_addfd.py @@ -64,7 +64,7 @@ def test(): ret_bytes = os.read(ret_fd, 128) os.close(ret_fd) if len(ret_bytes) != 0: - # /dev/null should be empty + # Expect zero bytes since the fd now points to /dev/null quit(1) os.close(notify_fd)