
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
152 lines
4.2 KiB
C++
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
|