From 74147bf551bc1753443bb5f278909bbeef5a8de3 Mon Sep 17 00:00:00 2001 From: Fredrik Fornwall Date: Sun, 14 Jul 2024 19:15:25 +0200 Subject: [PATCH] Hook system(3) to execute $PREFIX/bin/sh, not /system/bin/sh --- .gitignore | 1 + Makefile | 11 ++-- src/system.c | 112 +++++++++++++++++++++++++++++++++ tests/fexecve.c | 19 +++--- tests/system-uname.c | 6 ++ tests/system-uname.sh | 3 + tests/system-uname.sh-expected | 1 + 7 files changed, 141 insertions(+), 12 deletions(-) create mode 100644 src/system.c create mode 100644 tests/system-uname.c create mode 100755 tests/system-uname.sh create mode 100644 tests/system-uname.sh-expected diff --git a/.gitignore b/.gitignore index 72b6172..fe269f4 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ *.deb test-binary tests/fexecve +tests/system-uname diff --git a/Makefile b/Makefile index 901f06a..249a3c7 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ CC ?= clang TERMUX_BASE_DIR ?= /data/data/com.termux/files CFLAGS += -Wall -Wextra -Werror -Wshadow -fvisibility=hidden -std=c17 -Wno-error=tautological-pointer-compare -C_SOURCE := src/termux-exec.c src/exec-variants.c -CLANG_FORMAT := clang-format --sort-includes --style="{ColumnLimit: 120}" $(C_SOURCE) +C_SOURCE := src/termux-exec.c src/exec-variants.c src/system.c +CLANG_FORMAT := clang-format --sort-includes --style="{ColumnLimit: 120}" $(C_SOURCE) tests/fexecve.c tests/system-uname.c tests/print-argv0.c CLANG_TIDY ?= clang-tidy ifeq ($(SANITIZE),1) @@ -17,6 +17,9 @@ libtermux-exec.so: $(C_SOURCE) tests/fexecve: tests/fexecve.c $(CC) $(CFLAGS) -DTERMUX_BASE_DIR=\"$(TERMUX_BASE_DIR)\" $< -o $@ +tests/system-uname: tests/system-uname.c + $(CC) $(CFLAGS) -DTERMUX_BASE_DIR=\"$(TERMUX_BASE_DIR)\" $< -o $@ + $(TERMUX_BASE_DIR)/usr/bin/termux-exec-test-print-argv0: tests/print-argv0.c $(CC) $(CFLAGS) $< -o $@ @@ -31,9 +34,9 @@ uninstall: on-device-tests: make clean - ASAN_OPTIONS=symbolize=0,detect_leaks=0 make SANITIZE=1 on-device-tests-internal + ASAN_OPTIONS=symbolize=0,detect_leaks=0 make on-device-tests-internal -on-device-tests-internal: libtermux-exec.so tests/fexecve $(TERMUX_BASE_DIR)/usr/bin/termux-exec-test-print-argv0 +on-device-tests-internal: libtermux-exec.so tests/fexecve tests/system-uname $(TERMUX_BASE_DIR)/usr/bin/termux-exec-test-print-argv0 @LD_PRELOAD=${CURDIR}/libtermux-exec.so ./run-tests.sh format: diff --git a/src/system.c b/src/system.c new file mode 100644 index 0000000..570e1c4 --- /dev/null +++ b/src/system.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include + +#ifndef TERMUX_BASE_DIR +#define TERMUX_BASE_DIR "/data/data/com.termux/files" +#endif + +// From libc/malloc_debug/malloc_debug.cpp: +// Use this because the sigprocmask* functions filter out the reserved bionic +// signals including the signal this code blocks. +// From libc/private/ScopedSignalBlocker.h: +// This code needs to really block all the signals, not just the user-visible +// ones. We call __rt_sigprocmask(2) directly so we don't mask out our own +// signals (https://issuetracker.google.com/153624226 was a pthread_exit(3) +// crash because a request to dump the thread's stack came in as it was exiting). +static inline int __rt_sigprocmask(int how, const sigset64_t *new_set, sigset64_t *old_set, size_t sigset_size) { + return syscall(__NR_rt_sigprocmask, how, new_set, old_set, sigset_size); +} + +#define _SYSTEM_CLEANUP \ + __rt_sigprocmask(SIG_SETMASK, &sigchld_blocker_old_set, NULL, sizeof(sigset64_t)); \ + sigaction64(SIGINT, &sigint_ignorer_old_action, NULL); \ + sigaction64(SIGQUIT, &sigquit_ignorer_old_action, NULL) + +__attribute__((visibility("default"))) int system(const char *command) { + // "The system() function shall always return non-zero when command is NULL." + // http://pubs.opengroup.org/onlinepubs/9699919799/functions/system.html + if (command == NULL) + return 1; + + sigset64_t sigchld_blocker_set = {}; + sigset64_t sigchld_blocker_old_set = {}; + sigaddset64(&sigchld_blocker_set, SIGCHLD); + __rt_sigprocmask(SIG_BLOCK, &sigchld_blocker_set, &sigchld_blocker_old_set, sizeof(sigset64_t)); + + struct sigaction64 sigint_ignorer_action = {.sa_flags = 0, .sa_handler = SIG_IGN}; + struct sigaction64 sigint_ignorer_old_action = {}; + sigaction64(SIGINT, &sigint_ignorer_action, &sigint_ignorer_old_action); + + struct sigaction64 sigquit_ignorer_action = {.sa_flags = 0, .sa_handler = SIG_IGN}; + struct sigaction64 sigquit_ignorer_old_action = {}; + sigaction64(SIGQUIT, &sigquit_ignorer_action, &sigquit_ignorer_old_action); + + sigset64_t default_mask = {}; + if (sigint_ignorer_old_action.sa_handler != SIG_IGN) + sigaddset64(&default_mask, SIGINT); + if (sigquit_ignorer_old_action.sa_handler != SIG_IGN) + sigaddset64(&default_mask, SIGQUIT); + const int flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; + + posix_spawnattr_t attributes; + if ((errno = posix_spawnattr_init(&attributes))) { + _SYSTEM_CLEANUP; + return -1; + } + if ((errno = posix_spawnattr_setsigdefault64(&attributes, &default_mask))) { + _SYSTEM_CLEANUP; + return -1; + } + if ((errno = posix_spawnattr_setsigmask64(&attributes, &sigchld_blocker_old_set))) { + _SYSTEM_CLEANUP; + return -1; + } + if ((errno = posix_spawnattr_setflags(&attributes, flags))) { + _SYSTEM_CLEANUP; + return -1; + } + + const char *argv[] = {"sh", "-c", "--", command, NULL}; + pid_t child; + if ((errno = posix_spawn(&child, TERMUX_BASE_DIR "/usr/bin/sh", NULL, &attributes, (char **)(argv), environ)) != 0) { + _SYSTEM_CLEANUP; + return -1; + } + posix_spawnattr_destroy(&attributes); + + int status; + pid_t pid = TEMP_FAILURE_RETRY(waitpid(child, &status, 0)); + _SYSTEM_CLEANUP; + return (pid == -1 ? -1 : status); +} diff --git a/tests/fexecve.c b/tests/fexecve.c index 64ffc31..bbc682c 100644 --- a/tests/fexecve.c +++ b/tests/fexecve.c @@ -3,12 +3,15 @@ #include int main() { - const char* path_to_echo = TERMUX_BASE_DIR "/usr/bin/sh"; - int fd = open(path_to_echo, O_RDONLY); - if (fd < 0) perror("open"); - char* args[] = {"sh", "-c", "echo hello fexecve", NULL}; - char* env[] = {NULL}; - fexecve(fd, args, env); - perror("fexecve"); - return 0; + const char *path_to_echo = TERMUX_BASE_DIR "/usr/bin/sh"; + int fd = open(path_to_echo, O_RDONLY); + if (fd < 0) { + perror("open"); + return 1; + } + char *args[] = {"sh", "-c", "echo hello fexecve", NULL}; + char *env[] = {NULL}; + fexecve(fd, args, env); + perror("fexecve"); + return 0; } diff --git a/tests/system-uname.c b/tests/system-uname.c new file mode 100644 index 0000000..a220ac6 --- /dev/null +++ b/tests/system-uname.c @@ -0,0 +1,6 @@ +#include + +int main() { + system("uname"); + return 0; +} diff --git a/tests/system-uname.sh b/tests/system-uname.sh new file mode 100755 index 0000000..0d50a11 --- /dev/null +++ b/tests/system-uname.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +./tests/system-uname diff --git a/tests/system-uname.sh-expected b/tests/system-uname.sh-expected new file mode 100644 index 0000000..9b07567 --- /dev/null +++ b/tests/system-uname.sh-expected @@ -0,0 +1 @@ +Linux