[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