diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc --- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -10375,6 +10375,25 @@ #define INIT_HEXDUMP #endif +#if SANITIZER_INTERCEPT_ARGP_PARSE +INTERCEPTOR(int, argp_parse, const struct argp *argp, int argc, char **argv, + unsigned flags, int *arg_index, void *input) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, argp_parse, argp, argc, argv, flags, arg_index, + input); + for (int i = 0; i < argc; i++) + COMMON_INTERCEPTOR_READ_RANGE(ctx, argv[i], internal_strlen(argv[i]) + 1); + int res = REAL(argp_parse)(argp, argc, argv, flags, arg_index, input); + if (!res && arg_index) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, arg_index, sizeof(int)); + return res; +} + +#define INIT_ARGP_PARSE COMMON_INTERCEPT_FUNCTION(argp_parse); +#else +#define INIT_ARGP_PARSE +#endif + #include "sanitizer_common_interceptors_netbsd_compat.inc" static void InitializeCommonInterceptors() { @@ -10694,6 +10713,7 @@ INIT_UNAME; INIT___XUNAME; INIT_HEXDUMP; + INIT_ARGP_PARSE; INIT___PRINTF_CHK; } diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h --- a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -592,6 +592,7 @@ #define SANITIZER_INTERCEPT_FLOPEN SI_FREEBSD #define SANITIZER_INTERCEPT_PROCCTL SI_FREEBSD #define SANITIZER_INTERCEPT_HEXDUMP SI_FREEBSD +#define SANITIZER_INTERCEPT_ARGP_PARSE SI_GLIBC // This macro gives a way for downstream users to override the above // interceptor macros irrespective of the platform they are on. They have diff --git a/compiler-rt/test/sanitizer_common/TestCases/Linux/argp_parse.c b/compiler-rt/test/sanitizer_common/TestCases/Linux/argp_parse.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/sanitizer_common/TestCases/Linux/argp_parse.c @@ -0,0 +1,60 @@ +// RUN: %clang %s -o %t && %run %t -o baz + +// argp_parse is glibc specific. +// UNSUPPORTED: android, target={{.*(freebsd|netbsd).*}} + +#include +#include +#include +#include + +struct test { + const char *option_value; +}; + +static const struct argp_option options[] = { + {"option", 'o', "OPTION", 0, "Option", 0}, + {NULL, 0, NULL, 0, NULL, 0}, +}; + +static error_t parser(int key, char *arg, struct argp_state *state) { + if (key == 'o') { + ((struct test *)(state->input))->option_value = arg; + return 0; + } + return ARGP_ERR_UNKNOWN; +} + +static struct argp argp = {.options = options, .parser = parser}; + +void test_nulls(char *argv0) { + char *argv[] = {argv0, NULL}; + int res = argp_parse(NULL, 1, argv, 0, NULL, NULL); + assert(res == 0); +} + +void test_synthetic(char *argv0) { + char *argv[] = {argv0, "-o", "foo", "bar", NULL}; + struct test t = {NULL}; + int arg_index; + int res = argp_parse(&argp, 4, argv, 0, &arg_index, &t); + assert(res == 0); + assert(arg_index == 3); + assert(strcmp(t.option_value, "foo") == 0); +} + +void test_real(int argc, char **argv) { + struct test t = {NULL}; + int arg_index; + int res = argp_parse(&argp, argc, argv, 0, &arg_index, &t); + assert(res == 0); + assert(arg_index == 3); + assert(strcmp(t.option_value, "baz") == 0); +} + +int main(int argc, char **argv) { + test_nulls(argv[0]); + test_synthetic(argv[0]); + test_real(argc, argv); + return EXIT_SUCCESS; +}