llvm-project/compiler-rt/lib/interception/tests/interception_linux_test.cpp
Marco Elver 37445e96d8 [compiler-rt] Allow 3 simultaneous interceptors on Linux
Rework Linux (and *BSD) interceptors to allow for up to 3 (2 for *BSD)
simultaneous interceptors. See code comments for details.

The main motivation is to support new sampling sanitizers (in the spirit
of GWP-ASan), that have to intercept few functions. Unfortunately, the
reality is that there are user interceptors that exist in the wild.

To support foreign user interceptors, foreign dynamic analysis
interceptors, and compiler-rt interceptors all at the same time,
including any combination of them, this change enables up to 3
interceptors on Linux (2 on *BSD).

v2:
* Revert to to the simpler "weak wrapper -(alias)-> __interceptor"
  scheme on architectures that cannot implement a trampoline efficiently
  due to complexities of resolving a preemptible symbol (PowerPC64
  ELFv2 global entry, and i386 PIC).
* Avoid duplicate intercepted functions in gen_dynamic_list.py, due to
  matching __interceptor_X and ___interceptor_X.
* Fix s390 __tls_get_offset.

Reviewed By: dvyukov, MaskRay, vitalybuka

Differential Revision: https://reviews.llvm.org/D151085
2023-06-09 11:30:41 +02:00

152 lines
4.2 KiB
C++

//===-- interception_linux_test.cpp ---------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
// Tests for interception_linux.h.
//
//===----------------------------------------------------------------------===//
// Do not declare functions in ctype.h.
#define __NO_CTYPE
#include "interception/interception.h"
#include <stdlib.h>
#include "gtest/gtest.h"
#if SANITIZER_LINUX
static int isdigit_called;
namespace __interception {
int isalpha_called;
int isalnum_called;
int islower_called;
} // namespace __interception
using namespace __interception;
DECLARE_REAL(int, isdigit, int);
DECLARE_REAL(int, isalpha, int);
DECLARE_REAL(int, isalnum, int);
DECLARE_REAL(int, islower, int);
INTERCEPTOR(void *, malloc, SIZE_T s) { return calloc(1, s); }
INTERCEPTOR(void, dummy_doesnt_exist__, ) { __builtin_trap(); }
INTERCEPTOR(int, isdigit, int d) {
++isdigit_called;
return d >= '0' && d <= '9';
}
INTERCEPTOR(int, isalpha, int d) {
// Use non-commutative arithmetic to verify order of calls.
isalpha_called = isalpha_called * 10 + 3;
return (d >= 'a' && d <= 'z') || (d >= 'A' && d <= 'Z');
}
INTERCEPTOR(int, isalnum, int d) {
isalnum_called = isalnum_called * 10 + 3;
return __interceptor_isalpha(d) || __interceptor_isdigit(d);
}
INTERCEPTOR(int, islower, int d) {
islower_called = islower_called * 10 + 3;
return d >= 'a' && d <= 'z';
}
namespace __interception {
TEST(Interception, InterceptFunction) {
uptr malloc_address = 0;
EXPECT_TRUE(InterceptFunction("malloc", &malloc_address, (uptr)&malloc,
(uptr)&TRAMPOLINE(malloc)));
EXPECT_NE(0U, malloc_address);
EXPECT_FALSE(InterceptFunction("malloc", &malloc_address, (uptr)&calloc,
(uptr)&TRAMPOLINE(malloc)));
uptr dummy_address = 0;
EXPECT_FALSE(InterceptFunction("dummy_doesnt_exist__", &dummy_address,
(uptr)&dummy_doesnt_exist__,
(uptr)&TRAMPOLINE(dummy_doesnt_exist__)));
EXPECT_EQ(0U, dummy_address);
}
TEST(Interception, Basic) {
EXPECT_TRUE(INTERCEPT_FUNCTION(isdigit));
// After interception, the counter should be incremented.
isdigit_called = 0;
EXPECT_NE(0, isdigit('1'));
EXPECT_EQ(1, isdigit_called);
EXPECT_EQ(0, isdigit('a'));
EXPECT_EQ(2, isdigit_called);
// Calling the REAL function should not affect the counter.
isdigit_called = 0;
EXPECT_NE(0, REAL(isdigit)('1'));
EXPECT_EQ(0, REAL(isdigit)('a'));
EXPECT_EQ(0, isdigit_called);
}
TEST(Interception, ForeignOverrideDirect) {
// Actual interceptor is overridden.
EXPECT_FALSE(INTERCEPT_FUNCTION(isalpha));
isalpha_called = 0;
EXPECT_NE(0, isalpha('a'));
EXPECT_EQ(13, isalpha_called);
isalpha_called = 0;
EXPECT_EQ(0, isalpha('_'));
EXPECT_EQ(13, isalpha_called);
isalpha_called = 0;
EXPECT_NE(0, REAL(isalpha)('a'));
EXPECT_EQ(0, REAL(isalpha)('_'));
EXPECT_EQ(0, isalpha_called);
}
#if ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT
TEST(Interception, ForeignOverrideIndirect) {
// Actual interceptor is _not_ overridden.
EXPECT_TRUE(INTERCEPT_FUNCTION(isalnum));
isalnum_called = 0;
EXPECT_NE(0, isalnum('a'));
EXPECT_EQ(13, isalnum_called);
isalnum_called = 0;
EXPECT_EQ(0, isalnum('_'));
EXPECT_EQ(13, isalnum_called);
isalnum_called = 0;
EXPECT_NE(0, REAL(isalnum)('a'));
EXPECT_EQ(0, REAL(isalnum)('_'));
EXPECT_EQ(0, isalnum_called);
}
TEST(Interception, ForeignOverrideThree) {
// Actual interceptor is overridden.
EXPECT_FALSE(INTERCEPT_FUNCTION(islower));
islower_called = 0;
EXPECT_NE(0, islower('a'));
EXPECT_EQ(123, islower_called);
islower_called = 0;
EXPECT_EQ(0, islower('A'));
EXPECT_EQ(123, islower_called);
islower_called = 0;
EXPECT_NE(0, REAL(islower)('a'));
EXPECT_EQ(0, REAL(islower)('A'));
EXPECT_EQ(0, islower_called);
}
#endif // ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT
} // namespace __interception
#endif // SANITIZER_LINUX