From 21e1023d281bbdd8719a2279556fe19bc9aa478e Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 29 Jan 2021 22:00:10 -0800 Subject: [PATCH] Reduce memory requirements for execve() --- libc/calls/execve-nt.c | 7 ++----- libc/calls/execve-sysv.c | 7 ++++--- libc/calls/hefty/fork-nt.c | 4 ++-- libc/calls/mkntcmdline.c | 18 +++++++++++------- libc/calls/ntspawn.c | 4 ++-- libc/calls/ntspawn.h | 9 +++++---- test/libc/calls/mkntcmdline_test.c | 18 +++++++++--------- 7 files changed, 35 insertions(+), 32 deletions(-) diff --git a/libc/calls/execve-nt.c b/libc/calls/execve-nt.c index d22e027df..49ff0296f 100644 --- a/libc/calls/execve-nt.c +++ b/libc/calls/execve-nt.c @@ -47,11 +47,8 @@ textwindows int execve$nt(const char *program, char *const argv[], close(i); } } - for (i = 0; argv[i];) ++i; - i = (i + 1) * sizeof(char *); - argv = memcpy(alloca(i), argv, i); - memcpy(argv, &program, sizeof(program)); - rc = ntspawn(argv, envp, NULL, NULL, true, 0, NULL, &startinfo, &procinfo); + rc = ntspawn(program, argv, envp, NULL, NULL, true, 0, NULL, &startinfo, + &procinfo); if (rc == -1) return -1; CloseHandle(procinfo.hThread); do { diff --git a/libc/calls/execve-sysv.c b/libc/calls/execve-sysv.c index 6d99f3938..3814ad813 100644 --- a/libc/calls/execve-sysv.c +++ b/libc/calls/execve-sysv.c @@ -25,15 +25,16 @@ #include "libc/str/str.h" int execve$sysv(const char *prog, char *const argv[], char *const envp[]) { - size_t i, n; - char **shargs, bash[PATH_MAX]; + size_t i; + char **shargs; if (__execve$sysv(prog, argv, envp) != -1) return 0; if (errno != ENOEXEC) return -1; for (i = 0; argv[i];) ++i; shargs = alloca((i + 2) * sizeof(char *)); memcpy(shargs + 2, argv + 1, i * sizeof(char *)); shargs[0] = !IsFreebsd() ? _PATH_BSHELL - : firstnonnull(commandv("bash", bash), _PATH_BSHELL); + : firstnonnull(commandv("bash", alloca(PATH_MAX)), + _PATH_BSHELL); shargs[1] = prog; return __execve$sysv(shargs[0], shargs, envp); } diff --git a/libc/calls/hefty/fork-nt.c b/libc/calls/hefty/fork-nt.c index 613b1a7a5..5c85a314c 100644 --- a/libc/calls/hefty/fork-nt.c +++ b/libc/calls/hefty/fork-nt.c @@ -137,8 +137,8 @@ textwindows int fork$nt(void) { startinfo.hStdInput = g_fds.p[0].handle; startinfo.hStdOutput = g_fds.p[1].handle; startinfo.hStdError = g_fds.p[2].handle; - if (ntspawn(g_argv, environ, &kNtIsInheritable, NULL, true, 0, NULL, - &startinfo, &procinfo) != -1) { + if (ntspawn(g_argv[0], g_argv, environ, &kNtIsInheritable, NULL, true, 0, + NULL, &startinfo, &procinfo) != -1) { CloseHandle(reader); CloseHandle(procinfo.hThread); if (weaken(__sighandrvas) && diff --git a/libc/calls/mkntcmdline.c b/libc/calls/mkntcmdline.c index a30b228a7..1339cdcfb 100644 --- a/libc/calls/mkntcmdline.c +++ b/libc/calls/mkntcmdline.c @@ -30,24 +30,28 @@ * GetDosArgv() or GetDosArgv(). This function does NOT escape * 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 * @return freshly allocated lpCommandLine or NULL w/ errno * @kudos Daniel Colascione for teaching how to quote * @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; wint_t x, y; int slashes, n; size_t i, j, k; bool needsquote; char16_t cbuf[2]; - for (k = i = 0; argv[i]; ++i) { + for (arg = prog, k = i = 0; arg; arg = argv[++i]) { if (i) { cmdline[k++] = u' '; 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) { cmdline[k++] = u'"'; if (k == ARG_MAX) return e2big(); @@ -55,20 +59,20 @@ textwindows int mkntcmdline(char16_t cmdline[ARG_MAX], char *const argv[]) { for (j = 0;;) { if (needsquote) { slashes = 0; - while (argv[i][j] && argv[i][j] == '\\') slashes++, j++; + while (arg[j] && arg[j] == '\\') slashes++, j++; slashes <<= 1; - if (argv[i][j] == '"') slashes++; + if (arg[j] == '"') slashes++; while (slashes--) { cmdline[k++] = u'\\'; if (k == ARG_MAX) return e2big(); } } - x = argv[i][j++] & 0xff; + x = arg[j++] & 0xff; if (x >= 0300) { n = ThomPikeLen(x); x = ThomPikeByte(x); while (--n) { - if ((y = argv[i][j++] & 0xff)) { + if ((y = arg[j++] & 0xff)) { x = ThomPikeMerge(x, y); } else { x = 0; diff --git a/libc/calls/ntspawn.c b/libc/calls/ntspawn.c index cbb2a7995..a3606b50d 100644 --- a/libc/calls/ntspawn.c +++ b/libc/calls/ntspawn.c @@ -58,7 +58,7 @@ struct SpawnBlock { * @see spawnve() which abstracts this function */ 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_lpThreadAttributes, bool32 bInheritHandles, uint32_t dwCreationFlags, const char16_t *opt_lpCurrentDirectory, @@ -80,7 +80,7 @@ textwindows int ntspawn( (block = MapViewOfFileExNuma(handle, kNtFileMapRead | kNtFileMapWrite, 0, 0, blocksize, NULL, kNtNumaNoPreferredNode))) { - if (mkntcmdline(block->cmdline, argv) != -1 && + if (mkntcmdline(block->cmdline, prog, argv) != -1 && mkntenvblock(block->envvars, envp) != -1) { if (CreateProcess(NULL, block->cmdline, opt_lpProcessAttributes, opt_lpThreadAttributes, bInheritHandles, diff --git a/libc/calls/ntspawn.h b/libc/calls/ntspawn.h index e8c1a080e..6933ed293 100644 --- a/libc/calls/ntspawn.h +++ b/libc/calls/ntspawn.h @@ -6,11 +6,12 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) 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 ntspawn(char *const[], char *const[], struct NtSecurityAttributes *, - struct NtSecurityAttributes *, bool32, uint32_t, const char16_t *, - const struct NtStartupInfo *, struct NtProcessInformation *) hidden; +int ntspawn(const char *, char *const[], char *const[], + struct NtSecurityAttributes *, struct NtSecurityAttributes *, + bool32, uint32_t, const char16_t *, const struct NtStartupInfo *, + struct NtProcessInformation *) hidden; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/test/libc/calls/mkntcmdline_test.c b/test/libc/calls/mkntcmdline_test.c index 6e72d4062..0e68742bd 100644 --- a/test/libc/calls/mkntcmdline_test.c +++ b/test/libc/calls/mkntcmdline_test.c @@ -27,49 +27,49 @@ char16_t cmdline[ARG_MAX]; TEST(mkntcmdline, emptyArgvList_isEmpty) { char *argv[] = {NULL}; - EXPECT_NE(-1, mkntcmdline(cmdline, argv)); + EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv)); EXPECT_STREQ(u"", cmdline); } TEST(mkntcmdline, emptyArg_getsQuoted) { char *argv[] = {"", NULL}; - EXPECT_NE(-1, mkntcmdline(cmdline, argv)); + EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv)); EXPECT_STREQ(u"\"\"", cmdline); } TEST(mkntcmdline, ignoranceIsBliss) { 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); } TEST(mkntcmdline, spaceInArgument_getQuotesWrappedAround) { 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); } TEST(mkntcmdline, justQuote) { char *argv[] = {"\"", NULL}; - EXPECT_NE(-1, mkntcmdline(cmdline, argv)); + EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv)); EXPECT_STREQ(u"\"\\\"\"", cmdline); } TEST(mkntcmdline, justSlash) { char *argv[] = {"\\", NULL}; - EXPECT_NE(-1, mkntcmdline(cmdline, argv)); + EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv)); EXPECT_STREQ(u"\\", cmdline); } TEST(mkntcmdline, justSlashQuote) { char *argv[] = {"\\\"", NULL}; - EXPECT_NE(-1, mkntcmdline(cmdline, argv)); + EXPECT_NE(-1, mkntcmdline(cmdline, argv[0], argv)); EXPECT_STREQ(u"\"\\\\\\\"\"" /* "\\\"" */, cmdline); } TEST(mkntcmdline, basicQuoting) { 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); } @@ -79,7 +79,7 @@ TEST(mkntcmdline, testUnicode) { strdup("要依法治国是赞美那些谁是公义的和惩罚恶人。 - 韩非"), NULL, }; - EXPECT_NE(-1, mkntcmdline(cmdline, argv1)); + EXPECT_NE(-1, mkntcmdline(cmdline, argv1[0], argv1)); EXPECT_STREQ(u"(╯°□°)╯ \"要依法治国是赞美那些谁是公义的和惩罚恶人。 - 韩非\"", cmdline); }