[DTrace-devel] [PATCH v2 3/4] EINTR: make safe under signal hits
Kris Van Hees
kris.van.hees at oracle.com
Wed Nov 8 05:36:41 UTC 2023
On Wed, Nov 01, 2023 at 03:31:05PM +0000, Nick Alcock wrote:
> This commit audits DTrace (especially libproc and dt_proc, but not only
> that) for syscalls and library calls that may return EINTR but are not
> suitably guarded, and guards them, adjusting any that use timeouts so that
> the timeout ignores the interruption. (The plan was also to have any of
> them that needed to actually *use* this EINTR to trigger an early return to
> do that as well, but it turns out that none do, not even the ones in
> libproc.)
>
> This matters for libproc and dt_proc in particular now because the monitor
> thread is routinely hit by signals and so has to expect EINTR returns from
> appropriate syscalls (and consider what to do if that happens, because it
> means a proxy request is waiting and the DTrace main thread is blocked on
> it).
>
> This does not touch the core polling loop in dt_consume, since that loop is
> *expected* to return -EINTR, triggering an -EINTR return from dtrace_go()
> (see cmd/dtrace.c's call to that function). This may be a bug, since it
> means that repeatedly changing the window size is enough to cause DTrace to
> ignore the switchrate -- but the caller might well *want* an -EINTR return
> so it can handle whatever signal just hit.
>
> Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>
> ---
> libcommon/dof_parser_host.c | 29 +++++++++++++++++++++++++++--
> libdtrace/dt_pcap.c | 5 +++--
> libdtrace/dt_proc.c | 2 +-
> libproc/Pcontrol.c | 2 +-
> libproc/elfish.c | 5 +++--
> 5 files changed, 35 insertions(+), 8 deletions(-)
>
> diff --git a/libcommon/dof_parser_host.c b/libcommon/dof_parser_host.c
> index feb719020dc18..071f8e80ebc01 100644
> --- a/libcommon/dof_parser_host.c
> +++ b/libcommon/dof_parser_host.c
> @@ -10,6 +10,7 @@
> #include <stddef.h>
> #include <stdlib.h>
> #include <string.h>
> +#include <time.h>
> #include <unistd.h>
>
> #include "dof_parser.h"
> @@ -88,14 +89,38 @@ dof_parser_host_read(int in, int timeout)
> * only after that, both to make sure we don't underread and to make
> * sure we don't *overread* and concatenate part of another message
> * onto this one.
> + *
> + * Adjust the timeout whenever we are interrupted. If we can't figure
> + * out the time, don't bother adjusting, but still read: a read taking
> + * longer than expected is better than no read at all.
> */
> for (i = 0, sz = offsetof(dof_parsed_t, type); i < sz;) {
> size_t ret;
> + struct timespec start, end;
> + int no_adjustment = 0;
> + long timeout_msec = timeout * 1000;
>
> - if ((ret = poll(&fd, 1, timeout * 1000)) <= 0)
> + if (clock_gettime(CLOCK_REALTIME, &start) < 0)
> + no_adjustment = 1;
> +
> + while ((ret = poll(&fd, 1, timeout_msec)) <= 0 && errno == EINTR) {
> +
> + if (no_adjustment || clock_gettime(CLOCK_REALTIME, &end) < 0)
> + continue; /* Abandon timeout adjustment */
> +
> + timeout_msec -= ((end.tv_sec + ((unsigned long long) end.tv_nsec * NANOSEC)) -
> + (start.tv_sec + ((unsigned long long) start.tv_nsec * NANOSEC))) /
> + MICROSEC;
> +
> + if (timeout_msec < 0)
> + timeout_msec = 0;
> + }
> +
> + if (ret < 0)
> goto err;
>
> - ret = read(in, ((char *) reply) + i, sz - i);
> + while ((ret = read(in, ((char *) reply) + i, sz - i)) < 0 &&
> + errno == EINTR);
>
> if (ret <= 0)
> goto err;
> diff --git a/libdtrace/dt_pcap.c b/libdtrace/dt_pcap.c
> index 484816deccacc..bb41759eb8226 100644
> --- a/libdtrace/dt_pcap.c
> +++ b/libdtrace/dt_pcap.c
> @@ -98,7 +98,8 @@ dt_pcap_destroy(dtrace_hdl_t *dtp)
> */
> close(dtp->dt_pcap.dt_pcap_pipe[1]);
> pthread_join(dtp->dt_pcap.dt_pcap_output, NULL);
> - waitpid(dtp->dt_pcap.dt_pcap_pid, NULL, 0);
> + while (waitpid(dtp->dt_pcap.dt_pcap_pid, NULL, 0) < 0 &&
> + errno == EINTR);
> }
> }
>
> @@ -368,7 +369,7 @@ dt_pcap_filename(dtrace_hdl_t *dtp, FILE *fp)
> */
> close(pipe_in[1]);
> close(pipe_out[0]);
> - waitpid(pid, NULL, 0);
> + while (waitpid(pid, NULL, 0) < 0 && errno == EINTR);
> dtp->dt_pcap.dt_pcap_pid = -1;
> return NULL;
> }
> diff --git a/libdtrace/dt_proc.c b/libdtrace/dt_proc.c
> index ed142c3bfd46f..64d0a763cd56d 100644
> --- a/libdtrace/dt_proc.c
> +++ b/libdtrace/dt_proc.c
> @@ -1138,7 +1138,7 @@ dt_proc_loop(dt_proc_t *dpr, int awaiting_continue)
> jmp_buf this_exec_jmp, *old_exec_jmp;
> volatile int did_exec_retry = 0;
>
> - read(dpr->dpr_proxy_fd[0], &junk, 1);
> + while (read(dpr->dpr_proxy_fd[0], &junk, 1) < 0 && errno == EINTR);
> pfd.revents = 0;
>
> /*
> diff --git a/libproc/Pcontrol.c b/libproc/Pcontrol.c
> index 0cc1692f3b489..eb450bdc29814 100644
> --- a/libproc/Pcontrol.c
> +++ b/libproc/Pcontrol.c
> @@ -211,7 +211,7 @@ Pcreate(
> }
> close(forkblock[1]);
>
> - waitpid(pid, &status, 0);
> + while (waitpid(pid, &status, 0) < 0 && errno == EINTR);
> if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP ||
> (status >> 8) != (SIGTRAP | PTRACE_EVENT_EXEC << 8)) {
> rc = ENOENT;
> diff --git a/libproc/elfish.c b/libproc/elfish.c
> index f4e54e8324001..210e5ff0f5db5 100644
> --- a/libproc/elfish.c
> +++ b/libproc/elfish.c
> @@ -35,7 +35,7 @@
> int
> Pread_isa_info(struct ps_prochandle *P, const char *procname)
> {
> - int fd;
> + int fd, err;
> Elf64_Ehdr hdr;
>
> if ((fd = open(procname, O_RDONLY)) < 0) {
> @@ -43,7 +43,8 @@ Pread_isa_info(struct ps_prochandle *P, const char *procname)
> return -1;
> }
>
> - if (read(fd, &hdr, sizeof(hdr)) < 0) {
> + while ((err = read(fd, &hdr, sizeof(hdr))) < 0 && errno == EINTR);
> + if (err < 0) {
> _dprintf("%s is not an ELF file\n", procname);
> close(fd);
> return -1;
> --
> 2.42.0.271.g85384428f1
>
>
More information about the DTrace-devel
mailing list