diff --git a/linux/test/meson.build b/linux/test/meson.build index e3c6d43..54f8e1c 100644 --- a/linux/test/meson.build +++ b/linux/test/meson.build @@ -38,11 +38,18 @@ test_reload = executable( dependencies: [lfi_linux], ) +test_argv_mismatch = executable( + 'test_argv_mismatch.elf', + 'test_argv_mismatch.c', + dependencies: [lfi_linux], +) + testdata = [ {'bin': test_run, 'file': 'programs/empty.c', 'inputs': [], 'cflags': []}, {'bin': test_run, 'file': 'programs/hello.c', 'inputs': [], 'cflags': []}, {'bin': test_run, 'file': 'programs/alloc.c', 'inputs': [], 'cflags': []}, {'bin': test_run, 'file': 'programs/argv.c', 'inputs': ['one', 'two', 'three'], 'cflags': []}, + {'bin': test_argv_mismatch, 'file': 'programs/argv.c', 'inputs': ['one', 'two', 'three'], 'cflags': [], 'suffix': '_mismatch'}, {'bin': test_run, 'file': 'programs/envp.c', 'inputs': [], 'cflags': []}, {'bin': test_run, 'file': 'programs/mmap.c', 'inputs': [], 'cflags': []}, {'bin': test_run, 'file': 'programs/thread.c', 'inputs': [], 'cflags': []}, @@ -62,14 +69,16 @@ testdata = [ ] foreach input : testdata - out = input['file'].replace('/', '_') + '.lfi' + suffix = input.get('suffix', '') + out = input['file'].replace('/', '_') + suffix + '.lfi' exec = custom_target(out, output: out, input: input['file'], command: [lficc, '-O2', '-o', '@OUTPUT@', '@INPUT@', '-static-pie'] + input['cflags'], ) expected = input['file'] + '.out' - test(input['file'], + test_name = input['file'] + suffix + test(test_name, go, args: ['run', 'run.go', expected, input['bin'], exec] + input['inputs'], workdir: meson.current_source_dir(), suite: ['linux'] diff --git a/linux/test/test_argv_mismatch.c b/linux/test/test_argv_mismatch.c new file mode 100644 index 0000000..bda42fd --- /dev/null +++ b/linux/test/test_argv_mismatch.c @@ -0,0 +1,97 @@ +#include "lfi_linux.h" +#include "test.h" + +#include +#include +#include +#include +#include + +char * +xasprintf(const char *fmt, ...); + +struct Buf { + void *data; + size_t size; +}; + +int +main(int argc, const char **argv) +{ + struct LFIEngine *engine = lfi_new( + (struct LFIOptions) { + .boxsize = gb(4), + .pagesize = getpagesize(), + .no_verify = false, + .verbose = true, + }, + 1); + assert(engine); + + char cwd[FILENAME_MAX]; + char *d = getcwd(cwd, sizeof(cwd)); + assert(d == cwd); + + char *map = xasprintf("/=%s", cwd); + const char *maps[] = { + map, + NULL, + }; + + struct LFILinuxEngine *linux_ = lfi_linux_new(engine, + (struct LFILinuxOptions) { + .stacksize = mb(2), + .verbose = true, + .exit_unknown_syscalls = true, + .wd = "/", + .dir_maps = maps, + }); + assert(linux_); + + if (argc <= 1) { + fprintf(stderr, "no input program provided\n"); + return 1; + } + + struct LFILinuxProc *proc = lfi_proc_new(linux_); + assert(proc); + + bool ok = lfi_proc_load_file(proc, argv[1]); + assert(ok); + + const char *envp[] = { + "LFI=1", + "USER=sandbox", + "HOME=/home", + NULL, + }; + + // Replicate the repro case: construct argv with a trailing NULL + // and pass the total array size (including the NULL) as argc. + int sandbox_nargv = argc - 1; + const char *sandbox_argv[sandbox_nargv + 1]; + for (int i = 0; i < sandbox_nargv; i++) { + sandbox_argv[i] = argv[i + 1]; + } + sandbox_argv[sandbox_nargv] = NULL; + + struct LFILinuxThread *t = lfi_thread_new(proc, + sizeof(sandbox_argv) / sizeof(sandbox_argv[0]), &sandbox_argv[0], + &envp[0]); + assert(t); + + int result = lfi_thread_run(t); + assert(result == 0); + + lfi_thread_free(t); + + lfi_proc_free(proc); + + lfi_linux_free(linux_); + + lfi_free(engine); + + free(map); + + return 0; +} diff --git a/linux/thread.c b/linux/thread.c index e0166fa..771874c 100644 --- a/linux/thread.c +++ b/linux/thread.c @@ -170,7 +170,7 @@ stack_init(struct LFILinuxThread *t, int argc, const char **argv, (struct Auxv) { LINUX_AT_NULL, 0 }, }; - uint64_t box_argc = argc; + uint64_t box_argc = nargv; // Calculate where the sp should go (enough space from rand_start to store // argc/argv/envp/auxv). lfiptr stack_start = rand_start - sizeof(box_argc) - sizeof(box_argv) -