Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/luks/tests/meson.build
Original file line number Diff line number Diff line change
@@ -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
Expand Down
98 changes: 77 additions & 21 deletions src/pins/sss/clevis-decrypt-sss.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
#include <jose/b64.h>
#include <jose/jwe.h>

#include <sys/epoll.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/wait.h>

Expand Down Expand Up @@ -131,6 +131,24 @@ compact_jwe(FILE *file)
return json_incref(jwe);
}

/* Queue a child pid for reaping at teardown. Deferring the wait keeps the
* poll loop non-blocking: a child that stalls after closing its stdout can
* no longer freeze the event loop and starve its healthy siblings. */
static void
defer_reap(pid_t **reap, size_t *nreap, pid_t pid)
{
pid_t *tmp = realloc(*reap, (*nreap + 1) * sizeof(*tmp));

if (!tmp) {
/* Out of memory: reap now rather than leak the child. */
waitpid(pid, NULL, 0);
return;
}

*reap = tmp;
(*reap)[(*nreap)++] = pid;
}

int
main(int argc, char *argv[])
{
Expand All @@ -141,19 +159,18 @@ 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;
pid_t *reap = NULL;
size_t nreap = 0;

if (argc == 2 && strcmp(argv[1], "--summary") == 0)
return EXIT_FAILURE;

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;
Expand Down Expand Up @@ -195,31 +212,59 @@ 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);
pins = json_array();
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 (e.events & (EPOLLIN | EPOLLPRI)) {
/* 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;
defer_reap(&reap, &nreap, pin->pid);
pin->pid = 0;
pin->next->prev = pin->prev;
pin->prev->next = pin->next;
free(pin);
break;
}
continue;
}

{
const size_t ptl = pl * 2;

pin->pt = malloc(ptl);
Expand Down Expand Up @@ -249,8 +294,10 @@ 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);
defer_reap(&reap, &nreap, pin->pid);
pin->pid = 0;

if (!pin->pt) {
Expand Down Expand Up @@ -324,7 +371,16 @@ main(int argc, char *argv[])
free(pin);
}

close(epoll);
/* Reap children whose pipes closed during the poll loop. Blocking is
* safe here: the loop is finished, so a stalled child can no longer
* affect its siblings, and SIGTERM bounds anything stuck after close. */
for (size_t i = 0; i < nreap; i++) {
kill(reap[i], SIGTERM);
waitpid(reap[i], NULL, 0);
}
free(reap);

free(pollfds);
return ret;

usage:
Expand Down
1 change: 0 additions & 1 deletion src/pins/sss/clevis-encrypt-sss.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
#include <jose/b64.h>
#include <jose/jwe.h>

#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/wait.h>

Expand Down
10 changes: 8 additions & 2 deletions src/pins/sss/sss.c
Original file line number Diff line number Diff line change
Expand Up @@ -349,10 +349,16 @@ 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;
if (fcntl(dump[0], F_SETFD, FD_CLOEXEC) < 0 ||
fcntl(dump[1], F_SETFD, FD_CLOEXEC) < 0)
goto error;

if (pipe2(load, O_CLOEXEC) < 0)
if (pipe(load) < 0)
goto error;
if (fcntl(load[0], F_SETFD, FD_CLOEXEC) < 0 ||
fcntl(load[1], F_SETFD, FD_CLOEXEC) < 0)
goto error;

*pid = fork();
Expand Down