Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ on:
push:
branches:
- '*'
pull_request:

jobs:
check:
Expand All @@ -15,7 +14,7 @@ jobs:
- uses: nttld/setup-ndk@v1
id: setup-ndk
with:
ndk-version: r26d
ndk-version: r28c
link-to-sdk: true
- run: brew install clang-format
- run: make CC="${ANDROID_NDK_HOME}"/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang
Expand All @@ -24,7 +23,13 @@ jobs:
- run: make check CLANG_TIDY="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/clang-tidy --extra-arg=--target=aarch64-linux-android29"
env:
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}

host-build-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: make unit-test CC=clang HOST_BUILD=1
- run: make on-normal-linux-tests CC=clang HOST_BUILD=1

actionlint:
runs-on: ubuntu-latest
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
*.swp
*.deb
test-binary
tests/execl
tests/exec-directory
tests/fexecve
tests/popen
tests/system-uname
Expand Down
18 changes: 15 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
CC ?= clang
TERMUX_BASE_DIR ?= /data/data/com.termux/files
CFLAGS += -Wall -Wextra -Werror -Wshadow -fvisibility=hidden -std=c17
CFLAGS += -Wall -Wextra -Werror -Wshadow -fvisibility=hidden -std=c23 -D__USE_GNU
C_SOURCE := src/termux-exec.c src/exec-variants.c src/termux-readlink.c
CLANG_FORMAT := clang-format --sort-includes --style="{ColumnLimit: 120}" $(C_SOURCE) tests/fexecve.c tests/system-uname.c tests/print-argv0.c tests/popen.c
CLANG_TIDY ?= clang-tidy
TEST_BINARIES = tests/execl tests/exec-directory tests/fexecve tests/popen tests/system-uname tests/readlink-proc-self-exe

ifeq ($(SANITIZE),1)
CFLAGS += -O1 -g -fsanitize=address -fno-omit-frame-pointer
Expand All @@ -13,11 +14,19 @@ endif

ifeq ($(HOST_BUILD),1)
CFLAGS += -Wno-error=tautological-pointer-compare
else
TEST_BINARIES += $(TERMUX_BASE_DIR)/usr/bin/termux-exec-test-print-argv0
endif

libtermux-exec.so: $(C_SOURCE)
$(CC) $(CFLAGS) $(LDFLAGS) $(C_SOURCE) -DTERMUX_PREFIX=\"$(TERMUX_PREFIX)\" -DTERMUX_BASE_DIR=\"$(TERMUX_BASE_DIR)\" -shared -fPIC -o libtermux-exec.so

tests/execl: tests/execl.c
$(CC) $(CFLAGS) -DTERMUX_BASE_DIR=\"$(TERMUX_BASE_DIR)\" $< -o $@

tests/exec-directory: tests/exec-directory.c
$(CC) $(CFLAGS) -DTERMUX_BASE_DIR=\"$(TERMUX_BASE_DIR)\" $< -o $@

tests/fexecve: tests/fexecve.c
$(CC) $(CFLAGS) -DTERMUX_BASE_DIR=\"$(TERMUX_BASE_DIR)\" $< -o $@

Expand All @@ -34,7 +43,7 @@ $(TERMUX_BASE_DIR)/usr/bin/termux-exec-test-print-argv0: tests/print-argv0.c
$(CC) $(CFLAGS) $< -o $@

