[compiler-rt] 0ce057c - [sanitizer] Support v2 and v3 capabilities

Ilya Leoshkevich via llvm-commits llvm-commits at lists.llvm.org
Fri Feb 24 05:12:27 PST 2023


Author: Ilya Leoshkevich
Date: 2023-02-24T14:12:11+01:00
New Revision: 0ce057c5925d8bcb3b3835d439b5465c01812424

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

LOG: [sanitizer] Support v2 and v3 capabilities

capget() and capset() may read or write more than one cap_user_data_t,
depending on the version field of cap_user_header_t. Currently the
code assumes it's just one, so MSan complains if an application uses
version 2 or 3, where two cap_user_data_ts are used.

Parse the header in order to determine the number of cap_user_data_ts.
Also add a test.

Reviewed By: vitalybuka

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

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

Modified: 
    compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
    compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc
    compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cpp
    compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.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 e999239549ccc..9fc56238a64fc 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -5763,8 +5763,10 @@ INTERCEPTOR(int, capget, void *hdrp, void *datap) {
   // its metadata. See
   // https://github.com/google/sanitizers/issues/321.
   int res = REAL(capget)(hdrp, datap);
-  if (res == 0 && datap)
-    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, datap, __user_cap_data_struct_sz);
+  if (res == 0 && datap) {
+    unsigned datasz = __user_cap_data_struct_sz(hdrp);
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, datap, datasz);
+  }
   // We can also return -1 and write to hdrp->version if the version passed in
   // hdrp->version is unsupported. But that's not a trivial condition to check,
   // and anyway COMMON_INTERCEPTOR_READ_RANGE protects us to some extent.
@@ -5775,8 +5777,10 @@ INTERCEPTOR(int, capset, void *hdrp, const void *datap) {
   COMMON_INTERCEPTOR_ENTER(ctx, capset, hdrp, datap);
   if (hdrp)
     COMMON_INTERCEPTOR_READ_RANGE(ctx, hdrp, __user_cap_header_struct_sz);
-  if (datap)
-    COMMON_INTERCEPTOR_READ_RANGE(ctx, datap, __user_cap_data_struct_sz);
+  if (datap) {
+    unsigned datasz = __user_cap_data_struct_sz(hdrp);
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, datap, datasz);
+  }
   return REAL(capset)(hdrp, datap);
 }
 #define INIT_CAPGET                  \

diff  --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc
index 9d7518ac9476c..93b988ba16393 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc
@@ -417,14 +417,14 @@ PRE_SYSCALL(capget)(void *header, void *dataptr) {
 POST_SYSCALL(capget)(long res, void *header, void *dataptr) {
   if (res >= 0)
     if (dataptr)
-      POST_WRITE(dataptr, __user_cap_data_struct_sz);
+      POST_WRITE(dataptr, __user_cap_data_struct_sz(header));
 }
 
 PRE_SYSCALL(capset)(void *header, const void *data) {
   if (header)
     PRE_READ(header, __user_cap_header_struct_sz);
   if (data)
-    PRE_READ(data, __user_cap_data_struct_sz);
+    PRE_READ(data, __user_cap_data_struct_sz(header));
 }
 
 POST_SYSCALL(capset)(long res, void *header, const void *data) {}

diff  --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cpp
index eb2ee78c2f457..a04eed7aa5a6e 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cpp
@@ -248,7 +248,23 @@ namespace __sanitizer {
   unsigned struct_sysinfo_sz = sizeof(struct sysinfo);
   unsigned __user_cap_header_struct_sz =
       sizeof(struct __user_cap_header_struct);
-  unsigned __user_cap_data_struct_sz = sizeof(struct __user_cap_data_struct);
+  unsigned __user_cap_data_struct_sz(void *hdrp) {
+    int u32s = 0;
+    if (hdrp) {
+      switch (((struct __user_cap_header_struct *)hdrp)->version) {
+      case _LINUX_CAPABILITY_VERSION_1:
+        u32s = _LINUX_CAPABILITY_U32S_1;
+        break;
+      case _LINUX_CAPABILITY_VERSION_2:
+        u32s = _LINUX_CAPABILITY_U32S_2;
+        break;
+      case _LINUX_CAPABILITY_VERSION_3:
+        u32s = _LINUX_CAPABILITY_U32S_3;
+        break;
+      }
+    }
+    return sizeof(struct __user_cap_data_struct) * u32s;
+  }
   unsigned struct_new_utsname_sz = sizeof(struct new_utsname);
   unsigned struct_old_utsname_sz = sizeof(struct old_utsname);
   unsigned struct_oldold_utsname_sz = sizeof(struct oldold_utsname);

diff  --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h
index fdc69b8a5fba5..66ee57be11a10 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h
@@ -135,7 +135,7 @@ struct __sanitizer_perf_event_attr {
 extern unsigned struct_epoll_event_sz;
 extern unsigned struct_sysinfo_sz;
 extern unsigned __user_cap_header_struct_sz;
-extern unsigned __user_cap_data_struct_sz;
+extern unsigned __user_cap_data_struct_sz(void *hdrp);
 extern unsigned struct_new_utsname_sz;
 extern unsigned struct_old_utsname_sz;
 extern unsigned struct_oldold_utsname_sz;

diff  --git a/compiler-rt/test/sanitizer_common/TestCases/Linux/cap.c b/compiler-rt/test/sanitizer_common/TestCases/Linux/cap.c
new file mode 100644
index 0000000000000..68d91135901d7
--- /dev/null
+++ b/compiler-rt/test/sanitizer_common/TestCases/Linux/cap.c
@@ -0,0 +1,48 @@
+// RUN: %clang %s -o %t && %run %t
+// capget() and capset() are not intercepted on Android.
+// UNSUPPORTED: android
+
+#include <assert.h>
+#include <errno.h>
+#include <linux/capability.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "sanitizer_common/sanitizer_specific.h"
+
+/* Use capget() and capset() from glibc. */
+int capget(cap_user_header_t header, cap_user_data_t data);
+int capset(cap_user_header_t header, const cap_user_data_t data);
+
+static void test(int version, int u32s) {
+  struct __user_cap_header_struct hdr = {
+      .version = version,
+      .pid = 0,
+  };
+  struct __user_cap_data_struct data[u32s];
+  if (capget(&hdr, data)) {
+    assert(errno == EINVAL);
+    /* Check that memory is not touched. */
+#if __has_feature(memory_sanitizer)
+    assert(__msan_test_shadow(data, sizeof(data)) == 0);
+#endif
+    hdr.version = version;
+    int err = capset(&hdr, data);
+    assert(errno == EINVAL);
+  } else {
+    for (int i = 0; i < u32s; i++)
+      printf("%x %x %x\n", data[i].effective, data[i].permitted,
+             data[i].inheritable);
+    int err = capset(&hdr, data);
+    assert(!err);
+  }
+}
+
+int main() {
+  test(0, 1); /* Test an incorrect version. */
+  test(_LINUX_CAPABILITY_VERSION_1, _LINUX_CAPABILITY_U32S_1);
+  test(_LINUX_CAPABILITY_VERSION_2, _LINUX_CAPABILITY_U32S_2);
+  test(_LINUX_CAPABILITY_VERSION_3, _LINUX_CAPABILITY_U32S_3);
+
+  return EXIT_SUCCESS;
+}


        


More information about the llvm-commits mailing list