From 453c46430cd06452fdc2532734f9a8c3268aa32f Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Fri, 16 Oct 2026 12:17:56 -0700 Subject: [PATCH] api: handle short writes in seccomp_export_bpf() seccomp_export_bpf() issues a single write() and only checks for a negative return value, so if the write is short (e.g. a non-blocking pipe with less room than the program, or RLIMIT_FSIZE, or ENOSPC after some data was written) it returns success while only a part of the filter was written. Loop until the whole program is written, retrying on EINTR, and fail if write() returns an error or does not make progress. Add a test case using a non-blocking pipe which is smaller than the filter program. Signed-off-by: Kir Kolyshkin --- src/api.c | 22 +++++++++++++++++++--- tests/11-basic-basic_errors.c | 27 +++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/api.c b/src/api.c index 413cc81a..b002ec64 100644 --- a/src/api.c +++ b/src/api.c @@ -755,6 +755,9 @@ API int seccomp_export_pfc(const scmp_filter_ctx ctx, int fd) API int seccomp_export_bpf(const scmp_filter_ctx ctx, int fd) { int rc; + ssize_t n; + size_t len; + const char *buf; struct db_filter_col *col; struct bpf_program *program; @@ -766,9 +769,22 @@ API int seccomp_export_bpf(const scmp_filter_ctx ctx, int fd) if (rc < 0) return _rc_filter(rc); program = col->prgm_bpf; - rc = write(fd, program->blks, BPF_PGM_SIZE(program)); - if (rc < 0) - return _rc_filter_sys(col, -errno); + + /* handle short writes and EINTR */ + buf = (const char *)program->blks; + len = BPF_PGM_SIZE(program); + while (len > 0) { + n = write(fd, buf, len); + if (n < 0) { + if (errno == EINTR) + continue; + return _rc_filter_sys(col, -errno); + } + if (n == 0) + return _rc_filter_sys(col, -EIO); + buf += n; + len -= n; + } return 0; } diff --git a/tests/11-basic-basic_errors.c b/tests/11-basic-basic_errors.c index dcfa7c28..365b87c1 100644 --- a/tests/11-basic-basic_errors.c +++ b/tests/11-basic-basic_errors.c @@ -19,7 +19,9 @@ * along with this library; if not, see . */ +#define _GNU_SOURCE #include +#include #include #include @@ -32,6 +34,8 @@ int main(int argc, char *argv[]) unsigned int api; struct seccomp_notif *req = NULL; struct seccomp_notif_resp *resp = NULL; + int pipefd[2]; + int i; /* get the api level */ api = seccomp_api_get(); @@ -179,6 +183,29 @@ int main(int argc, char *argv[]) seccomp_release(ctx); ctx = NULL; + /* seccomp_export_bpf short write */ + ctx = seccomp_init(SCMP_ACT_ALLOW); + if (ctx == NULL) + return -1; + for (i = 0; i < 1024; i++) { + rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(1), SCMP_SYS(write), 1, + SCMP_A0(SCMP_CMP_EQ, i)); + if (rc != 0) + return -1; + } + if (pipe2(pipefd, O_NONBLOCK) < 0) + return -1; + /* a pipe smaller than the filter only takes a part of it */ + if (fcntl(pipefd[1], F_SETPIPE_SZ, getpagesize()) < 0) + return -1; + rc = seccomp_export_bpf(ctx, pipefd[1]); + if (rc != -ECANCELED) + return -1; + close(pipefd[0]); + close(pipefd[1]); + seccomp_release(ctx); + ctx = NULL; + /* seccomp_export_bpf_mem errors */ char buf[1024]; size_t buf_len = sizeof(buf);