From 38e65907f39f08b4252844e3683c1baae2a27000 Mon Sep 17 00:00:00 2001 From: Jason Bowman Date: Sat, 14 Mar 2026 15:47:58 -0700 Subject: [PATCH 1/5] sss: remove unused sys/epoll.h include from clevis-encrypt-sss clevis-encrypt-sss.c includes sys/epoll.h but never uses any epoll functions. This unused include prevents compilation on platforms that lack epoll (e.g., macOS/Darwin). --- src/pins/sss/clevis-encrypt-sss.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pins/sss/clevis-encrypt-sss.c b/src/pins/sss/clevis-encrypt-sss.c index 1b2cc314..ac26fed1 100644 --- a/src/pins/sss/clevis-encrypt-sss.c +++ b/src/pins/sss/clevis-encrypt-sss.c @@ -42,7 +42,6 @@ #include #include -#include #include #include From 01301da52423650c7936ace524e60ede77516082 Mon Sep 17 00:00:00 2001 From: Jason Bowman Date: Sat, 14 Mar 2026 15:49:16 -0700 Subject: [PATCH 2/5] sss: replace Linux epoll with POSIX poll in clevis-decrypt-sss clevis-decrypt-sss.c used epoll to monitor child process output file descriptors. epoll is Linux-specific and prevents compilation on other POSIX platforms such as macOS/Darwin. Replace epoll with poll(), which is POSIX standard and functionally equivalent for the small number of file descriptors monitored here. The pollfds array is dynamically allocated to match the number of child processes. --- src/pins/sss/clevis-decrypt-sss.c | 51 +++++++++++++++++++------------ 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/src/pins/sss/clevis-decrypt-sss.c b/src/pins/sss/clevis-decrypt-sss.c index 15183764..1a7003ab 100644 --- a/src/pins/sss/clevis-decrypt-sss.c +++ b/src/pins/sss/clevis-decrypt-sss.c @@ -41,7 +41,7 @@ #include #include -#include +#include #include #include @@ -141,7 +141,8 @@ main(int argc, char *argv[]) int ret = EXIT_FAILURE; json_t *p = NULL; json_int_t t = 1; - int epoll = -1; + struct pollfd *pollfds = NULL; + nfds_t nfds = 0; size_t pl = 0; if (argc == 2 && strcmp(argv[1], "--summary") == 0) @@ -150,10 +151,6 @@ main(int argc, char *argv[]) if (isatty(STDIN_FILENO) || argc != 1) goto usage; - epoll = epoll_create1(EPOLL_CLOEXEC); - if (epoll < 0) - return ret; - jwe = compact_jwe(stdin); if (!jwe) goto egress; @@ -195,12 +192,17 @@ main(int argc, char *argv[]) if (!pin->file) goto egress; - if (epoll_ctl(epoll, EPOLL_CTL_ADD, fileno(pin->file), - &(struct epoll_event) { - .events = EPOLLIN | EPOLLPRI, - .data.fd = fileno(pin->file) - }) < 0) - goto egress; + { + struct pollfd *tmp = realloc(pollfds, + (nfds + 1) * sizeof(*pollfds)); + if (!tmp) + goto egress; + pollfds = tmp; + pollfds[nfds].fd = fileno(pin->file); + pollfds[nfds].events = POLLIN | POLLPRI; + pollfds[nfds].revents = 0; + nfds++; + } } json_decref(pins); @@ -208,18 +210,27 @@ main(int argc, char *argv[]) if (!pins) goto egress; - for (struct epoll_event e; true; ) { - int r = 0; - - r = epoll_wait(epoll, &e, 1, -1); - if (r != 1) + while (true) { + int r = poll(pollfds, nfds, -1); + if (r <= 0) break; for (struct pin *pin = chldrn.next; pin != &chldrn; pin = pin->next) { - if (!pin->file || e.data.fd != fileno(pin->file)) + nfds_t pi; + + if (!pin->file) + continue; + + for (pi = 0; pi < nfds; pi++) { + if (pollfds[pi].fd == fileno(pin->file)) + break; + } + if (pi >= nfds) + continue; + if (!(pollfds[pi].revents & (POLLIN | POLLPRI))) continue; - if (e.events & (EPOLLIN | EPOLLPRI)) { + { const size_t ptl = pl * 2; pin->pt = malloc(ptl); @@ -324,7 +335,7 @@ main(int argc, char *argv[]) free(pin); } - close(epoll); + free(pollfds); return ret; usage: From 151184479eb80ffc401a34c1377f41f73ec89203 Mon Sep 17 00:00:00 2001 From: Jason Bowman Date: Sat, 14 Mar 2026 15:51:15 -0700 Subject: [PATCH 3/5] sss: replace pipe2 with portable pipe and fcntl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pipe2() is a Linux-specific extension (requires _GNU_SOURCE) that atomically creates a pipe with flags. Replace it with the POSIX equivalent: pipe() followed by fcntl(F_SETFD, FD_CLOEXEC). The atomicity difference is irrelevant here since the program is single-threaded — there is no risk of a concurrent fork leaking file descriptors between the pipe() and fcntl() calls. This enables compilation on platforms that lack pipe2(), such as macOS/Darwin. --- src/pins/sss/sss.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pins/sss/sss.c b/src/pins/sss/sss.c index 7486d6c5..c12079c0 100644 --- a/src/pins/sss/sss.c +++ b/src/pins/sss/sss.c @@ -349,11 +349,15 @@ call(char *const argv[], const void *buf, size_t len, pid_t *pid) *pid = 0; - if (pipe2(dump, O_CLOEXEC) < 0) + if (pipe(dump) < 0) goto error; + fcntl(dump[0], F_SETFD, FD_CLOEXEC); + fcntl(dump[1], F_SETFD, FD_CLOEXEC); - if (pipe2(load, O_CLOEXEC) < 0) + if (pipe(load) < 0) goto error; + fcntl(load[0], F_SETFD, FD_CLOEXEC); + fcntl(load[1], F_SETFD, FD_CLOEXEC); *pid = fork(); if (*pid < 0) From 8b3c0409dea0ad9f271686b71ff3a73dfd21cc7c Mon Sep 17 00:00:00 2001 From: Jason Bowman Date: Sat, 14 Mar 2026 15:53:15 -0700 Subject: [PATCH 4/5] luks: make cryptsetup optional in test suite The LUKS test directory is included unconditionally by the parent meson.build (only gated by cross-compilation), but all LUKS tests require the cryptsetup binary. When cryptsetup is unavailable (e.g., on macOS/Darwin or minimal build environments), the build fails at configure time. Make cryptsetup optional and use subdir_done() to skip the entire test directory when it is not found. This also avoids the luksmeta_data.get('OLD_CRYPTSETUP') error that occurs when libcryptsetup was not detected. Move the jq find_program() call after the cryptsetup guard since it is only used within LUKS tests. --- src/luks/tests/meson.build | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/luks/tests/meson.build b/src/luks/tests/meson.build index aedc6353..1e8540ae 100644 --- a/src/luks/tests/meson.build +++ b/src/luks/tests/meson.build @@ -1,9 +1,14 @@ # We use jq for comparing the pin config in the clevis luks list tests. jq = find_program('jq', required: false) -# We use cryptsetup for testing LUKS2 binding and saving the token in a -# given token slot. -cryptsetup = find_program('cryptsetup', required: true) +# All LUKS tests require cryptsetup. The test directory is included +# unconditionally by the parent meson.build, so we must handle the case +# where cryptsetup is not available (e.g., on macOS/Darwin). +cryptsetup = find_program('cryptsetup', required: false) +if not cryptsetup.found() + warning('Will not run LUKS tests due to missing cryptsetup') + subdir_done() +endif # Use keyctl to check an existing token id can be created from # kernel keyring password @@ -14,6 +19,9 @@ else warning('keyutils not installed, unable to test existing token id binding') endif +# We use jq for comparing the pin config in the clevis luks list tests. +jq = find_program('jq', required: false) + common_functions = configure_file(input: 'tests-common-functions.in', output: 'tests-common-functions', configuration: luksmeta_data, From 549c8001fedef6e5100e879fadf4696d6eeb07ff Mon Sep 17 00:00:00 2001 From: Jason Bowman Date: Mon, 16 Mar 2026 09:54:41 -0700 Subject: [PATCH 5/5] sss: handle poll events correctly in clevis-decrypt-sss When a pin's file descriptor is closed after reading, it must be removed from the poll set. Unlike epoll (which automatically removes closed fds), poll() will continue to return events for closed fds. Additionally, poll() can return POLLHUP/POLLERR/POLLNVAL when a child process exits or encounters errors. These events must be handled to avoid infinite loops, but only when there's no data to read (POLLIN not set) - otherwise we might discard valid data from a process that wrote output then exited. This fix: - Sets closed fds to -1 in the pollfds array (poll ignores negative fds) - Handles error/hangup events by cleaning up failed pins, but only when POLLIN is not set, ensuring we read all available data first --- src/pins/sss/clevis-decrypt-sss.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/pins/sss/clevis-decrypt-sss.c b/src/pins/sss/clevis-decrypt-sss.c index 1a7003ab..298d6961 100644 --- a/src/pins/sss/clevis-decrypt-sss.c +++ b/src/pins/sss/clevis-decrypt-sss.c @@ -227,8 +227,22 @@ main(int argc, char *argv[]) } if (pi >= nfds) continue; - if (!(pollfds[pi].revents & (POLLIN | POLLPRI))) + + /* If no data available but pipe closed/errored, mark as failed */ + if (!(pollfds[pi].revents & (POLLIN | POLLPRI))) { + if (pollfds[pi].revents & (POLLERR | POLLHUP | POLLNVAL)) { + fclose(pin->file); + pin->file = NULL; + pollfds[pi].fd = -1; + waitpid(pin->pid, NULL, 0); + pin->pid = 0; + pin->next->prev = pin->prev; + pin->prev->next = pin->next; + free(pin); + break; + } continue; + } { const size_t ptl = pl * 2; @@ -260,6 +274,8 @@ main(int argc, char *argv[]) fclose(pin->file); pin->file = NULL; + /* Remove closed fd from poll set (poll ignores negative fds) */ + pollfds[pi].fd = -1; waitpid(pin->pid, NULL, 0); pin->pid = 0;