This commit is contained in:
Gavin Hayes 2024-02-27 11:24:36 -08:00 committed by GitHub
commit d89acf79cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 259 additions and 152 deletions

View file

@ -28,3 +28,11 @@ bool IsApeLoadable(char buf[8]) {
READ64LE(buf) == READ64LE("jartsr='") || READ64LE(buf) == READ64LE("jartsr='") ||
READ64LE(buf) == READ64LE("APEDBG='"); READ64LE(buf) == READ64LE("APEDBG='");
} }
/**
* Returns true if executable image is an APE
*/
bool IsApeMagic(const char buf[8]) {
return READ64LE(buf) == READ64LE("MZqFpD='") ||
READ64LE(buf) == READ64LE("JTqFpD='");
}

View file

@ -76,7 +76,7 @@ int execve(const char *prog, char *const argv[], char *const envp[]) {
rc = _weaken(sys_pledge_linux)(__execpromises, __pledge_mode); rc = _weaken(sys_pledge_linux)(__execpromises, __pledge_mode);
} }
if (!rc) { if (!rc) {
if (0 && _weaken(__zipos_parseuri) && if (_weaken(__zipos_parseuri) &&
(_weaken(__zipos_parseuri)(prog, &uri) != -1)) { (_weaken(__zipos_parseuri)(prog, &uri) != -1)) {
rc = _weaken(__zipos_open)(&uri, O_RDONLY | O_CLOEXEC); rc = _weaken(__zipos_open)(&uri, O_RDONLY | O_CLOEXEC);
if (rc != -1) { if (rc != -1) {

View file

@ -2,7 +2,8 @@
#define COSMOPOLITAN_LIBC_CALLS_EXECVE_SYSV_H_ #define COSMOPOLITAN_LIBC_CALLS_EXECVE_SYSV_H_
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
bool IsApeLoadable(char[8]) libcesque; bool IsApeLoadable(char[8]);
bool IsApeMagic(const char[8]);
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_CALLS_EXECVE_SYSV_H_ */ #endif /* COSMOPOLITAN_LIBC_CALLS_EXECVE_SYSV_H_ */

View file

@ -21,6 +21,7 @@
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h" #include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigset.internal.h" #include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/stat.internal.h" #include "libc/calls/struct/stat.internal.h"
#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall-sysv.internal.h"
@ -34,6 +35,7 @@
#include "libc/intrin/strace.internal.h" #include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h" #include "libc/intrin/weaken.h"
#include "libc/limits.h" #include "libc/limits.h"
#include "libc/paths.h"
#include "libc/proc/execve.internal.h" #include "libc/proc/execve.internal.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/f.h" #include "libc/sysv/consts/f.h"
@ -42,12 +44,14 @@
#include "libc/sysv/consts/mfd.h" #include "libc/sysv/consts/mfd.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/s.h"
#include "libc/sysv/consts/shm.h" #include "libc/sysv/consts/shm.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
#include "libc/zip.internal.h"
static bool IsAPEFd(const int fd) { static bool IsAPEFd(const int fd) {
char buf[8]; char buf[8];
return (sys_pread(fd, buf, 8, 0, 0) == 8) && IsApeLoadable(buf); return (sys_pread(fd, buf, 8, 0, 0) == 8) && IsApeMagic(buf);
} }
static int fexecve_impl(const int fd, char *const argv[], char *const envp[]) { static int fexecve_impl(const int fd, char *const argv[], char *const envp[]) {
@ -64,50 +68,74 @@ static int fexecve_impl(const int fd, char *const argv[], char *const envp[]) {
return rc; return rc;
} }
typedef enum { #define defer(fn) __attribute__((cleanup(fn)))
PTF_NUM = 1 << 0,
PTF_NUM2 = 1 << 1,
PTF_NUM3 = 1 << 2,
PTF_ANY = 1 << 3
} PTF_PARSE;
static bool ape_to_elf(void *ape, const size_t apesize) { void cleanup_close(int *pFD) {
static const char printftok[] = "printf '"; STRACE("time to close");
static const size_t printftoklen = sizeof(printftok) - 1; if (*pFD != -1) {
const char *tok = memmem(ape, apesize, printftok, printftoklen); close(*pFD);
if (tok) {
tok += printftoklen;
uint8_t *dest = ape;
PTF_PARSE state = PTF_ANY;
uint8_t value = 0;
for (; tok < (const char *)(dest + apesize); tok++) {
if ((state & (PTF_NUM | PTF_NUM2 | PTF_NUM3)) &&
(*tok >= '0' && *tok <= '7')) {
value = (value << 3) | (*tok - '0');
state <<= 1;
if (state & PTF_ANY) {
*dest++ = value;
}
} else if (state & PTF_NUM) {
break;
} else {
if (state & (PTF_NUM2 | PTF_NUM3)) {
*dest++ = value;
}
if (*tok == '\\') {
state = PTF_NUM;
value = 0;
} else if (*tok == '\'') {
return true;
} else {
*dest++ = *tok;
state = PTF_ANY;
}
}
}
} }
errno = ENOEXEC; }
return false; #define defer_close defer(cleanup_close)
void cleanup_unlink(const char **path) {
STRACE("time to unlink");
if (*path != NULL) {
sys_unlink(*path);
}
}
#define defer_unlink defer(cleanup_unlink)
#undef defer_unlink
#undef defer_close
#undef defer
static inline int isZipFile(const void *data, size_t data_size) {
if (!_weaken(GetZipEocd)) {
return enosys();
}
int ziperror;
return _weaken(GetZipEocd)(data, data_size, &ziperror) != NULL;
}
static int isFdAZipFile(const int fd) {
if (!_weaken(mmap) || !_weaken(munmap) || !_weaken(GetZipEocd) || __vforked) {
return enosys();
}
struct stat st;
if (fstat(fd, &st) == -1) {
return -1;
}
void *space = _weaken(mmap)(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (space == MAP_FAILED) {
return -1;
}
int rc = isZipFile(space, st.st_size);
if(_weaken(munmap)(space, st.st_size) == -1) {
return -1;
}
return rc;
}
typedef enum {
FEXEF_ZIP = 1 << 0,
FEXEF_APE = 1 << 1
} FEXEF;
static inline int getFexeFlags(const void *data, size_t data_size) {
if (!_weaken(GetZipEocd)) {
return enosys();
}
int rc = isZipFile(data, data_size);
if (rc == -1) {
return -1;
}
int flags = rc << 0;
if (data_size >= 8) {
flags |= (int)IsApeMagic(data) << 1;
}
return flags;
} }
/** /**
@ -115,7 +143,7 @@ static bool ape_to_elf(void *ape, const size_t apesize) {
* *
* This does an inplace conversion of APE to ELF when detected!!!! * This does an inplace conversion of APE to ELF when detected!!!!
*/ */
static int fd_to_mem_fd(const int infd, char *path) { static int fd_to_mem_fd(const int infd, FEXEF *flags) {
if ((!IsLinux() && !IsFreebsd()) || !_weaken(mmap) || !_weaken(munmap)) { if ((!IsLinux() && !IsFreebsd()) || !_weaken(mmap) || !_weaken(munmap)) {
return enosys(); return enosys();
} }
@ -142,23 +170,13 @@ static int fd_to_mem_fd(const int infd, char *path) {
ssize_t readRc; ssize_t readRc;
readRc = pread(infd, space, st.st_size, 0); readRc = pread(infd, space, st.st_size, 0);
bool success = readRc != -1; bool success = readRc != -1;
if (success && (st.st_size > 8) && IsApeLoadable(space)) { if (success) {
int flags = fcntl(fd, F_GETFD); int fexe_flags = getFexeFlags(space, st.st_size);
if ((success = (flags != -1) && success = fexe_flags != -1;
(fcntl(fd, F_SETFD, flags & (~FD_CLOEXEC)) != -1) && *flags = fexe_flags;
ape_to_elf(space, st.st_size))) {
const int newfd = fcntl(fd, F_DUPFD, 9001);
if (newfd != -1) {
close(fd);
fd = newfd;
}
}
} }
const int e = errno; const int e = errno;
if ((_weaken(munmap)(space, st.st_size) != -1) && success) { if ((_weaken(munmap)(space, st.st_size) != -1) && success) {
if (path) {
FormatInt32(stpcpy(path, "COSMOPOLITAN_INIT_ZIPOS="), fd);
}
unassert(readRc == st.st_size); unassert(readRc == st.st_size);
return fd; return fd;
} else if (!success) { } else if (!success) {
@ -175,9 +193,9 @@ static int fd_to_mem_fd(const int infd, char *path) {
* Executes binary executable at file descriptor. * Executes binary executable at file descriptor.
* *
* This is only supported on Linux and FreeBSD. APE binaries are * This is only supported on Linux and FreeBSD. APE binaries are
* supported. Zipos is supported. Zipos fds or FD_CLOEXEC APE fds or * supported. Zipos is supported. Zipos fds are copied to a new memfd. Zip files
* fds that fail fexecve with ENOEXEC are copied to a new memfd (with * and APE files are F_DUPFD to a high number with FD_CLOEXEC turned off. APE
* in-memory APE to ELF conversion) and fexecve is (re)attempted. * files are ran with execve.
* *
* @param fd is opened executable and current file position is ignored * @param fd is opened executable and current file position is ignored
* @return doesn't return on success, otherwise -1 w/ errno * @return doesn't return on success, otherwise -1 w/ errno
@ -193,55 +211,91 @@ int fexecve(int fd, char *const argv[], char *const envp[]) {
} else { } else {
STRACE("fexecve(%d, %s, %s) → ...", fd, DescribeStringList(argv), STRACE("fexecve(%d, %s, %s) → ...", fd, DescribeStringList(argv),
DescribeStringList(envp)); DescribeStringList(envp));
int savedErr = 0; int newfd = fd;
do { do {
if (!IsLinux() && !IsFreebsd()) { if (!IsLinux() && !IsFreebsd()) {
rc = enosys(); rc = enosys();
break; break;
} }
if (!__isfdkind(fd, kFdZip)) { FEXEF fflags;
bool memfdReq; if (__isfdkind(fd, kFdZip)) {
BLOCK_SIGNALS; BLOCK_SIGNALS;
BLOCK_CANCELATION; BLOCK_CANCELATION;
strace_enabled(-1); strace_enabled(-1);
memfdReq = ((rc = fcntl(fd, F_GETFD)) != -1) && (rc & FD_CLOEXEC) && newfd = fd_to_mem_fd(fd, &fflags);
IsAPEFd(fd);
strace_enabled(+1); strace_enabled(+1);
ALLOW_CANCELATION; ALLOW_CANCELATION;
ALLOW_SIGNALS; ALLOW_SIGNALS;
if (rc == -1) { if (newfd == -1) {
break; break;
} else if (!memfdReq) { }
fexecve_impl(fd, argv, envp); } else {
if (errno != ENOEXEC) { if (!__vforked) {
int isFdAZipFileRc;
BLOCK_SIGNALS;
BLOCK_CANCELATION;
strace_enabled(-1);
isFdAZipFileRc = isFdAZipFile(newfd);
strace_enabled(+1);
ALLOW_CANCELATION;
ALLOW_SIGNALS;
if (isFdAZipFileRc == -1) {
break; break;
} }
savedErr = ENOEXEC; fflags = isFdAZipFileRc << 0;
} }
bool isAPE;
BLOCK_SIGNALS;
BLOCK_CANCELATION;
isAPE = IsAPEFd(newfd);
ALLOW_CANCELATION;
ALLOW_SIGNALS;
fflags |= (int)isAPE << 1;
} }
int newfd; if (fflags) {
char *path = alloca(PATH_MAX); int flags;
BLOCK_SIGNALS; BLOCK_SIGNALS;
BLOCK_CANCELATION; BLOCK_CANCELATION;
strace_enabled(-1); flags = fcntl(newfd, F_GETFD);
newfd = fd_to_mem_fd(fd, path); if (flags != -1) {
strace_enabled(+1); flags = fcntl(newfd, F_SETFD, flags & (~FD_CLOEXEC));
ALLOW_CANCELATION; }
ALLOW_SIGNALS; ALLOW_CANCELATION;
if (newfd == -1) { ALLOW_SIGNALS;
if (flags == -1) {
break;
}
BLOCK_SIGNALS;
BLOCK_CANCELATION;
const int highfd = fcntl(newfd, F_DUPFD, 9001);
if (highfd != -1) {
close(newfd);
newfd = highfd;
}
ALLOW_CANCELATION;
ALLOW_SIGNALS;
}
if (fflags & FEXEF_ZIP) {
char *path = alloca(PATH_MAX);
FormatInt32(stpcpy(path, "COSMOPOLITAN_INIT_ZIPOS="), newfd);
size_t numenvs;
for (numenvs = 0; envp[numenvs];) ++numenvs;
static _Thread_local char *envs[500];
memcpy(envs, envp, numenvs * sizeof(char *));
envs[numenvs] = path;
envs[numenvs + 1] = NULL;
envp = envs;
}
if (fflags & FEXEF_APE) {
char path[14 + 12];
FormatInt32(stpcpy(path, "/dev/fd/"), newfd);
sys_execve(path, argv, envp);
break; break;
} }
size_t numenvs; fexecve_impl(newfd, argv, envp);
for (numenvs = 0; envp[numenvs];) ++numenvs; } while (0);
// const size_t desenvs = min(500, max(numenvs + 1, 2)); if (newfd != fd) {
static _Thread_local char *envs[500]; int keepErrno = errno;
memcpy(envs, envp, numenvs * sizeof(char *));
envs[numenvs] = path;
envs[numenvs + 1] = NULL;
fexecve_impl(newfd, argv, envs);
if (!savedErr) {
savedErr = errno;
}
BLOCK_SIGNALS; BLOCK_SIGNALS;
BLOCK_CANCELATION; BLOCK_CANCELATION;
strace_enabled(-1); strace_enabled(-1);
@ -249,9 +303,7 @@ int fexecve(int fd, char *const argv[], char *const envp[]) {
strace_enabled(+1); strace_enabled(+1);
ALLOW_CANCELATION; ALLOW_CANCELATION;
ALLOW_SIGNALS; ALLOW_SIGNALS;
} while (0); errno = keepErrno;
if (savedErr) {
errno = savedErr;
} }
rc = -1; rc = -1;
} }

View file

@ -19,9 +19,7 @@ TEST_LIBC_CALLS_BINS = \
$(TEST_LIBC_CALLS_COMS) \ $(TEST_LIBC_CALLS_COMS) \
$(TEST_LIBC_CALLS_COMS:%=%.dbg) \ $(TEST_LIBC_CALLS_COMS:%=%.dbg) \
o/$(MODE)/test/libc/calls/life-nomod.com \ o/$(MODE)/test/libc/calls/life-nomod.com \
o/$(MODE)/test/libc/calls/life-classic.com \ o/$(MODE)/test/libc/calls/life-classic.com
o/$(MODE)/test/libc/calls/zipread.com.dbg \
o/$(MODE)/test/libc/calls/zipread.com
TEST_LIBC_CALLS_TESTS = \ TEST_LIBC_CALLS_TESTS = \
$(TEST_LIBC_CALLS_SRCS_TEST:%.c=o/$(MODE)/%.com.ok) $(TEST_LIBC_CALLS_SRCS_TEST:%.c=o/$(MODE)/%.com.ok)
@ -118,8 +116,7 @@ o/$(MODE)/test/libc/calls/life-nomod.com.dbg: \
o/$(MODE)/test/libc/calls/tiny64.elf.zip.o \ 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-nomod.com.zip.o \
o/$(MODE)/test/libc/calls/life-classic.com.zip.o \ o/$(MODE)/test/libc/calls/life-classic.com.zip.o: private \
o/$(MODE)/test/libc/calls/zipread.com.zip.o: private \
ZIPOBJ_FLAGS += \ ZIPOBJ_FLAGS += \
-B -B

View file

@ -14,7 +14,9 @@ TEST_LIBC_PROC_COMS = \
TEST_LIBC_PROC_BINS = \ TEST_LIBC_PROC_BINS = \
$(TEST_LIBC_PROC_COMS) \ $(TEST_LIBC_PROC_COMS) \
$(TEST_LIBC_PROC_COMS:%=%.dbg) $(TEST_LIBC_PROC_COMS:%=%.dbg) \
o/$(MODE)/test/libc/proc/zipread.com.dbg \
o/$(MODE)/test/libc/proc/zipread.com
TEST_LIBC_PROC_TESTS = \ TEST_LIBC_PROC_TESTS = \
$(TEST_LIBC_PROC_SRCS_TEST:%.c=o/$(MODE)/%.com.ok) $(TEST_LIBC_PROC_SRCS_TEST:%.c=o/$(MODE)/%.com.ok)
@ -90,6 +92,7 @@ o/$(MODE)/test/libc/proc/execve_test.com.dbg: \
o/$(MODE)/test/libc/proc/execve_test.o \ o/$(MODE)/test/libc/proc/execve_test.o \
o/$(MODE)/test/libc/calls/life-nomod.com.zip.o \ o/$(MODE)/test/libc/calls/life-nomod.com.zip.o \
o/$(MODE)/test/libc/proc/execve_test_prog1.com.zip.o \ o/$(MODE)/test/libc/proc/execve_test_prog1.com.zip.o \
o/$(MODE)/test/libc/proc/echo.elf.zip.o \
o/$(MODE)/test/libc/mem/prog/life.elf.zip.o \ o/$(MODE)/test/libc/mem/prog/life.elf.zip.o \
o/$(MODE)/test/libc/mem/prog/sock.elf.zip.o \ o/$(MODE)/test/libc/mem/prog/sock.elf.zip.o \
o/$(MODE)/test/libc/proc/proc.pkg \ o/$(MODE)/test/libc/proc/proc.pkg \
@ -102,16 +105,38 @@ o/$(MODE)/test/libc/proc/fexecve_test.com.dbg: \
$(TEST_LIBC_PROC_DEPS) \ $(TEST_LIBC_PROC_DEPS) \
o/$(MODE)/test/libc/proc/fexecve_test.o \ o/$(MODE)/test/libc/proc/fexecve_test.o \
o/$(MODE)/test/libc/proc/proc.pkg \ o/$(MODE)/test/libc/proc/proc.pkg \
o/$(MODE)/test/libc/proc/echo.elf.zip.o \
o/$(MODE)/test/libc/mem/prog/life.elf.zip.o \ o/$(MODE)/test/libc/mem/prog/life.elf.zip.o \
o/$(MODE)/test/libc/calls/life-nomod.com.zip.o \ o/$(MODE)/test/libc/calls/life-nomod.com.zip.o \
o/$(MODE)/test/libc/calls/zipread.com.zip.o \ o/$(MODE)/test/libc/proc/zipread.com.zip.o \
$(LIBC_TESTMAIN) \ $(LIBC_TESTMAIN) \
$(CRT) \ $(CRT) \
$(APE_NO_MODIFY_SELF) $(APE_NO_MODIFY_SELF)
@$(APELINK) @$(APELINK)
o/$(MODE)/test/libc/proc/zipread.com.dbg: \
$(LIBC_RUNTIME) \
o/$(MODE)/test/libc/proc/zipread.o \
o/$(MODE)/test/libc/mem/prog/life.elf.zip.o \
$(CRT) \
$(APE)
@$(APELINK)
o/$(MODE)/test/libc/proc/echo.elf: \
o/$(MODE)/tool/build/assimilate.com \
o/$(MODE)/tool/build/echo.com
@$(COMPILE) -wACP -T$@ \
build/bootstrap/cp.com \
o/$(MODE)/tool/build/echo.com \
o/$(MODE)/test/libc/proc/echo.elf
@$(COMPILE) -wAASSIMILATE -T$@ \
o/$(MODE)/tool/build/assimilate.com -bcef \
o/$(MODE)/test/libc/proc/echo.elf
o/$(MODE)/test/libc/proc/echo.elf.zip.o \
o/$(MODE)/test/libc/proc/execve_test_prog1.com.zip.o \ o/$(MODE)/test/libc/proc/execve_test_prog1.com.zip.o \
o/$(MODE)/test/libc/proc/life-pe.com.zip.o: private \ o/$(MODE)/test/libc/proc/life-pe.com.zip.o \
o/$(MODE)/test/libc/proc/zipread.com.zip.o: private \
ZIPOBJ_FLAGS += \ ZIPOBJ_FLAGS += \
-B -B

View file

@ -18,6 +18,7 @@
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/struct/rusage.h" #include "libc/calls/struct/rusage.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/calls/syscall_support-sysv.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
@ -26,6 +27,8 @@
#include "libc/intrin/kprintf.h" #include "libc/intrin/kprintf.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/s.h"
#include "libc/temp.h" #include "libc/temp.h"
#include "libc/testlib/ezbench.h" #include "libc/testlib/ezbench.h"
#include "libc/testlib/subprocess.h" #include "libc/testlib/subprocess.h"
@ -35,6 +38,9 @@ __static_yoink("zipos");
#define N 16 #define N 16
int fds[2];
char buf[8];
bool SupportsFexecve = false;
void SetUpOnce(void) { void SetUpOnce(void) {
testlib_enable_tmp_setup_teardown(); testlib_enable_tmp_setup_teardown();
} }
@ -48,6 +54,21 @@ void GenBuf(char buf[8], int x) {
} }
} }
void SetUp(void) {
char buf[8];
if (__argc == 4 && !strcmp(__argv[1], "-")) {
GenBuf(buf, atoi(__argv[2]));
ASSERT_STREQ(buf, __argv[3]);
exit(0);
}
if (IsLinux()) {
if (!__is_linux_2_6_23()) return;
// TODO check for memfd
struct stat st;
SupportsFexecve = stat("/proc/self/fd", &st) == 0 && S_ISDIR(st.st_mode);
}
}
TEST(execve, testArgPassing) { TEST(execve, testArgPassing) {
int i; int i;
char ibuf[12], buf[8]; char ibuf[12], buf[8];
@ -64,11 +85,27 @@ TEST(execve, testArgPassing) {
} }
} }
TEST(execve, elfIsUnreadable_mayBeExecuted) {
if (IsWindows() || IsXnu()) return;
testlib_extract("/zip/echo.elf", "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}));
exit(1);
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(execve, ziposELF) { TEST(execve, ziposELF) {
if (1) return; // TODO: rewrite if (IsWindows()) return;
if (IsFreebsd()) return; // TODO: fixme on freebsd if (!SupportsFexecve) {
if (IsLinux() && !__is_linux_2_6_23()) return; // TODO: fixme on old linux
if (!IsLinux() && !IsFreebsd()) {
EXPECT_SYS(ENOSYS, -1, EXPECT_SYS(ENOSYS, -1,
execve("/zip/life.elf", (char *const[]){0}, (char *const[]){0})); execve("/zip/life.elf", (char *const[]){0}, (char *const[]){0}));
return; return;
@ -80,10 +117,7 @@ TEST(execve, ziposELF) {
} }
TEST(execve, ziposAPE) { TEST(execve, ziposAPE) {
if (1) return; // TODO: rewrite if (!SupportsFexecve) {
if (IsFreebsd()) return; // TODO: fixme on freebsd
if (IsLinux() && !__is_linux_2_6_23()) return; // TODO: fixme on old linux
if (!IsLinux() && !IsFreebsd()) {
EXPECT_EQ(-1, execve("/zip/life-nomod.com", (char *const[]){0}, EXPECT_EQ(-1, execve("/zip/life-nomod.com", (char *const[]){0},
(char *const[]){0})); (char *const[]){0}));
return; return;

View file

@ -16,8 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#if 0 // TODO(G4Vi): improve reliability of fexecve() implementation
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/calls/syscall_support-sysv.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
@ -25,6 +25,7 @@
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/mfd.h" #include "libc/sysv/consts/mfd.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/s.h"
#include "libc/testlib/subprocess.h" #include "libc/testlib/subprocess.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
// clang-format off // clang-format off
@ -33,6 +34,7 @@ __static_yoink("zipos");
int fds[2]; int fds[2];
char buf[8]; char buf[8];
uint8_t elf_buf[4096];
void SetUpOnce(void) { void SetUpOnce(void) {
testlib_enable_tmp_setup_teardown(); testlib_enable_tmp_setup_teardown();
@ -41,29 +43,18 @@ void SetUpOnce(void) {
void SetUp(void) { void SetUp(void) {
if (IsFreebsd()) exit(0); // TODO: fixme on freebsd if (IsFreebsd()) exit(0); // TODO: fixme on freebsd
if (IsLinux() && !__is_linux_2_6_23()) exit(0); // TODO: fixme on old linux if (IsLinux() && !__is_linux_2_6_23()) exit(0); // TODO: fixme on old linux
} // linux fexecve relies on execve from /proc
if (IsLinux()) {
TEST(execve, elfIsUnreadable_mayBeExecuted) { struct stat st;
if (IsWindows() || IsXnu()) return; if (stat("/proc/self/fd", &st) != 0 || !S_ISDIR(st.st_mode)) {
testlib_extract("/zip/echo", "echo", 0111); exit(0);
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) { TEST(fexecve, elfIsUnreadable_mayBeExecuted) {
if (!IsLinux() && !IsFreebsd()) return; if (!IsLinux() && !IsFreebsd()) return;
testlib_extract("/zip/echo", "echo", 0111); testlib_extract("/zip/echo.elf", "echo", 0111);
ASSERT_SYS(0, 0, pipe2(fds, O_CLOEXEC)); ASSERT_SYS(0, 0, pipe2(fds, O_CLOEXEC));
SPAWN(vfork); SPAWN(vfork);
ASSERT_SYS(0, 1, dup2(4, 1)); ASSERT_SYS(0, 1, dup2(4, 1));
@ -71,7 +62,7 @@ TEST(fexecve, elfIsUnreadable_mayBeExecuted) {
if (IsFreebsd()) ASSERT_SYS(0, 1, lseek(5, 1, SEEK_SET)); if (IsFreebsd()) ASSERT_SYS(0, 1, lseek(5, 1, SEEK_SET));
ASSERT_SYS(0, 0, ASSERT_SYS(0, 0,
fexecve(5, (char *const[]){"echo", "hi", 0}, (char *const[]){0})); fexecve(5, (char *const[]){"echo", "hi", 0}, (char *const[]){0}));
notpossible; exit(1);
EXITS(0); EXITS(0);
bzero(buf, 8); bzero(buf, 8);
ASSERT_SYS(0, 0, close(4)); ASSERT_SYS(0, 0, close(4));
@ -81,23 +72,27 @@ TEST(fexecve, elfIsUnreadable_mayBeExecuted) {
} }
TEST(fexecve, memfd_create) { TEST(fexecve, memfd_create) {
if (1) return; // TODO: fixme
if (!IsLinux()) return; if (!IsLinux()) return;
SPAWN(vfork); int life_fd = open("/zip/life.elf", O_RDONLY);
#define TINY_ELF_PROGRAM "\ ASSERT_NE(-1, life_fd);
\177\105\114\106\002\001\001\000\000\000\000\000\000\000\000\000\
\002\000\076\000\001\000\000\000\170\000\100\000\000\000\000\000\
\100\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
\000\000\000\000\100\000\070\000\001\000\000\000\000\000\000\000\
\001\000\000\000\005\000\000\000\000\000\000\000\000\000\000\000\
\000\000\100\000\000\000\000\000\000\000\100\000\000\000\000\000\
\200\000\000\000\000\000\000\000\200\000\000\000\000\000\000\000\
\000\020\000\000\000\000\000\000\152\052\137\152\074\130\017\005"
int fd = memfd_create("foo", MFD_CLOEXEC); int fd = memfd_create("foo", MFD_CLOEXEC);
if (fd == -1 && errno == ENOSYS) _Exit(42); if(fd == -1) {
write(fd, TINY_ELF_PROGRAM, sizeof(TINY_ELF_PROGRAM) - 1); ASSERT_EQ(ENOSYS, errno);
return;
}
while(1) {
const ssize_t bytes_read = read(life_fd, elf_buf, sizeof(elf_buf));
if (bytes_read <= 0) {
ASSERT_LE(0, bytes_read);
break;
}
ASSERT_EQ(bytes_read, write(fd, elf_buf, bytes_read));
}
ASSERT_SYS(0, 0, close(life_fd));
SPAWN(vfork);
fexecve(fd, (char *const[]){0}, (char *const[]){0}); fexecve(fd, (char *const[]){0}, (char *const[]){0});
EXITS(42); EXITS(42);
ASSERT_SYS(0, 0, close(fd));
} }
TEST(fexecve, APE) { TEST(fexecve, APE) {
@ -141,16 +136,11 @@ TEST(fexecve, ziposAPE) {
} }
TEST(fexecve, ziposAPEHasZipos) { TEST(fexecve, ziposAPEHasZipos) {
if (1) return; // TODO: fixme
if (!IsLinux() && !IsFreebsd()) return; if (!IsLinux() && !IsFreebsd()) return;
int fd = open("/zip/zipread.com", O_RDONLY); int fd = open("/zip/zipread.com", O_RDONLY);
ASSERT_NE(-1, fd); ASSERT_NE(-1, fd);
SPAWN(fork); SPAWN(fork);
ASSERT_NE(-1, fd);
if (fd == -1 && errno == ENOSYS) _Exit(42);
fexecve(fd, (char *const[]){0}, (char *const[]){0}); fexecve(fd, (char *const[]){0}, (char *const[]){0});
EXITS(42); EXITS(42);
close(fd); close(fd);
} }
#endif