diff --git a/compiler-rt/lib/msan/tests/msan_test.cpp b/compiler-rt/lib/msan/tests/msan_test.cpp index a126dd4fdd55e..d1c481483dfad 100644 --- a/compiler-rt/lib/msan/tests/msan_test.cpp +++ b/compiler-rt/lib/msan/tests/msan_test.cpp @@ -4908,5 +4908,100 @@ TEST(MemorySanitizer, timer_create) { EXPECT_POISONED(timer2); timer_delete(timer); } + +TEST(MemorySanitizer, getservent_r) { + if (access("/etc/services", O_RDONLY) != 0) + GTEST_SKIP() << "Missing /etc/services"; + struct servent result_buf; + struct servent *result; + char buf[1024]; + EXPECT_POISONED(result_buf); + EXPECT_POISONED(result); + EXPECT_POISONED(buf); + ASSERT_EQ(getservent_r(&result_buf, buf, sizeof(buf), &result), 0); + EXPECT_NOT_POISONED(result); + ASSERT_NE(result, nullptr); + EXPECT_NOT_POISONED(result_buf); + EXPECT_NOT_POISONED(buf); +} + +TEST(MemorySanitizer, getservbyname_r) { + if (access("/etc/services", O_RDONLY) != 0) + GTEST_SKIP() << "Missing /etc/services"; + struct servent result_buf; + struct servent *result; + char buf[1024]; + EXPECT_POISONED(result_buf); + EXPECT_POISONED(result); + EXPECT_POISONED(buf); + ASSERT_EQ( + getservbyname_r("ssh", nullptr, &result_buf, buf, sizeof(buf), &result), + 0); + EXPECT_NOT_POISONED(result); + // If this fails, check /etc/services if "ssh" exists. I picked this because + // it should exist everywhere, if it doesn't, I am sorry. Disable the test + // then please. + ASSERT_NE(result, nullptr); + EXPECT_NOT_POISONED(result_buf); + EXPECT_NOT_POISONED(buf); +} + +TEST(MemorySanitizer, getservbyname_r_unknown) { + if (access("/etc/services", O_RDONLY) != 0) + GTEST_SKIP() << "Missing /etc/services"; + struct servent result_buf; + struct servent *result; + char buf[1024]; + EXPECT_POISONED(result_buf); + EXPECT_POISONED(result); + EXPECT_POISONED(buf); + ASSERT_EQ(getservbyname_r("invalidhadfuiasdhi", nullptr, &result_buf, buf, + sizeof(buf), &result), + 0); + EXPECT_NOT_POISONED(result); + ASSERT_EQ(result, nullptr); + EXPECT_POISONED(result_buf); + EXPECT_POISONED(buf); +} + +TEST(MemorySanitizer, getservbyport_r) { + if (access("/etc/services", O_RDONLY) != 0) + GTEST_SKIP() << "Missing /etc/services"; + struct servent result_buf; + struct servent *result; + char buf[1024]; + EXPECT_POISONED(result_buf); + EXPECT_POISONED(result); + EXPECT_POISONED(buf); + ASSERT_EQ(getservbyport_r(htons(22), nullptr, &result_buf, buf, sizeof(buf), + &result), + 0); + EXPECT_NOT_POISONED(result); + // If this fails, check /etc/services if "ssh" exists. I picked this because + // it should exist everywhere, if it doesn't, I am sorry. Disable the test + // then please. + ASSERT_NE(result, nullptr); + EXPECT_NOT_POISONED(result_buf); + EXPECT_NOT_POISONED(buf); +} + +TEST(MemorySanitizer, getservbyport_r_smallbuf) { + if (access("/etc/services", O_RDONLY) != 0) + GTEST_SKIP() << "Missing /etc/services"; + struct servent result_buf; + struct servent *result; + char buf[1]; + EXPECT_POISONED(result_buf); + EXPECT_POISONED(result); + EXPECT_POISONED(buf); + ASSERT_EQ(getservbyport_r(htons(22), nullptr, &result_buf, buf, sizeof(buf), + &result), + ERANGE); + EXPECT_NOT_POISONED(result); + ASSERT_EQ(result, nullptr); + EXPECT_POISONED(result_buf); + EXPECT_POISONED(buf); +} + #endif } // namespace diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc index 761dbd3f5a679..5a15d75f0c86a 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -10279,6 +10279,71 @@ INTERCEPTOR(SSIZE_T, freadlink, int fd, char *buf, SIZE_T bufsiz) { # define INIT_FREADLINK #endif +#if SANITIZER_INTERCEPT_GETSERVENT_R || SANITIZER_INTERCEPT_GETSERVBYNAME_R || \ + SANITIZER_INTERCEPT_GETSERVBYPORT_R + +UNUSED static void HandleGetServentReentrantResult( + void *ctx, int res, struct __sanitizer_servent *result_buf, char *buf, + SIZE_T buflen, struct __sanitizer_servent **result) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (char *)result, sizeof(void *)); + if (res) + return; + if (*result) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (char *)*result, + sizeof(__sanitizer_servent)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); + } +} + +#endif + +#if SANITIZER_INTERCEPT_GETSERVENT_R +INTERCEPTOR(int, getservent_r, struct __sanitizer_servent *result_buf, + char *buf, SIZE_T buflen, struct __sanitizer_servent **result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getservent_r, result_buf, buf, buflen, result); + int res = REAL(getservent_r)(result_buf, buf, buflen, result); + HandleGetServentReentrantResult(ctx, res, result_buf, buf, buflen, result); + return res; +} +# define INIT_GETSERVENT_R COMMON_INTERCEPT_FUNCTION(getservent_r) +#else +# define INIT_GETSERVENT_R +#endif + +#if SANITIZER_INTERCEPT_GETSERVBYNAME_R +INTERCEPTOR(int, getservbyname_r, const char *name, const char *proto, + struct __sanitizer_servent *result_buf, char *buf, SIZE_T buflen, + struct __sanitizer_servent **result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getservbyname_r, name, proto, result_buf, buf, + buflen, result); + COMMON_INTERCEPTOR_READ_STRING(ctx, name, internal_strlen(name)); + int res = REAL(getservbyname_r)(name, proto, result_buf, buf, buflen, result); + HandleGetServentReentrantResult(ctx, res, result_buf, buf, buflen, result); + return res; +} +# define INIT_GETSERVBYNAME_R COMMON_INTERCEPT_FUNCTION(getservbyname_r) +#else +# define INIT_GETSERVBYNAME_R +#endif + +#if SANITIZER_INTERCEPT_GETSERVBYPORT_R +INTERCEPTOR(int, getservbyport_r, int port, const char *proto, + struct __sanitizer_servent *result_buf, char *buf, SIZE_T buflen, + struct __sanitizer_servent **result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getservbyport_r, port, proto, result_buf, buf, + buflen, result); + int res = REAL(getservbyport_r)(port, proto, result_buf, buf, buflen, result); + HandleGetServentReentrantResult(ctx, res, result_buf, buf, buflen, result); + return res; +} +# define INIT_GETSERVBYPORT_R COMMON_INTERCEPT_FUNCTION(getservbyport_r) +#else +# define INIT_GETSERVBYPORT_R +#endif + #include "sanitizer_common_interceptors_netbsd_compat.inc" namespace __sanitizer { @@ -10604,4 +10669,7 @@ static void InitializeCommonInterceptors() { INIT_FREADLINK; INIT___PRINTF_CHK; + INIT_GETSERVENT_R; + INIT_GETSERVBYNAME_R; + INIT_GETSERVBYPORT_R; } diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h index 468b5494d0092..b8f2f738e7478 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -645,6 +645,10 @@ SANITIZER_WEAK_IMPORT void *aligned_alloc(__sanitizer::usize __alignment, # define SI_MAC_OS_DEPLOYMENT_MIN_13_00 0 #endif #define SANITIZER_INTERCEPT_FREADLINK (SI_MAC && SI_MAC_OS_DEPLOYMENT_MIN_13_00) +#define SANITIZER_INTERCEPT_GETSERVENT_R SI_GLIBC +#define SANITIZER_INTERCEPT_GETSERVBYNAME_R SI_GLIBC +#define SANITIZER_INTERCEPT_GETSERVBYPORT_R 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 // to do two things: 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 67f00ff6f9e72..1f7e3d21b6a6f 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h @@ -1509,6 +1509,15 @@ extern unsigned IOCTL_KIOCSOUND; extern unsigned IOCTL_PIO_SCRNMAP; #endif +# if SANITIZER_GLIBC +struct __sanitizer_servent { + char *s_name; + char **s_aliases; + int s_port; + char *s_proto; +}; +# endif + extern const int si_SEGV_MAPERR; extern const int si_SEGV_ACCERR; } // namespace __sanitizer diff --git a/compiler-rt/test/sanitizer_common/TestCases/Linux/getservent_r.cpp b/compiler-rt/test/sanitizer_common/TestCases/Linux/getservent_r.cpp new file mode 100644 index 0000000000000..b356c0ed807f6 --- /dev/null +++ b/compiler-rt/test/sanitizer_common/TestCases/Linux/getservent_r.cpp @@ -0,0 +1,44 @@ +// RUN: %clangxx -O0 %s -o %t && %run %t + +// REQUIRES: glibc, netbase + +#include +#include +#include +#include +#include +#include +#include +#include + +void CheckResult(const char *file, int line, int ret) { + if (ret != 0) { + fprintf(stderr, "ERROR: %s:%d - %s\n", file, line, strerror(ret)); + } + assert(ret == 0); +} + +#define CHECK_RESULT(ret) CheckResult(__FILE__, __LINE__, ret) + +int main(void) { + assert(access("/etc/services", O_RDONLY) == 0); + struct servent result_buf; + struct servent *result; + char buf[1024]; + // If these fail, check /etc/services if "ssh" exists. I picked this because + // it should exist everywhere, if it doesn't, I am sorry. Disable the test + // then please. + CHECK_RESULT( + getservbyname_r("ssh", nullptr, &result_buf, buf, sizeof(buf), &result)); + assert(result != nullptr); + CHECK_RESULT(getservbyport_r(htons(22), nullptr, &result_buf, buf, + sizeof(buf), &result)); + assert(result != nullptr); + + CHECK_RESULT(getservent_r(&result_buf, buf, sizeof(buf), &result)); + assert(result != nullptr); + + CHECK_RESULT(getservbyname_r("invalidhadfuiasdhi", nullptr, &result_buf, buf, + sizeof(buf), &result)); + assert(result == nullptr); +} diff --git a/compiler-rt/test/sanitizer_common/lit.common.cfg.py b/compiler-rt/test/sanitizer_common/lit.common.cfg.py index 5406e8838f2fc..c3c1336bacd53 100644 --- a/compiler-rt/test/sanitizer_common/lit.common.cfg.py +++ b/compiler-rt/test/sanitizer_common/lit.common.cfg.py @@ -100,3 +100,6 @@ def build_invocation(compile_flags): if config.host_os == "NetBSD": config.substitutions.insert(0, ("%run", config.netbsd_noaslr_prefix)) + +if os.path.exists("/etc/services"): + config.available_features.add("netbase")