Reduce memory requirements for execve()

This commit is contained in:
Justine Tunney 2021-01-29 22:00:10 -08:00
parent eaca5b3e81
commit 21e1023d28
7 changed files with 35 additions and 32 deletions

View file

@ -47,11 +47,8 @@ textwindows int execve$nt(const char *program, char *const argv[],
close(i); close(i);
} }
} }
for (i = 0; argv[i];) ++i; rc = ntspawn(program, argv, envp, NULL, NULL, true, 0, NULL, &startinfo,
i = (i + 1) * sizeof(char *); &procinfo);
argv = memcpy(alloca(i), argv, i);
memcpy(argv, &program, sizeof(program));
rc = ntspawn(argv, envp, NULL, NULL, true, 0, NULL, &startinfo, &procinfo);
if (rc == -1) return -1; if (rc == -1) return -1;
CloseHandle(procinfo.hThread); CloseHandle(procinfo.hThread);
do { do {

View file

@ -25,15 +25,16 @@
#include "libc/str/str.h" #include "libc/str/str.h"
int execve$sysv(const char *prog, char *const argv[], char *const envp[]) { int execve$sysv(const char *prog, char *const argv[], char *const envp[]) {
size_t i, n; size_t i;
char **shargs, bash[PATH_MAX]; char **shargs;
if (__execve$sysv(prog, argv, envp) != -1) return 0; if (__execve$sysv(prog, argv, envp) != -1) return 0;
if (errno != ENOEXEC) return -1; if (errno != ENOEXEC) return -1;
for (i = 0; argv[i];) ++i; for (i = 0; argv[i];) ++i;
shargs = alloca((i + 2) * sizeof(char *)); shargs = alloca((i + 2) * sizeof(char *));
memcpy(shargs + 2, argv + 1, i * sizeof(char *)); memcpy(shargs + 2, argv + 1, i * sizeof(char *));
shargs[0] = !IsFreebsd() ? _PATH_BSHELL shargs[0] = !IsFreebsd() ? _PATH_BSHELL
: firstnonnull(commandv("bash", bash), _PATH_BSHELL); : firstnonnull(commandv("bash", alloca(PATH_MAX)),
_PATH_BSHELL);
shargs[1] = prog; shargs[1] = prog;
return __execve$sysv(shargs[0], shargs, envp); return __execve$sysv(shargs[0], shargs, envp);
} }

View file

@ -137,8 +137,8 @@ textwindows int fork$nt(void) {
startinfo.hStdInput = g_fds.p[0].handle; startinfo.hStdInput = g_fds.p[0].handle;
startinfo.hStdOutput = g_fds.p[1].handle; startinfo.hStdOutput = g_fds.p[1].handle;
startinfo.hStdError = g_fds.p[2].handle; startinfo.hStdError = g_fds.p[2].handle;
if (ntspawn(g_argv, environ, &kNtIsInheritable, NULL, true, 0, NULL, if (ntspawn(g_argv[0], g_argv, environ, &kNtIsInheritable, NULL, true, 0,
&startinfo, &procinfo) != -1) { NULL, &startinfo, &procinfo) != -1) {
CloseHandle(reader); CloseHandle(reader);
CloseHandle(procinfo.hThread); CloseHandle(procinfo.hThread);
if (weaken(__sighandrvas) && if (weaken(__sighandrvas) &&

View file

@ -30,24 +30,28 @@
* GetDosArgv() or GetDosArgv(). This function does NOT escape * GetDosArgv() or GetDosArgv(). This function does NOT escape
* command interpreter syntax, e.g. $VAR (sh), %VAR% (cmd). * command interpreter syntax, e.g. $VAR (sh), %VAR% (cmd).
* *
* @param cmdline is output buffer
* @param prog is used as argv[0]
* @param argv is an a NULL-terminated array of UTF-8 strings * @param argv is an a NULL-terminated array of UTF-8 strings
* @return freshly allocated lpCommandLine or NULL w/ errno * @return freshly allocated lpCommandLine or NULL w/ errno
* @kudos Daniel Colascione for teaching how to quote * @kudos Daniel Colascione for teaching how to quote
* @see libc/runtime/dosargv.c * @see libc/runtime/dosargv.c
*/ */
textwindows int mkntcmdline(char16_t cmdline[ARG_MAX], char *const argv[]) { textwindows int mkntcmdline(char16_t cmdline[ARG_MAX], const char *prog,
char *const argv[]) {
char *arg;
uint64_t w; uint64_t w;
wint_t x, y; wint_t x, y;
int slashes, n; int slashes, n;
size_t i, j, k; size_t i, j, k;
bool needsquote; bool needsquote;
char16_t cbuf[2]; char16_t cbuf[2];
for (k = i = 0; argv[i]; ++i) { for (arg = prog, k = i = 0; arg; arg = argv[++i]) {
if (i) { if (i) {
cmdline[k++] = u' '; cmdline[k++] = u' ';
if (k == ARG_MAX) return e2big(); if (k == ARG_MAX) return e2big();
} }
needsquote = !*argv[i] || argv[i][strcspn(argv[i], " \t\n\v\"")]; needsquote = !arg[0] || arg[strcspn(arg, " \t\n\v\"")];
if (needsquote) { if (needsquote) {
cmdline[k++] = u'"'; cmdline[k++] = u'"';
if (k == ARG_MAX) return e2big(); if (k == ARG_MAX) return e2big();
@ -55,20 +59,20 @@ textwindows int mkntcmdline(char16_t cmdline[ARG_MAX], char *const argv[]) {
for (j = 0;;) { for (j = 0;;) {
if (needsquote) { if (needsquote) {
slashes = 0; slashes = 0;
while (argv[i][j] && argv[i][j] == '\\') slashes++, j++; while (arg[j] && arg[j] == '\\') slashes++, j++;
slashes <<= 1; slashes <<= 1;
if (argv[i][j] == '"') slashes++; if (arg[j] == '"') slashes++;
while (slashes--) { while (slashes--) {
cmdline[k++] = u'\\'; cmdline[k++] = u'\\';
if (k == ARG_MAX) return e2big(); if (k == ARG_MAX) return e2big();
} }
} }
x = argv[i][j++] & 0xff; x = arg[j++] & 0xff;
if (x >= 0300) { if (x >= 0300) {
n = ThomPikeLen(x); n = ThomPikeLen(x);
x = ThomPikeByte(x); x = ThomPikeByte(x);
while (--n) { while (--n) {
if ((y = argv[i][j++] & 0xff)) { if ((y = arg[j++] & 0xff)) {
x = ThomPikeMerge(x, y); x = ThomPikeMerge(x, y);
} else { } else {
x = 0; x = 0;

View file

@ -58,7 +58,7 @@ struct SpawnBlock {
* @see spawnve() which abstracts this function * @see spawnve() which abstracts this function
*/ */
textwindows int ntspawn( textwindows int ntspawn(
char *const argv[], char *const envp[], const char *prog, char *const argv[], char *const envp[],
struct NtSecurityAttributes *opt_lpProcessAttributes, struct NtSecurityAttributes *opt_lpProcessAttributes,
struct NtSecurityAttributes *opt_lpThreadAttributes, bool32 bInheritHandles, struct NtSecurityAttributes *opt_lpThreadAttributes, bool32 bInheritHandles,
uint32_t dwCreationFlags, const char16_t *opt_lpCurrentDirectory, uint32_t dwCreationFlags, const char16_t *opt_lpCurrentDirectory,
@ -80,7 +80,7 @@ textwindows int ntspawn(
(block = (block =
MapViewOfFileExNuma(handle, kNtFileMapRead | kNtFileMapWrite, 0, 0, MapViewOfFileExNuma(handle, kNtFileMapRead | kNtFileMapWrite, 0, 0,
blocksize, NULL, kNtNumaNoPreferredNode))) { blocksize, NULL, kNtNumaNoPreferredNode))) {
if (mkntcmdline(block->cmdline, argv) != -1 && if (mkntcmdline(block->cmdline, prog, argv) != -1 &&
mkntenvblock(block->envvars, envp) != -1) { mkntenvblock(block->envvars, envp) != -1) {
if (CreateProcess(NULL, block->cmdline, opt_lpProcessAttributes, if (CreateProcess(NULL, block->cmdline, opt_lpProcessAttributes,
opt_lpThreadAttributes, bInheritHandles, opt_lpThreadAttributes, bInheritHandles,

View file

@ -6,11 +6,12 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
int mkntcmdline(char16_t[ARG_MAX], char *const[]) hidden; int mkntcmdline(char16_t[ARG_MAX], const char *, char *const[]) hidden;
int mkntenvblock(char16_t[ARG_MAX], char *const[]) hidden; int mkntenvblock(char16_t[ARG_MAX], char *const[]) hidden;
int ntspawn(char *const[], char *const[], struct NtSecurityAttributes *, int ntspawn(const char *, char *const[], char *const[],
struct NtSecurityAttributes *, bool32, uint32_t, const char16_t *, struct NtSecurityAttributes *, struct NtSecurityAttributes *,
const struct NtStartupInfo *, struct NtProcessInformation *) hidden; bool32, uint32_t, const char16_t *, const struct NtStartupInfo *,
struct NtProcessInformation *) hidden;
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -27,49 +27,49 @@ char16_t cmdline[ARG_MAX];
TEST(mkntcmdline, emptyArgvList_isEmpty) { TEST(mkntcmdline, emptyArgvList_isEmpty) {
char *argv[] = {NULL}; char *argv[] = {NULL};
EXPECT_NE(-1, mkntcmdline(cmdline, argv)); EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv));
EXPECT_STREQ(u"", cmdline); EXPECT_STREQ(u"", cmdline);
} }
TEST(mkntcmdline, emptyArg_getsQuoted) { TEST(mkntcmdline, emptyArg_getsQuoted) {
char *argv[] = {"", NULL}; char *argv[] = {"", NULL};
EXPECT_NE(-1, mkntcmdline(cmdline, argv)); EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv));
EXPECT_STREQ(u"\"\"", cmdline); EXPECT_STREQ(u"\"\"", cmdline);
} }
TEST(mkntcmdline, ignoranceIsBliss) { TEST(mkntcmdline, ignoranceIsBliss) {
char *argv[] = {"echo", "hello", "world", NULL}; char *argv[] = {"echo", "hello", "world", NULL};
EXPECT_NE(-1, mkntcmdline(cmdline, argv)); EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv));
EXPECT_STREQ(u"echo hello world", cmdline); EXPECT_STREQ(u"echo hello world", cmdline);
} }
TEST(mkntcmdline, spaceInArgument_getQuotesWrappedAround) { TEST(mkntcmdline, spaceInArgument_getQuotesWrappedAround) {
char *argv[] = {"echo", "hello there", "world", NULL}; char *argv[] = {"echo", "hello there", "world", NULL};
EXPECT_NE(-1, mkntcmdline(cmdline, argv)); EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv));
EXPECT_STREQ(u"echo \"hello there\" world", cmdline); EXPECT_STREQ(u"echo \"hello there\" world", cmdline);
} }
TEST(mkntcmdline, justQuote) { TEST(mkntcmdline, justQuote) {
char *argv[] = {"\"", NULL}; char *argv[] = {"\"", NULL};
EXPECT_NE(-1, mkntcmdline(cmdline, argv)); EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv));
EXPECT_STREQ(u"\"\\\"\"", cmdline); EXPECT_STREQ(u"\"\\\"\"", cmdline);
} }
TEST(mkntcmdline, justSlash) { TEST(mkntcmdline, justSlash) {
char *argv[] = {"\\", NULL}; char *argv[] = {"\\", NULL};
EXPECT_NE(-1, mkntcmdline(cmdline, argv)); EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv));
EXPECT_STREQ(u"\\", cmdline); EXPECT_STREQ(u"\\", cmdline);
} }
TEST(mkntcmdline, justSlashQuote) { TEST(mkntcmdline, justSlashQuote) {
char *argv[] = {"\\\"", NULL}; char *argv[] = {"\\\"", NULL};
EXPECT_NE(-1, mkntcmdline(cmdline, argv)); EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv));
EXPECT_STREQ(u"\"\\\\\\\"\"" /* "\\\"" */, cmdline); EXPECT_STREQ(u"\"\\\\\\\"\"" /* "\\\"" */, cmdline);
} }
TEST(mkntcmdline, basicQuoting) { TEST(mkntcmdline, basicQuoting) {
char *argv[] = {"a\"b c", "d", NULL}; char *argv[] = {"a\"b c", "d", NULL};
EXPECT_NE(-1, mkntcmdline(cmdline, argv)); EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv));
EXPECT_STREQ(u"\"a\\\"b c\" d" /* "a\"b c" d */, cmdline); EXPECT_STREQ(u"\"a\\\"b c\" d" /* "a\"b c" d */, cmdline);
} }
@ -79,7 +79,7 @@ TEST(mkntcmdline, testUnicode) {
strdup("要依法治国是赞美那些谁是公义的和惩罚恶人。 - 韩非"), strdup("要依法治国是赞美那些谁是公义的和惩罚恶人。 - 韩非"),
NULL, NULL,
}; };
EXPECT_NE(-1, mkntcmdline(cmdline, argv1)); EXPECT_NE(-1, mkntcmdline(cmdline, argv1[0], argv1));
EXPECT_STREQ(u"(╯°□°)╯ \"要依法治国是赞美那些谁是公义的和惩罚恶人。 - 韩非\"", EXPECT_STREQ(u"(╯°□°)╯ \"要依法治国是赞美那些谁是公义的和惩罚恶人。 - 韩非\"",
cmdline); cmdline);
} }