nk/exec: Simplify and remove double null termination.

The current code pads with an extra character that is then rewritten
into a null character.  This isn't necessary with post-C99
implementations of standardized snprintf, so get rid of it.

Also add a note warning that nk_generate_env() and nk_execute()
are not async signal safe and are thus unsuitable for use in
multithreaded processes.

nk_execute() could be rewritten to be async signal safe without much
trouble, as the only problem point is snprintf() which is not guaranteed
to be async signal safe by POSIX.

However, nk_generate_env() performs chroot() if a chroot_path is
specified, and chroot() is not async signal safe in POSIX.
Additionally, malloc() can be called in rare cases where user
information fields are very long, and malloc() is obviously not async
signal safe.  Finally, snprintf() is used here, too, but it could be
replaced.

Converting to posix_spawn() is a no-go because posix_spawn() has no
facility for changing rlimits or chroot on the spawned process.

In summary, I don't think the gains are worth it.  Multithreaded
processes should just not fork().
This commit is contained in:
Nicholas J. Kain 2022-02-25 04:53:02 -05:00
parent 62aef529f4
commit ba8f674e15

View File

@ -12,6 +12,17 @@
#include "nk/exec.h"
#include "nk/io.h"
/*
* Note that neither nk_generate_env or nk_execute are async signal safe, so
* these functions should only be called after fork() in a non-multithreaded
* process.
*
* I don't consider this to be a problem in general, since in a multithreaded process
* it would be far better to fork off a subprocess early on before threads are
* created and use a socketpair() to request subprocess creation from the
* single-threaded subprocess from the multithreaded main program.
*/
#define DEFAULT_ROOT_PATH "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"
#define DEFAULT_PATH "/bin:/usr/bin:/usr/local/bin"
#define MAX_ARGS 256
@ -19,9 +30,8 @@
#define NK_GEN_ENV(GEN_STR, ...) do { \
if (env_offset >= envlen) return -3; \
ssize_t snlen = snprintf(envbuf, envbuflen, GEN_STR "0", __VA_ARGS__); \
ssize_t snlen = snprintf(envbuf, envbuflen, GEN_STR, __VA_ARGS__); \
if (snlen < 0 || (size_t)snlen >= envbuflen) return -2; \
if (snlen > 0) envbuf[snlen-1] = 0; \
env[env_offset++] = envbuf; envbuf += snlen; envbuflen -= (size_t)snlen; \
} while (0)
@ -108,14 +118,13 @@ out:
}
#define NK_GEN_ARG(GEN_STR, ...) do { \
ssize_t snlen = snprintf(argbuf, argbuflen, GEN_STR "0", __VA_ARGS__); \
ssize_t snlen = snprintf(argbuf, argbuflen, GEN_STR, __VA_ARGS__); \
if (snlen < 0 || (size_t)snlen >= argbuflen) { \
static const char errstr[] = "nk_execute: constructing argument list failed\n"; \
safe_write(STDERR_FILENO, errstr, sizeof errstr); \
_Exit(EXIT_FAILURE); \
} \
if (snlen > 0) argbuf[snlen-1] = 0; \
argv[curv] = argbuf; argv[++curv] = (char *)0; \
argv[curv] = argbuf; argv[++curv] = NULL; \
argbuf += snlen; argbuflen -= (size_t)snlen; \
} while (0)
@ -167,8 +176,7 @@ endarg:
safe_write(STDERR_FILENO, errstr, sizeof errstr);
_Exit(EXIT_FAILURE);
}
const size_t len = (size_t)(p - q);
NK_GEN_ARG("%.*s", (int)len, q);
NK_GEN_ARG("%.*s", (int)(p - q), q);
q = p + 1;
if (atend || curv >= (MAX_ARGS - 1))
break;