diff --git a/libc/calls/calls.h b/libc/calls/calls.h index f6fc42a04..2604a1c54 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -84,6 +84,7 @@ int execv(const char *, char *const[]); int execve(const char *, char *const[], char *const[]); int execvp(const char *, char *const[]); int execvpe(const char *, char *const[], char *const[]); +int fexecve(int, char *const[], char *const[]); int faccessat(int, const char *, int, uint32_t); int fadvise(int, uint64_t, uint64_t, int); int fchdir(int); diff --git a/libc/calls/fexecve.c b/libc/calls/fexecve.c new file mode 100644 index 000000000..224217cf0 --- /dev/null +++ b/libc/calls/fexecve.c @@ -0,0 +1,78 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/dce.h" +#include "libc/fmt/itoa.h" +#include "libc/intrin/asan.internal.h" +#include "libc/intrin/kprintf.h" +#include "libc/intrin/likely.h" +#include "libc/intrin/strace.internal.h" +#include "libc/str/str.h" +#include "libc/sysv/errfuns.h" + +int sys_fexecve(int, char *const[], char *const[]); + +/** + * Executes binary executable at file descriptor. + * + * This is only supported on Linux and FreeBSD. APE binaries currently + * aren't supported. + * + * @param fd is opened executable and current file position is ignored + * @return doesn't return on success, otherwise -1 w/ errno + * @raise ENOEXEC if file at `fd` isn't an assimilated ELF executable + * @raise ENOSYS on Windows, XNU, OpenBSD, NetBSD, and Metal + */ +int fexecve(int fd, char *const argv[], char *const envp[]) { + int rc; + size_t i; + if (!argv || !envp || + (IsAsan() && + (!__asan_is_valid_strlist(argv) || !__asan_is_valid_strlist(envp)))) { + rc = efault(); + } else { +#ifdef SYSDEBUG + if (UNLIKELY(__strace > 0)) { + kprintf(STRACE_PROLOGUE "fexecve(%d, {", fd); + for (i = 0; argv[i]; ++i) { + if (i) kprintf(", "); + kprintf("%#s", argv[i]); + } + kprintf("}, {"); + for (i = 0; envp[i]; ++i) { + if (i) kprintf(", "); + kprintf("%#s", envp[i]); + } + kprintf("})\n"); + } +#endif + if (IsLinux()) { + char path[14 + 12]; + FormatInt32(stpcpy(path, "/proc/self/fd/"), fd); + rc = __sys_execve(path, argv, envp); + } else if (IsFreebsd()) { + rc = sys_fexecve(fd, argv, envp); + } else { + rc = enosys(); + } + } + STRACE("fexecve(%d) failed %d% m", fd, rc); + return rc; +} diff --git a/libc/sysv/calls/sys_fexecve.s b/libc/sysv/calls/sys_fexecve.s new file mode 100644 index 000000000..3490a9ddb --- /dev/null +++ b/libc/sysv/calls/sys_fexecve.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall sys_fexecve,0xffffff1ecfffffff,globl,hidden diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index 458f4b99c..0850d4bcd 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -204,7 +204,7 @@ syscon open O_NOFOLLOW_ANY 0 0x20000000 0 0 0 0 # syscon open O_SYNC 0x00101000 0x00000080 0x00000080 0x00000080 0x00000080 0 # bsd consensus syscon open O_NOCTTY 0x00000100 0x00020000 0x00008000 0x00008000 0x00008000 0 # used for remote viewing (default behavior on freebsd) syscon open O_NOATIME 0x00040000 0 0 0 0 0 # optimize away access time update -syscon open O_EXEC 0 0 0x00040000 0 0x04000000 0 # it's specified by posix what does it mean +syscon open O_EXEC 0x00200000 0 0x00040000 0 0x04000000 0 # open only for executing (POSIX.1 hack for when file mode is 0111); see fexecve(); O_PATH on Linux syscon open O_SEARCH 0 0 0x00040000 0 0x00800000 0 # it's specified by posix what does it mean syscon open O_DSYNC 0x00001000 0x00400000 0 0x00000080 0x00010000 0 # syscon open O_RSYNC 0x00101000 0 0 0x00000080 0x00020000 0 # diff --git a/libc/sysv/consts/O_EXEC.s b/libc/sysv/consts/O_EXEC.s index 2337014db..05a0ef1a8 100644 --- a/libc/sysv/consts/O_EXEC.s +++ b/libc/sysv/consts/O_EXEC.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon open,O_EXEC,0,0,0x00040000,0,0x04000000,0 +.syscon open,O_EXEC,0x00200000,0,0x00040000,0,0x04000000,0 diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index 17a4febab..0619c452c 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -673,7 +673,7 @@ scall sys_bsdthread_register 0xfffffffff216efff globl hidden #scall extattr_set_file 0x169fff164fffffff globl #scall extattr_set_link 0x16ffff19cfffffff globl #scall extattrctl 0x168fff163fffffff globl -#scall fexecve 0x1d1fff1ecfffffff globl +scall sys_fexecve 0xffffff1ecfffffff globl hidden #scall ffclock_getcounter 0xffffff0f1fffffff globl #scall ffclock_getestimate 0xffffff0f3fffffff globl #scall ffclock_setestimate 0xffffff0f2fffffff globl diff --git a/test/libc/calls/fexecve_test.c b/test/libc/calls/fexecve_test.c new file mode 100644 index 000000000..5afd0ec7a --- /dev/null +++ b/test/libc/calls/fexecve_test.c @@ -0,0 +1,66 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/dce.h" +#include "libc/runtime/runtime.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/o.h" +#include "libc/testlib/subprocess.h" +#include "libc/testlib/testlib.h" + +int fds[2]; +char buf[8]; +char testlib_enable_tmp_setup_teardown; + +TEST(execve, elfIsUnreadable_mayBeExecuted) { + if (IsWindows() || IsXnu()) return; + testlib_extract("/zip/echo", "echo", 0111); + ASSERT_SYS(0, 0, pipe2(fds, O_CLOEXEC)); + SPAWN(vfork); + ASSERT_SYS(0, 1, dup2(4, 1)); + ASSERT_SYS( + 0, 0, + execve("echo", (char *const[]){"echo", "hi", 0}, (char *const[]){0})); + notpossible; + EXITS(0); + bzero(buf, 8); + ASSERT_SYS(0, 0, close(4)); + ASSERT_SYS(0, 3, read(3, buf, 7)); + ASSERT_SYS(0, 0, close(3)); + ASSERT_STREQ("hi\n", buf); +} + +TEST(fexecve, elfIsUnreadable_mayBeExecuted) { + if (!IsLinux() && !IsFreebsd()) return; + testlib_extract("/zip/echo", "echo", 0111); + ASSERT_SYS(0, 0, pipe2(fds, O_CLOEXEC)); + SPAWN(vfork); + ASSERT_SYS(0, 1, dup2(4, 1)); + ASSERT_SYS(0, 5, open("echo", O_EXEC | O_CLOEXEC)); + if (IsFreebsd()) ASSERT_SYS(0, 1, lseek(5, 1, SEEK_SET)); + ASSERT_SYS(0, 0, + fexecve(5, (char *const[]){"echo", "hi", 0}, (char *const[]){0})); + notpossible; + EXITS(0); + bzero(buf, 8); + ASSERT_SYS(0, 0, close(4)); + ASSERT_SYS(0, 3, read(3, buf, 7)); + ASSERT_SYS(0, 0, close(3)); + ASSERT_STREQ("hi\n", buf); +} diff --git a/test/libc/calls/test.mk b/test/libc/calls/test.mk index ff00f3200..861f419aa 100644 --- a/test/libc/calls/test.mk +++ b/test/libc/calls/test.mk @@ -88,6 +88,16 @@ o/$(MODE)/test/libc/calls/life-nomod.com.dbg: \ $(APE_NO_MODIFY_SELF) @$(APELINK) +o/$(MODE)/test/libc/calls/fexecve_test.com.dbg: \ + $(TEST_LIBC_CALLS_DEPS) \ + o/$(MODE)/test/libc/calls/fexecve_test.o \ + o/$(MODE)/test/libc/calls/calls.pkg \ + o/$(MODE)/tool/build/echo.zip.o \ + $(LIBC_TESTMAIN) \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) + @$(APELINK) + o/$(MODE)/test/libc/calls/tiny64.elf.zip.o \ o/$(MODE)/test/libc/calls/life-nomod.com.zip.o \ o/$(MODE)/test/libc/calls/life-classic.com.zip.o: private \ diff --git a/test/libc/zipos/open_test.c b/test/libc/zipos/open_test.c index 25c8edbc7..d90e39be2 100644 --- a/test/libc/zipos/open_test.c +++ b/test/libc/zipos/open_test.c @@ -17,15 +17,14 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/mem/mem.h" #include "libc/mem/gc.h" +#include "libc/mem/mem.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/sysv/consts/o.h" #include "libc/testlib/hyperion.h" #include "libc/testlib/testlib.h" #include "libc/thread/spawn.h" -#include "libc/zipos/zipos.h" STATIC_YOINK("zip_uri_support"); STATIC_YOINK("libc/testlib/hyperion.txt");