Index: lib/msan/lit_tests/Linux/xattr.cc =================================================================== --- /dev/null +++ lib/msan/lit_tests/Linux/xattr.cc @@ -0,0 +1,142 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p 2>&1 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// Do not depend on libattr headers. +#ifndef ENOATTR +#define ENOATTR ENODATA +#endif + +extern "C" { +ssize_t listxattr(const char *path, char *list, size_t size); +ssize_t llistxattr(const char *path, char *list, size_t size); +ssize_t flistxattr(int fd, char *list, size_t size); +ssize_t getxattr(const char *path, const char *name, void *value, size_t size); +ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size); +ssize_t fgetxattr(int fd, const char *name, void *value, size_t size); +} + +char g_path[1024]; +int g_fd; + +// Life before closures... +ssize_t listxattr_wrapper(char *buf, size_t size) { + return listxattr(g_path, buf, size); +} + +ssize_t llistxattr_wrapper(char *buf, size_t size) { + return llistxattr(g_path, buf, size); +} + +ssize_t flistxattr_wrapper(char *buf, size_t size) { + return flistxattr(g_fd, buf, size); +} + +ssize_t getxattr_wrapper(const char *name, char *buf, size_t size) { + return getxattr(g_path, name, buf, size); +} + +ssize_t lgetxattr_wrapper(const char *name, char *buf, size_t size) { + return lgetxattr(g_path, name, buf, size); +} + +ssize_t fgetxattr_wrapper(const char *name, char *buf, size_t size) { + return fgetxattr(g_fd, name, buf, size); +} + +size_t test_list(ssize_t fun(char*, size_t), char **buf) { + int buf_size = 1024; + while (true) { + *buf = (char *)malloc(buf_size); + assert(__msan_test_shadow(*buf, buf_size) != -1); + ssize_t res = fun(*buf, buf_size); + if (res >= 0) { + assert(__msan_test_shadow(*buf, buf_size) == res); + return res; + } + if (errno == ENOTSUP) { + printf("Extended attributes are disabled. *xattr test is a no-op.\n"); + exit(0); + } + assert(errno == ERANGE); + free(*buf); + buf_size *= 2; + } +} + +// True means success. False means result inconclusive because we don't have +// access to this attribute. +bool test_get_single_attr(ssize_t fun(const char *, char *, size_t), + const char *attr_name) { + char *buf; + int buf_size = 1024; + while (true) { + buf = (char *)malloc(buf_size); + assert(__msan_test_shadow(buf, buf_size) != -1); + ssize_t res = fun(attr_name, buf, buf_size); + if (res >= 0) { + assert(__msan_test_shadow(buf, buf_size) == res); + free(buf); + return true; + } + if (errno == ENOTSUP) { + printf("Extended attributes are disabled. *xattr test is a no-op.\n"); + exit(0); + } + if (errno == ENOATTR) + return false; + assert(errno == ERANGE); + free(buf); + buf_size *= 2; + } +} + +void test_get(ssize_t fun(const char *, char *, size_t), const char *attr_list, + size_t attr_list_size) { + // Try every attribute, until we see one we can access. Attribute names are + // null-separated strings in attr_list. + size_t attr_list_len = argz_count(attr_list, attr_list_size); + char **attrs = (char **)malloc((attr_list_len + 1) * sizeof(char *)); + size_t i; + for (i = 0; (i < attr_list_len) && attrs[i]; i++) { + if (test_get_single_attr(fun, attrs[i])) + return; + } + printf("*xattr test could not access any attributes.\n"); +} + +// TODO: set some attributes before trying to retrieve them with *getxattr. +// Currently the list is empty, so *getxattr is not tested. +int main(int argc, char *argv[]) { + assert(argc == 2); + snprintf(g_path, sizeof(g_path), "%s/%s", argv[1], "xattr_test_root/a"); + + g_fd = open(g_path, O_RDONLY); + assert(g_fd); + + char *attr_list; + size_t attr_list_size; + attr_list_size = test_list(listxattr_wrapper, &attr_list); + free(attr_list); + attr_list_size = test_list(llistxattr_wrapper, &attr_list); + free(attr_list); + attr_list_size = test_list(flistxattr_wrapper, &attr_list); + + test_get(getxattr_wrapper, attr_list, attr_list_size); + test_get(lgetxattr_wrapper, attr_list, attr_list_size); + test_get(fgetxattr_wrapper, attr_list, attr_list_size); + + free(attr_list); + return 0; +} Index: lib/sanitizer_common/sanitizer_common_interceptors.inc =================================================================== --- lib/sanitizer_common/sanitizer_common_interceptors.inc +++ lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -3067,6 +3067,79 @@ #define INIT_TIMES #endif +#if SANITIZER_INTERCEPT_LISTXATTR +INTERCEPTOR(SSIZE_T, listxattr, const char *path, char *list, SIZE_T size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, listxattr, path, list, size); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + SSIZE_T res = REAL(listxattr)(path, list, size); + // Here and below, size == 0 is a special case where nothing is written to the + // buffer, and res contains the desired buffer size. + if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res); + return res; +} +INTERCEPTOR(SSIZE_T, llistxattr, const char *path, char *list, SIZE_T size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, llistxattr, path, list, size); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + SSIZE_T res = REAL(llistxattr)(path, list, size); + if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res); + return res; +} +INTERCEPTOR(SSIZE_T, flistxattr, int fd, char *list, SIZE_T size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, flistxattr, fd, list, size); + SSIZE_T res = REAL(flistxattr)(fd, list, size); + if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res); + return res; +} +#define INIT_LISTXATTR \ + COMMON_INTERCEPT_FUNCTION(listxattr); \ + COMMON_INTERCEPT_FUNCTION(llistxattr); \ + COMMON_INTERCEPT_FUNCTION(flistxattr); +#else +#define INIT_LISTXATTR +#endif + +#if SANITIZER_INTERCEPT_GETXATTR +INTERCEPTOR(SSIZE_T, getxattr, const char *path, const char *name, char *value, + SIZE_T size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getxattr, path, name, value, size); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); + SSIZE_T res = REAL(getxattr)(path, name, value, size); + if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res); + return res; +} +INTERCEPTOR(SSIZE_T, lgetxattr, const char *path, const char *name, char *value, + SIZE_T size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lgetxattr, path, name, value, size); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); + SSIZE_T res = REAL(lgetxattr)(path, name, value, size); + if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res); + return res; +} +INTERCEPTOR(SSIZE_T, fgetxattr, int fd, const char *name, char *value, + SIZE_T size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fgetxattr, fd, name, value, size); + if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); + SSIZE_T res = REAL(fgetxattr)(fd, name, value, size); + if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res); + return res; +} +#define INIT_GETXATTR \ + COMMON_INTERCEPT_FUNCTION(getxattr); \ + COMMON_INTERCEPT_FUNCTION(lgetxattr); \ + COMMON_INTERCEPT_FUNCTION(fgetxattr); +#else +#define INIT_GETXATTR +#endif + + #define SANITIZER_COMMON_INTERCEPTORS_INIT \ INIT_TEXTDOMAIN; \ INIT_STRCMP; \ @@ -3182,4 +3255,6 @@ INIT_GETLINE; \ INIT_ICONV; \ INIT_TIMES; \ + INIT_LISTXATTR; \ + INIT_GETXATTR; /**/ Index: lib/sanitizer_common/sanitizer_common_syscalls.inc =================================================================== --- lib/sanitizer_common/sanitizer_common_syscalls.inc +++ lib/sanitizer_common/sanitizer_common_syscalls.inc @@ -1004,8 +1004,8 @@ POST_SYSCALL(getxattr)(long res, const void *path, const void *name, void *value, long size) { - if (res >= 0) { - if (value) POST_WRITE(value, size); + if (size && res > 0) { + if (value) POST_WRITE(value, res); } } @@ -1019,8 +1019,8 @@ POST_SYSCALL(lgetxattr)(long res, const void *path, const void *name, void *value, long size) { - if (res >= 0) { - if (value) POST_WRITE(value, size); + if (size && res > 0) { + if (value) POST_WRITE(value, res); } } @@ -1031,8 +1031,8 @@ POST_SYSCALL(fgetxattr)(long res, long fd, const void *name, void *value, long size) { - if (res >= 0) { - if (value) POST_WRITE(value, size); + if (size && res > 0) { + if (value) POST_WRITE(value, res); } } @@ -1042,8 +1042,8 @@ } POST_SYSCALL(listxattr)(long res, const void *path, void *list, long size) { - if (res >= 0) { - if (list) POST_WRITE(list, size); + if (size && res > 0) { + if (list) POST_WRITE(list, res); } } @@ -1053,16 +1053,16 @@ } POST_SYSCALL(llistxattr)(long res, const void *path, void *list, long size) { - if (res >= 0) { - if (list) POST_WRITE(list, size); + if (size && res > 0) { + if (list) POST_WRITE(list, res); } } PRE_SYSCALL(flistxattr)(long fd, void *list, long size) {} POST_SYSCALL(flistxattr)(long res, long fd, void *list, long size) { - if (res >= 0) { - if (list) POST_WRITE(list, size); + if (size && res > 0) { + if (list) POST_WRITE(list, res); } } Index: lib/sanitizer_common/sanitizer_platform_interceptors.h =================================================================== --- lib/sanitizer_common/sanitizer_platform_interceptors.h +++ lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -177,4 +177,7 @@ #define SANITIZER_INTERCEPT_PTHREAD_COND SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_LISTXATTR SI_LINUX +#define SANITIZER_INTERCEPT_GETXATTR SI_LINUX + #endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H