[compiler-rt] cc6b86e - [sanitizer] Intercept glibc's argp_parse()

Ilya Leoshkevich via llvm-commits llvm-commits at lists.llvm.org
Wed Mar 8 07:10:16 PST 2023


Author: Ilya Leoshkevich
Date: 2023-03-08T16:08:19+01:00
New Revision: cc6b86e175da1cf4fa7de98ee99bb9e55e16ac7d

URL: https://github.com/llvm/llvm-project/commit/cc6b86e175da1cf4fa7de98ee99bb9e55e16ac7d
DIFF: https://github.com/llvm/llvm-project/commit/cc6b86e175da1cf4fa7de98ee99bb9e55e16ac7d.diff

LOG: [sanitizer] Intercept glibc's argp_parse()

Glibc provides the argp_parse() function for parsing command line
arguments [1].

Indicate that argc/argv are read from and arg_index is written to.
Strictly speaking, we also need to indicate that argp is read from,
but this would require describing its layout, and most people use a
static initializer there, so it's not worth the effort.

[1] https://www.gnu.org/software/libc/manual/html_node/Argp.html

Reviewed By: vitalybuka

Differential Revision: https://reviews.llvm.org/D143330

Added: 
    compiler-rt/test/sanitizer_common/TestCases/Linux/argp_parse.c

Modified: 
    compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
    compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h

Removed: 
    


################################################################################
diff  --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
index 9fc56238a64fc..51703a34f0e9b 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -10375,6 +10375,25 @@ INTERCEPTOR(void, hexdump, const void *ptr, int length, const char *header, int
 #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 @@ static void InitializeCommonInterceptors() {
   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
index 814ff462d1cf5..eb39fabfd5983 100644
--- 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
index 0000000000000..ec78f276c9eaf
--- /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 <argp.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+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;
+}


        


More information about the llvm-commits mailing list