Add APE fexecve() support (#733)

This commit is contained in:
Gavin Hayes 2023-02-22 21:58:23 -05:00 committed by GitHub
parent b275e664ec
commit ff9c15f48a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 191 additions and 29 deletions

View file

@ -19,6 +19,7 @@
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/blocksigs.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/execve-sysv.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
@ -37,6 +38,11 @@ static bool CanExecute(const char *path) {
return !sys_faccessat(AT_FDCWD, path, X_OK, 0);
}
bool IsAPEMagic(char buf[8]) {
return READ64LE(buf) == READ64LE("MZqFpD='") ||
READ64LE(buf) == READ64LE("JTqFpD='");
}
static bool IsApeBinary(const char *path) {
int fd;
char buf[8];
@ -44,11 +50,7 @@ static bool IsApeBinary(const char *path) {
// TODO(jart): Should we block signals too?
BLOCK_CANCELLATIONS;
if ((fd = sys_open(path, O_RDONLY, 0)) != -1) {
if (sys_read(fd, buf, 8) == 8 && //
(READ64LE(buf) == READ64LE("MZqFpD='") ||
READ64LE(buf) == READ64LE("JTqFpD='"))) {
res = true;
}
res = sys_read(fd, buf, 8) == 8 && IsAPEMagic(buf);
sys_close(fd);
}
ALLOW_CANCELLATIONS;

View file

@ -0,0 +1,10 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_EXECVE_SYSV_H_
#define COSMOPOLITAN_LIBC_CALLS_EXECVE_SYSV_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
bool IsAPEMagic(char[8]) _Hide;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_EXECVE_SYSV_H_ */

View file

@ -16,21 +16,147 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/blocksigs.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/execve-sysv.internal.h"
#include "libc/calls/struct/stat.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/mfd.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/shm.h"
#include "libc/sysv/errfuns.h"
static bool IsAPEFd(const int fd) {
char buf[8];
bool res;
return (sys_pread(fd, buf, 8, 0, 0) == 8) && IsAPEMagic(buf);
}
static int fexecve_impl(const int fd, char *const argv[], char *const envp[]) {
int rc;
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();
}
return rc;
}
typedef enum {
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) {
static const char printftok[] = "printf '";
static const size_t printftoklen = sizeof(printftok) - 1;
const char *tok = memmem(ape, apesize, printftok, printftoklen);
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;
}
static int ape_fd_to_mem_elf_fd(const int infd, char *path) {
if (!IsLinux() && !IsFreebsd() || !_weaken(mmap) || !_weaken(munmap)) {
return enosys();
}
struct stat st;
if (sys_fstat(infd, &st) == -1) {
return -1;
}
int fd;
if (IsLinux()) {
fd = sys_memfd_create(__func__, MFD_CLOEXEC);
if ((fd != -1) && path) {
FormatInt32(stpcpy(path, "/proc/self/fd/"), fd);
}
} else if (IsFreebsd()) {
fd = sys_shm_open(SHM_ANON, O_CREAT | O_RDWR, 0);
} else {
return enosys();
}
if (fd == -1) {
return -1;
}
void *space;
if ((sys_ftruncate(fd, st.st_size, st.st_size) != -1) &&
((space = _weaken(mmap)(0, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED,
fd, 0)) != MAP_FAILED)) {
ssize_t readRc;
readRc = sys_pread(infd, space, st.st_size, 0, 0);
bool success = readRc != -1;
if (success && (st.st_size > 8) && IsAPEMagic(space)) {
success = ape_to_elf(space, st.st_size);
}
const int e = errno;
if ((_weaken(munmap)(space, st.st_size) != -1) && success) {
_unassert(readRc == st.st_size);
return fd;
} else if (!success) {
errno = e;
}
}
const int e = errno;
close(fd);
errno = e;
return -1;
}
/**
* Executes binary executable at file descriptor.
*
* This is only supported on Linux and FreeBSD. APE binaries currently
* aren't supported.
* This is only supported on Linux and FreeBSD. APE binaries are
* supported.
*
* @param fd is opened executable and current file position is ignored
* @return doesn't return on success, otherwise -1 w/ errno
@ -39,7 +165,6 @@
*/
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)))) {
@ -47,14 +172,21 @@ int fexecve(int fd, char *const argv[], char *const envp[]) {
} else {
STRACE("fexecve(%d, %s, %s) → ...", fd, DescribeStringList(argv),
DescribeStringList(envp));
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();
rc = fexecve_impl(fd, argv, envp);
if ((errno == ENOEXEC) && (IsLinux() || IsFreebsd())) {
int newfd = -1;
BEGIN_CANCELLATION_POINT;
BLOCK_SIGNALS;
strace_enabled(-1);
if (IsAPEFd(fd)) {
newfd = ape_fd_to_mem_elf_fd(fd, NULL);
}
strace_enabled(+1);
ALLOW_SIGNALS;
END_CANCELLATION_POINT;
if (newfd != -1) {
rc = fexecve_impl(newfd, argv, envp);
}
}
}
STRACE("fexecve(%d) failed %d% m", fd, rc);

View file

@ -105,6 +105,7 @@ i32 sys_setresuid(u32, u32, u32) _Hide;
i32 sys_setreuid(u32, u32) _Hide;
i32 sys_setsid(void) _Hide;
i32 sys_setuid(i32) _Hide;
i32 sys_shm_open(const char *, i32, u32) _Hide;
i32 sys_sigaction(i32, const void *, void *, i64, i64) _Hide;
i32 sys_sigaltstack(const void *, void *) _Hide;
i32 sys_symlinkat(const char *, i32, const char *) _Hide;

View file

@ -1620,6 +1620,7 @@ syscon shm SHM_HUGETLB 0x0800 0 0 0 0 0
syscon shm SHM_LOCKED 0x0400 0 0 0 0 0
syscon shm SHM_NORESERVE 0x1000 0 0 0 0 0
syscon shm SHM_REMAP 0x4000 0 0 0 0 0
syscon shm SHM_ANON 0 0 1 0 0 0
syscon lock LOCK_UNLOCK_CACHE 54 0 0 0 0 0 # wut

View file

@ -0,0 +1,2 @@
.include "o/libc/sysv/consts/syscon.internal.inc"
.syscon shm,SHM_ANON,0,0,1,0,0,0

View file

@ -2,24 +2,26 @@
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_SHM_H_
#include "libc/runtime/symbolic.h"
#define SHM_DEST SYMBOLIC(SHM_DEST)
#define SHM_EXEC SYMBOLIC(SHM_EXEC)
#define SHM_HUGETLB SYMBOLIC(SHM_HUGETLB)
#define SHM_INFO SYMBOLIC(SHM_INFO)
#define SHM_LOCK SYMBOLIC(SHM_LOCK)
#define SHM_LOCKED SYMBOLIC(SHM_LOCKED)
#define SHM_ANON SYMBOLIC(SHM_ANON)
#define SHM_DEST SYMBOLIC(SHM_DEST)
#define SHM_EXEC SYMBOLIC(SHM_EXEC)
#define SHM_HUGETLB SYMBOLIC(SHM_HUGETLB)
#define SHM_INFO SYMBOLIC(SHM_INFO)
#define SHM_LOCK SYMBOLIC(SHM_LOCK)
#define SHM_LOCKED SYMBOLIC(SHM_LOCKED)
#define SHM_NORESERVE SYMBOLIC(SHM_NORESERVE)
#define SHM_R SYMBOLIC(SHM_R)
#define SHM_RDONLY SYMBOLIC(SHM_RDONLY)
#define SHM_REMAP SYMBOLIC(SHM_REMAP)
#define SHM_RND SYMBOLIC(SHM_RND)
#define SHM_STAT SYMBOLIC(SHM_STAT)
#define SHM_UNLOCK SYMBOLIC(SHM_UNLOCK)
#define SHM_W SYMBOLIC(SHM_W)
#define SHM_R SYMBOLIC(SHM_R)
#define SHM_RDONLY SYMBOLIC(SHM_RDONLY)
#define SHM_REMAP SYMBOLIC(SHM_REMAP)
#define SHM_RND SYMBOLIC(SHM_RND)
#define SHM_STAT SYMBOLIC(SHM_STAT)
#define SHM_UNLOCK SYMBOLIC(SHM_UNLOCK)
#define SHM_W SYMBOLIC(SHM_W)
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern const char *SHM_ANON;
extern const int SHM_DEST;
extern const int SHM_EXEC;
extern const int SHM_HUGETLB;

View file

@ -86,3 +86,14 @@ TEST(fexecve, memfd_create) {
fexecve(fd, (char *const[]){0}, (char *const[]){0});
EXITS(42);
}
TEST(fexecve, APE) {
if (!IsLinux() && !IsFreebsd()) return;
testlib_extract("/zip/life-nomod.com", "life-nomod.com", 0555);
SPAWN(fork);
int fd = open("life-nomod.com", O_RDONLY);
ASSERT_NE(-1, fd);
if (fd == -1 && errno == ENOSYS) _Exit(42);
fexecve(fd, (char *const[]){0}, (char *const[]){0});
EXITS(42);
}

View file

@ -93,6 +93,7 @@ o/$(MODE)/test/libc/calls/fexecve_test.com.dbg: \
o/$(MODE)/test/libc/calls/fexecve_test.o \
o/$(MODE)/test/libc/calls/calls.pkg \
o/$(MODE)/tool/build/echo.zip.o \
o/$(MODE)/test/libc/calls/life-nomod.com.zip.o \
$(LIBC_TESTMAIN) \
$(CRT) \
$(APE_NO_MODIFY_SELF)