Re: [PATCH v2] libsemanage: semanage_pipe_data(): multiplex the compiler pipes
Stephen Smalley <[email protected]> Fri, 24 Jul 2026 10:16:43 -0400
| Newsgroups | org.kernel.vger.selinux |
|---|---|
| Message-ID | <CAEjxPJ4bgogdVYNfiQ9bbxAzNbv2cgZTtt5n=6h++zUiJc4tdg@mail.gmail.com> |
On Mon, Jul 20, 2026 at 2:29 PM Stephen Smalley <[email protected]> wrote: > > The parent writes the entire input to the compiler's stdin, then > drains stdout to EOF, then drains stderr. If the child writes more > than a pipe buffer to a stream the parent is not yet reading, both > sides block. With hll/pp the input phase is safe (fp_to_buffer() > drains stdin before any output), but sepol_module_package_to_cil() can > emit stderr warnings while writing CIL to stdout, so a module that > produces enough warnings fills the stderr pipe and semodule -i > hanges. A streaming HLL that writes diagnostics while reading would > deadlock in the input phase too. > > Replace the serial write/read with a poll() loop over all three pipes, > feeding input while draining stdout and stderr as they become > ready. read_from_pipe_to_data() is folded into an incremental append > helper. > > Signed-off-by: Stephen Smalley <[email protected]> Merged. > --- > v2 fixing the formatting. > > libsemanage/src/direct_api.c | 191 +++++++++++++++++++++++------------ > 1 file changed, 124 insertions(+), 67 deletions(-) > > diff --git a/libsemanage/src/direct_api.c b/libsemanage/src/direct_api.c > index 6c84b23c..132e2a2a 100644 > --- a/libsemanage/src/direct_api.c > +++ b/libsemanage/src/direct_api.c > @@ -27,6 +27,7 @@ > #include <assert.h> > #include <fcntl.h> > #include <libgen.h> > +#include <poll.h> > #include <stdio.h> > #include <stdio_ext.h> > #include <stdlib.h> > @@ -565,48 +566,135 @@ cleanup: > return retval; > } > > -static int read_from_pipe_to_data(semanage_handle_t *sh, size_t initial_len, > - int fd, char **out_data_read, > - size_t *out_read_len) > +static int append_read(semanage_handle_t *sh, int fd, char **buf, size_t *len, > + size_t *cap) > { > - size_t max_len = initial_len; > - ssize_t read_len; > - size_t data_read_len = 0; > - char *data_read = NULL; > + ssize_t r; > > - if (max_len <= 0) { > - max_len = 1; > + if (*len == *cap) { > + ssize_t ncap; > + char *tmp; > + > + if (__builtin_mul_overflow(*cap ? *cap : 4096, 2, &ncap)) { > + ERR(sh, "Overflow"); > + return -1; > + } > + tmp = realloc(*buf, ncap); > + if (!tmp) { > + ERR(sh, "Out of memory."); > + return -1; > + } > + *buf = tmp; > + *cap = ncap; > } > - data_read = malloc(max_len * sizeof(*data_read)); > - if (data_read == NULL) { > - ERR(sh, "Failed to malloc, out of memory."); > + > + r = read(fd, *buf + *len, *cap - *len); > + if (r < 0) { > + if (errno == EINTR || errno == EAGAIN) > + return 1; > + ERR(sh, "Failed to read from pipe."); > return -1; > } > + if (r == 0) > + return 0; > + *len += (size_t)r; > + return 1; > +} > + > +static int pump_pipes(semanage_handle_t *sh, int in_fd, int out_fd, int err_fd, > + const char *in_data, size_t in_len, char **out_data, > + size_t *out_len, char **err_data, size_t *err_len) > +{ > + size_t in_off = 0, out_cap = 0, err_cap = 0; > + int rc = 0; > + > + *out_data = NULL; > + *out_len = 0; > + *err_data = NULL; > + *err_len = 0; > + > + while (in_fd >= 0 || out_fd >= 0 || err_fd >= 0) { > + struct pollfd pfd[3]; > + > + int n = 0, i_in = -1, i_out = -1, i_err = -1; > + > + if (in_fd >= 0) { > + pfd[n].fd = in_fd; > + pfd[n].events = POLLOUT; > + i_in = n++; > + } > + if (out_fd >= 0) { > + pfd[n].fd = out_fd; > + pfd[n].events = POLLIN; > + i_out = n++; > + } > + if (err_fd >= 0) { > + pfd[n].fd = err_fd; > + pfd[n].events = POLLIN; > + i_err = n++; > + } > + > + if (poll(pfd, n, -1) < 0) { > + if (errno == EINTR) > + continue; > + ERR(sh, "poll() failed."); > + rc = -1; > + break; > + } > + > + if (i_in >= 0 && (pfd[i_in].revents & (POLLOUT | POLLERR))) { > + ssize_t w = > + write(in_fd, in_data + in_off, in_len - in_off); > + if (w < 0) { > + if (errno != EINTR && errno != EAGAIN) { > + ERR(sh, > + "Failed to write to input pipe."); > + rc = -1; > + close(in_fd); > + in_fd = -1; > + } > + } else { > + in_off += (size_t)w; > + if (in_off == in_len) { > + close(in_fd); > + in_fd = -1; > + } > + } > + } > > - while ((read_len = read(fd, data_read + data_read_len, > - max_len - data_read_len)) > 0) { > - data_read_len += read_len; > - if (data_read_len == max_len) { > - char *tmp; > - if (__builtin_mul_overflow(max_len, 2, &max_len)) { > - ERR(sh, "Overflow"); > - free(data_read); > - return -1; > + if (i_out >= 0 && > + (pfd[i_out].revents & (POLLIN | POLLHUP | POLLERR))) { > + int r = append_read(sh, out_fd, out_data, out_len, > + &out_cap); > + if (r <= 0) { > + if (r < 0) > + rc = -1; > + close(out_fd); > + out_fd = -1; > } > - tmp = realloc(data_read, max_len); > - if (tmp == NULL) { > - ERR(sh, "Failed to realloc, out of memory."); > - free(data_read); > - return -1; > + } > + > + if (i_err >= 0 && > + (pfd[i_err].revents & (POLLIN | POLLHUP | POLLERR))) { > + int r = append_read(sh, err_fd, err_data, err_len, > + &err_cap); > + if (r <= 0) { > + if (r < 0) > + rc = -1; > + close(err_fd); > + err_fd = -1; > } > - data_read = tmp; > } > } > > - *out_read_len = data_read_len; > - *out_data_read = data_read; > + if (in_fd >= 0) > + close(in_fd); > + if (out_fd >= 0) > + close(out_fd); > + if (err_fd >= 0) > + close(err_fd); > > - return 0; > + return rc; > } > > // Forward error messages to redirected stderr pipe > @@ -649,7 +737,6 @@ static int semanage_pipe_data(semanage_handle_t *sh, const char *path, > char *err_data_read = NULL; > int retval; > int status = 0; > - size_t initial_len; > size_t data_read_len = 0; > size_t err_data_read_len = 0; > struct sigaction old_signal; > @@ -747,46 +834,16 @@ child_err: > any_err = 1; > } > > - retval = write_full(input_fd[PIPE_WRITE], in_data, in_data_len); > - if (retval == -1) { > - ERR(sh, "Failed to write data to input pipe."); > - any_err = 1; > - } > - retval = close(input_fd[PIPE_WRITE]); > + retval = pump_pipes(sh, input_fd[PIPE_WRITE], > + output_fd[PIPE_READ], err_fd[PIPE_READ], > + in_data, in_data_len, &data_read, > + &data_read_len, &err_data_read, > + &err_data_read_len); > input_fd[PIPE_WRITE] = -1; > - if (retval == -1) { > - ERR(sh, "Unable to close write end of input pipe."); > - any_err = 1; > - } > - > - initial_len = 1 << 17; > - retval = read_from_pipe_to_data(sh, initial_len, > - output_fd[PIPE_READ], > - &data_read, &data_read_len); > - if (retval != 0) { > - any_err = 1; > - } > - retval = close(output_fd[PIPE_READ]); > output_fd[PIPE_READ] = -1; > - if (retval == -1) { > - ERR(sh, "Unable to close read end of output pipe."); > - any_err = 1; > - } > - > - initial_len = 1 << 9; > - retval = read_from_pipe_to_data(sh, initial_len, > - err_fd[PIPE_READ], > - &err_data_read, > - &err_data_read_len); > - if (retval != 0) { > - any_err = 1; > - } > - retval = close(err_fd[PIPE_READ]); > err_fd[PIPE_READ] = -1; > - if (retval == -1) { > - ERR(sh, "Unable to close read end of error pipe."); > + if (retval != 0) > any_err = 1; > - } > > errno = ENODATA; > if (waitpid(pid, &status, 0) == -1 || !WIFEXITED(status)) { > -- > 2.55.0 >