From f639882be8883d9dee6ed291852b721e10103a0d Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Wed, 3 Nov 2021 10:21:43 -0700 Subject: [PATCH] [sanitizer] Allow getsockname with NULL addrlen This is already permitted in getpeername, and returns EFAULT on Linux (does not crash the program). Fixes https://github.com/google/sanitizers/issues/1451. Differential Revision: https://reviews.llvm.org/D113055 --- .../sanitizer_common_interceptors.inc | 21 +++++++---- .../TestCases/Linux/get_sock_peer_name.cpp | 35 +++++++++++++++++++ 2 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 compiler-rt/test/sanitizer_common/TestCases/Linux/get_sock_peer_name.cpp diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc index a6a5230cdadc3c..abb38ccfa15d21 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -2712,17 +2712,20 @@ INTERCEPTOR(int, getnameinfo, void *sockaddr, unsigned salen, char *host, #endif #if SANITIZER_INTERCEPT_GETSOCKNAME -INTERCEPTOR(int, getsockname, int sock_fd, void *addr, int *addrlen) { +INTERCEPTOR(int, getsockname, int sock_fd, void *addr, unsigned *addrlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getsockname, sock_fd, addr, addrlen); - COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); - int addrlen_in = *addrlen; + unsigned addr_sz; + if (addrlen) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); + addr_sz = *addrlen; + } // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getsockname)(sock_fd, addr, addrlen); - if (res == 0) { - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addrlen_in, *addrlen)); + if (!res && addr && addrlen) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addr_sz, *addrlen)); } return res; } @@ -3227,13 +3230,17 @@ INTERCEPTOR(int, getpeername, int sockfd, void *addr, unsigned *addrlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpeername, sockfd, addr, addrlen); unsigned addr_sz; - if (addrlen) addr_sz = *addrlen; + if (addrlen) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); + addr_sz = *addrlen; + } // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getpeername)(sockfd, addr, addrlen); - if (!res && addr && addrlen) + if (!res && addr && addrlen) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addr_sz, *addrlen)); + } return res; } #define INIT_GETPEERNAME COMMON_INTERCEPT_FUNCTION(getpeername); diff --git a/compiler-rt/test/sanitizer_common/TestCases/Linux/get_sock_peer_name.cpp b/compiler-rt/test/sanitizer_common/TestCases/Linux/get_sock_peer_name.cpp new file mode 100644 index 00000000000000..cd916d36b4b2e2 --- /dev/null +++ b/compiler-rt/test/sanitizer_common/TestCases/Linux/get_sock_peer_name.cpp @@ -0,0 +1,35 @@ +// Test that ASan doesn't raise false alarm when getsockname and getpeername +// are called with addrlen=nullptr; +// +// RUN: %clangxx %s -o %t && %run %t 2>&1 + +#include +#include +#include +#include + +int main() { + const int fd = socket(AF_INET, SOCK_DGRAM, 0); + assert(fd >= 0); + + const sockaddr_in sin = { + .sin_family = AF_INET, + .sin_port = 1234, + .sin_addr = + { + .s_addr = INADDR_LOOPBACK, + }, + }; + assert(connect(fd, reinterpret_cast(&sin), sizeof(sin)) == + 0); + + errno = 0; + assert(getsockname(fd, nullptr, nullptr) == -1); + assert(errno == EFAULT); + + errno = 0; + assert(getpeername(fd, nullptr, nullptr) == -1); + assert(errno == EFAULT); + + return 0; +}