From 7f3798f276318d6b83148cad9ca61deacecb8c07 Mon Sep 17 00:00:00 2001 From: Andrew Taylor Date: Wed, 1 Nov 2023 11:46:31 +1100 Subject: [PATCH] wrap fileno() so it works --- compile_time_python/compile.py | 5 +- tests/do_tests.sh | 9 ++++ .../000000-clang-14.0-x86_64-pc-linux-gnu.txt | 0 .../000000-clang-14.0-x86_64-pc-linux-gnu.txt | 0 .../000000-clang-14.0-x86_64-pc-linux-gnu.txt | 0 .../000000-clang-14.0-x86_64-pc-linux-gnu.txt | 0 .../000000-clang-14.0-x86_64-pc-linux-gnu.txt | 0 tests/run_time_no_errors/check_exit_status.sh | 22 +++++++++ .../{no_leak_empty.c => empty_main.c} | 2 + tests/run_time_no_errors/fileno.c | 9 ++++ tests/single_test.sh | 6 +-- wrapper_c/dcc_dual_sanitizers.c | 48 +++++++++++++++++-- wrapper_c/dcc_main.c | 2 +- wrapper_c/dcc_util.c | 3 +- 14 files changed, 96 insertions(+), 10 deletions(-) rename tests/expected_output/{no_leak_empty--leak-check => check_exit_status}/000000-clang-14.0-x86_64-pc-linux-gnu.txt (100%) create mode 100644 tests/expected_output/empty_main--leak-check/000000-clang-14.0-x86_64-pc-linux-gnu.txt create mode 100644 tests/expected_output/empty_main-fsanitize=address/000000-clang-14.0-x86_64-pc-linux-gnu.txt create mode 100644 tests/expected_output/empty_main/000000-clang-14.0-x86_64-pc-linux-gnu.txt create mode 100644 tests/expected_output/fileno/000000-clang-14.0-x86_64-pc-linux-gnu.txt create mode 100755 tests/run_time_no_errors/check_exit_status.sh rename tests/run_time_no_errors/{no_leak_empty.c => empty_main.c} (50%) create mode 100644 tests/run_time_no_errors/fileno.c diff --git a/compile_time_python/compile.py b/compile_time_python/compile.py index 65e5f0a..d8b4cc4 100644 --- a/compile_time_python/compile.py +++ b/compile_time_python/compile.py @@ -379,13 +379,14 @@ def get_rename_arguments(source, options, rename_functions=True): rename_arguments += ['-Dmain=__fake_variable;extern "C" int __real_main'] else: rename_arguments += ["-Dmain=__real_main"] - rename_arguments += [f"-D{f}=__wrap_{f}" for f in override_functions] + rename_arguments += [f"-D{f}=__wrap_{f}" for f in ["fileno"] + override_functions] source = source.replace("__wrap_main", "main") + source = source.replace("__real_fileno", "fileno") for f in override_functions: source = source.replace("__real_" + f, f) else: rename_arguments += [ - "-Wl" + "".join(",-wrap," + f for f in ["main"] + override_functions) + "-Wl" + "".join(",-wrap," + f for f in ["main","fileno"] + override_functions) ] return rename_arguments, source diff --git a/tests/do_tests.sh b/tests/do_tests.sh index 79ef5c0..dd6c71e 100755 --- a/tests/do_tests.sh +++ b/tests/do_tests.sh @@ -13,6 +13,15 @@ export dcc_cpp="${dcc}++" export c_compiler="${2:-clang}" export cpp_compiler="${3:-clang++}" +command -v "$dcc" > /dev/null || { + echo "$0: error: $dcc not found" + exit 1 +} +command -v "$dcc_cpp" > /dev/null || { + echo "$0: warning: $dcc_cpp not found - no C++ tests will be run" + exit 1 +} + e="$tests_dir/extracted_compile_time_errors" mkdir -p "$e" ( diff --git a/tests/expected_output/no_leak_empty--leak-check/000000-clang-14.0-x86_64-pc-linux-gnu.txt b/tests/expected_output/check_exit_status/000000-clang-14.0-x86_64-pc-linux-gnu.txt similarity index 100% rename from tests/expected_output/no_leak_empty--leak-check/000000-clang-14.0-x86_64-pc-linux-gnu.txt rename to tests/expected_output/check_exit_status/000000-clang-14.0-x86_64-pc-linux-gnu.txt diff --git a/tests/expected_output/empty_main--leak-check/000000-clang-14.0-x86_64-pc-linux-gnu.txt b/tests/expected_output/empty_main--leak-check/000000-clang-14.0-x86_64-pc-linux-gnu.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/expected_output/empty_main-fsanitize=address/000000-clang-14.0-x86_64-pc-linux-gnu.txt b/tests/expected_output/empty_main-fsanitize=address/000000-clang-14.0-x86_64-pc-linux-gnu.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/expected_output/empty_main/000000-clang-14.0-x86_64-pc-linux-gnu.txt b/tests/expected_output/empty_main/000000-clang-14.0-x86_64-pc-linux-gnu.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/expected_output/fileno/000000-clang-14.0-x86_64-pc-linux-gnu.txt b/tests/expected_output/fileno/000000-clang-14.0-x86_64-pc-linux-gnu.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/run_time_no_errors/check_exit_status.sh b/tests/run_time_no_errors/check_exit_status.sh new file mode 100755 index 0000000..46ad410 --- /dev/null +++ b/tests/run_time_no_errors/check_exit_status.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +cat >exit_42.c < +int main(void) {exit(42);} +eof + +cat >return_42.c <&2 +done + +rm -f a.out exit_42.c return_42.c \ No newline at end of file diff --git a/tests/run_time_no_errors/no_leak_empty.c b/tests/run_time_no_errors/empty_main.c similarity index 50% rename from tests/run_time_no_errors/no_leak_empty.c rename to tests/run_time_no_errors/empty_main.c index bbf66aa..6333e5e 100644 --- a/tests/run_time_no_errors/no_leak_empty.c +++ b/tests/run_time_no_errors/empty_main.c @@ -1,3 +1,5 @@ +//dcc_flags= +//dcc_flags=-fsanitize=address //dcc_flags=--leak-check int main(void) { diff --git a/tests/run_time_no_errors/fileno.c b/tests/run_time_no_errors/fileno.c new file mode 100644 index 0000000..892af5f --- /dev/null +++ b/tests/run_time_no_errors/fileno.c @@ -0,0 +1,9 @@ +#include +#include + +int main(void) { + assert(fileno(stdin) == 0); + assert(fileno(stdout) == 1); + assert(fileno(stderr) == 2); + assert(fileno(fopen(__FILE__, "r")) == 3); +} diff --git a/tests/single_test.sh b/tests/single_test.sh index 190e004..3772db1 100755 --- a/tests/single_test.sh +++ b/tests/single_test.sh @@ -1,7 +1,6 @@ #!/usr/bin/env bash command -v "$dcc" > /dev/null || exit 1 -command -v "$dcc_cpp" > /dev/null || exit 1 tmpdir=$(mktemp -d) trap 'cd /;rm -fr "$tmpdir"' EXIT @@ -51,7 +50,7 @@ compile_options_list=${compile_options_list:-'dcc_flags=""'} for compile_options in $compile_options_list do - rm -f a.out + rm -f a.out tmp.actual_* compile_options=$(echo "$compile_options"|sed 's/#/ /g') case "$src_file" in *.c) @@ -65,6 +64,7 @@ do ;; *.cpp) + command -v "$dcc_cpp" > /dev/null || continue dcc_flags= suffix=`echo $compile_options|sed 's/^dcc_flags=//;s/ /_/g;s/["$]//g;s/src_file//;s?/?_?g'` eval $compile_options @@ -76,7 +76,7 @@ do *.sh) expected_output_basename="`basename $src_file .sh`" - $src_file tmp.actual_stderr >/dev/null + $src_file tmp.actual_stderr >tmp.actual_stdout ;; *) diff --git a/wrapper_c/dcc_dual_sanitizers.c b/wrapper_c/dcc_dual_sanitizers.c index f5aab0b..285c78c 100644 --- a/wrapper_c/dcc_dual_sanitizers.c +++ b/wrapper_c/dcc_dual_sanitizers.c @@ -15,7 +15,8 @@ static FILE *get_cookie(FILE *f, const char *mode) { } for (int i = 0; i < FOPEN_MAX; i++) { if (!file_cookies[i].stream) { - file_cookies[i].fd = fileno(f); + extern int __real_fileno(FILE *stream); + file_cookies[i].fd = __real_fileno(f); file_cookies[i].stream = f; file_cookies[i].cookie_stream = open_cookie(&file_cookies[i], mode); return file_cookies[i].cookie_stream; @@ -28,6 +29,7 @@ static FILE *get_cookie(FILE *f, const char *mode) { return f; } + static int init_check_output(void); static void init_cookies(void) { setbuf(stderr, NULL); @@ -135,6 +137,7 @@ enum which_system_call { sc_clock, sc_close, sc_fdopen, + sc_fileno, sc_fopen, sc_freopen, sc_popen, @@ -154,6 +157,7 @@ static char *system_call_names[] = { [sc_close] = "close", [sc_fopen] = "fopen", [sc_fdopen] = "fdopen", + [sc_fileno] = "fileno", [sc_freopen] = "freopen", [sc_popen] = "popen", [sc_read] = "read", @@ -570,7 +574,6 @@ int __wrap_system(const char *command) { } - static FILE *fopen_helper(FILE *f, const char *mode, enum which_system_call system_call) { #if __I_AM_SANITIZER1__ FILE *f1 = get_cookie(f, mode); @@ -649,7 +652,8 @@ FILE *__wrap_freopen(const char *pathname, const char *mode, FILE *stream) { FILE *f1 = __real_freopen(pathname, mode, file_cookies[i].stream); if (f1) { file_cookies[i].stream = f1; - file_cookies[i].fd = fileno(f1); + extern int __real_fileno(FILE *stream); + file_cookies[i].fd = __real_fileno(f1); (void)synchronize_system_call_result(sc_freopen, 1); return file_cookies[i].cookie_stream; } else { @@ -679,3 +683,41 @@ static void unlink_sanitizer2_executable(void) { } } #endif + +static int cookie_stream_to_fd(FILE *stream) { + int fd = -1; + for (int i = 0; i < FOPEN_MAX; i++) { + if (file_cookies[i].cookie_stream == stream) { + fd = file_cookies[i].fd; + break; + } + } + + // in single santizer mode cookies are used for stdin, stdout & stderr not files + if (fd == -1) { + extern int __real_fileno(FILE *stream); + fd = __real_fileno(stream); + } + return fd; +} + +#if __N_SANITIZERS__ > 1 + +#undef fileno + +int __wrap_fileno(FILE *stream) { + synchronize_system_call(sc_fileno, 0); +#if __I_AM_SANITIZER1__ + return synchronize_system_call_result(sc_fileno, cookie_stream_to_fd(stream)); +#else + return synchronize_system_call_result(sc_fileno); +#endif +} + +#else + +int __wrap_fileno(FILE *stream) { + return cookie_stream_to_fd(stream); +} + +#endif diff --git a/wrapper_c/dcc_main.c b/wrapper_c/dcc_main.c index 04a02b0..9208f55 100644 --- a/wrapper_c/dcc_main.c +++ b/wrapper_c/dcc_main.c @@ -117,7 +117,7 @@ int __wrap_main(int argc, char *argv[], char *envp[]) { clear_stack(); extern char **environ; int r = __real_main(argc, argv, environ); - debug_printf(2, "__real_main returning %d\n", r); + debug_printf(2, "__real_main exiting %d\n", r); exit(r); return 1; // not reached } diff --git a/wrapper_c/dcc_util.c b/wrapper_c/dcc_util.c index 557ce0f..3841fe8 100644 --- a/wrapper_c/dcc_util.c +++ b/wrapper_c/dcc_util.c @@ -13,7 +13,8 @@ static void launch_valgrind(int argc, char *argv[]) { sizeof tar_data / sizeof tar_data[0], valgrind_error_pipe); fflush(valgrind_error_pipe); setbuf(valgrind_error_pipe, NULL); - valgrind_error_fd = (int)fileno(valgrind_error_pipe); + extern int __real_fileno(FILE *stream); + valgrind_error_fd = (int)__real_fileno(valgrind_error_pipe); } else { debug_printf(2, "popen __MONITOR_VALGRIND__ failed"); return;