clean:
rm -f libtermux-exec.so tests/*-actual test-binary $(TERMUX_BASE_DIR)/usr/bin/termux-exec-test-print-argv0
rm -f libtermux-exec.so tests/*-actual $(TEST_BINARIES)

install: libtermux-exec.so
install libtermux-exec.so $(DESTDIR)$(PREFIX)/lib/libtermux-exec.so
Expand All @@ -46,9 +55,12 @@ on-device-tests:
make clean
ASAN_OPTIONS=symbolize=0,detect_leaks=0 make on-device-tests-internal

on-device-tests-internal: libtermux-exec.so tests/fexecve tests/popen tests/system-uname tests/readlink-proc-self-exe $(TERMUX_BASE_DIR)/usr/bin/termux-exec-test-print-argv0
on-device-tests-internal: libtermux-exec.so $(TEST_BINARIES)
@LD_PRELOAD=${CURDIR}/libtermux-exec.so ./run-tests.sh

on-normal-linux-tests: $(TEST_BINARIES)
./run-tests.sh

format:
$(CLANG_FORMAT) -i $(C_SOURCE) tests/*.c

Expand Down
21 changes: 19 additions & 2 deletions run-tests.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
#!/data/data/com.termux/files/usr/bin/bash
#!/bin/bash

set -u

UNAME_OS=$(uname -o)
EXIT_CODE=0

for f in tests/*.sh; do
if [ "$UNAME_OS" != Android ]; then
if [ "$f" = tests/call-system-bin-sh.sh ] || [ "$f" = tests/print-argv0.sh ] || [ "$f" = tests/fexecve.sh ] ; then
echo "Skipping $f..."
continue
fi
fi

printf "Running $f..."

EXPECTED_FILE=$f-expected
Expand All @@ -14,7 +24,14 @@ for f in tests/*.sh; do
if cmp --silent $ACTUAL_FILE $EXPECTED_FILE; then
printf " OK\n"
else
printf " FAILED - compare expected $EXPECTED_FILE with ${ACTUAL_FILE}\n"
printf " FAILED - compare expected ${EXPECTED_FILE} with ${ACTUAL_FILE}\n"
echo "### Expected:"
cat "$EXPECTED_FILE"
echo "### Actual:"
cat "$ACTUAL_FILE"
echo "###"
EXIT_CODE=1
fi
done

exit $EXIT_CODE
25 changes: 20 additions & 5 deletions src/termux-exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <unistd.h>

Expand Down Expand Up @@ -284,9 +285,19 @@ __attribute__((visibility("default"))) int execve(const char *executable_path, c
fprintf(stderr, LOG_PREFIX "Rewritten path: '%s'\n", executable_path);
}

char normalized_path_buffer[PATH_MAX];
executable_path = normalize_path(executable_path, normalized_path_buffer);

if (access(executable_path, X_OK) != 0) {
// Error out if the file is not executable:
errno = EACCES;
// Error out if the file is not executable.
struct stat stat_buffer;
if (stat(executable_path, &stat_buffer) == 0) {
// File exists but is executable:
errno = EACCES;
} else {
// File does not exist:
errno = ENOENT;
}
return -1;
}

Expand Down Expand Up @@ -315,6 +326,13 @@ __attribute__((visibility("default"))) int execve(const char *executable_path, c
// We use one more byte since inspect_file_header() will null terminate the buffer.
char header[256];
ssize_t read_bytes = read(fd, header, sizeof(header) - 1);
if (read_bytes < 0) {
if (errno == EISDIR) {
// execve() should error with EACCES if file is directory.
errno = EACCES;
}
return -1;
}
close(fd);

struct file_header_info info = {
Expand All @@ -338,9 +356,6 @@ __attribute__((visibility("default"))) int execve(const char *executable_path, c
}
}

char normalized_path_buffer[PATH_MAX];
executable_path = normalize_path(executable_path, normalized_path_buffer);

char **new_allocated_envp = NULL;
char *termux_self_exe = NULL;

Expand Down
11 changes: 11 additions & 0 deletions tests/exec-directory.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <errno.h>
#include <stdio.h>
#include <unistd.h>

int main(int, char **argv, char **env) {
execve(".", argv, env);
printf("errno = %d\n", errno);
execve("/", argv, env);
printf("errno = %d\n", errno);
return 0;
}
4 changes: 4 additions & 0 deletions tests/exec-directory.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
set -e -u

./tests/exec-directory
2 changes: 2 additions & 0 deletions tests/exec-directory.sh-expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
errno = 13
errno = 13
22 changes: 22 additions & 0 deletions tests/execl.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <errno.h>
#include <stdio.h>
#include <unistd.h>

void exec_debug(char *path) {
int ret = execl(path, "--arg", (char *)NULL);
if (ret != -1) {
fprintf(stderr, "Unexpected return value when execl():ing non-existing file: %d\n", ret);
} else {
printf("errno = %d\n", errno);
}
}

int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "Unexpected argc=%d\n", argc);
return 1;
}
exec_debug("/this-file-does-not-exist");
exec_debug(argv[1]);
return 0;
}
10 changes: 10 additions & 0 deletions tests/execl.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/sh
set -e -u

# Make TMPFILE an existing but non-executable file:
TMPFILE=$(mktemp)
echo "1" > $TMPFILE

./tests/execl $TMPFILE

rm $TMPFILE
2 changes: 2 additions & 0 deletions tests/execl.sh-expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
errno = 2
errno = 13
1 change: 1 addition & 0 deletions tests/fexecve.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <fcntl.h>
#include <stdio.h>
#define __USE_XOPEN2K8
#include <unistd.h>

int main() {
Expand Down
2 changes: 1 addition & 1 deletion tests/not-executable.sh-expected
Original file line number Diff line number Diff line change
@@ -1 +1 @@
./run-tests.sh: line 12: tests/not-executable.sh: Permission denied
./run-tests.sh: line 22: tests/not-executable.sh: Permission denied
1 change: 1 addition & 0 deletions tests/popen.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#define _POSIX_C_SOURCE 2
#include <stdio.h>

int main() {
Expand Down
2 changes: 2 additions & 0 deletions tests/readlink-proc-self-exe.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#define _DEFAULT_SOURCE
#define __USE_XOPEN2K8
#include <fcntl.h>
#include <linux/limits.h>
#include <stdio.h>
#include <string.h>
Expand Down