diff --git a/tests/src/fork_helpers.c b/tests/src/fork_helpers.c index 67b442538..92a0a328c 100644 --- a/tests/src/fork_helpers.c +++ b/tests/src/fork_helpers.c @@ -16,7 +16,9 @@ #include +#include #include +#include #include #include @@ -40,6 +42,27 @@ typedef enum { CHILD_EXIT_CODE_REPORTING_FAILED = 122, } child_exit_code_t; +static int env_contains_substring(const char *name, const char *substring) +{ + const char *value = getenv(name); + if (value == NULL) { + return 0; + } else { + return strstr(value, substring) != NULL; + } +} + +static int probably_running_under_valgrind(void) +{ + if (env_contains_substring("LD_PRELOAD", "/vgpreload_")) { + return 1; + } + if (env_contains_substring("DYLD_INSERT_LIBRARIES", "/vgpreload_")) { + return 1; + } + return 0; +} + #if defined(__GNUC__) __attribute__((__noreturn__)) #endif @@ -107,6 +130,20 @@ write_done: * to debug. Another reason is that we must not cause external effects * such as destroying a PSA persistent key.) */ + if (probably_running_under_valgrind()) { + /* Valgrind overloads _exit(), and this makes it do weird things, + * including an lseek() call to rewind the pointer on the file + * description for the `.datax` file, causing the same test cases + * to run again (or parse errors, depending on the exact amount + * of rewinding). + * + * Valgrind doesn't overload execve() and friends. So instead of + * _exit(), execute a shell command that returns the same status. + */ + char cmd[20]; + snprintf(cmd, sizeof(cmd), "exit %d", child_exit_code); + execlp("sh", "sh", "-c", cmd, NULL); + } _exit(child_exit_code); }