Skip to content

Commit fcbcc61

Browse files
committedJan 4, 2019
hwasan: Use system allocator to realloc and free untagged pointers in interceptor mode.
The Android dynamic loader has a non-standard feature that allows libraries such as the hwasan runtime to interpose symbols even after the symbol already has a value. The new value of the symbol is used to relocate libraries loaded after the interposing library, but existing libraries keep the old value. This behaviour is activated by the DF_1_GLOBAL flag in DT_FLAGS_1, which is set by passing -z global to the linker, which is what we already do to link the hwasan runtime. What this means in practice is that if we have .so files that depend on interceptor-mode hwasan without the main executable depending on it, some of the libraries in the process will be using the hwasan allocator and some will be using the system allocator, and these allocators need to interact somehow. For example, if an instrumented library calls a function such as strdup that allocates memory on behalf of the caller, the instrumented library can reasonably expect to be able to call free to deallocate the memory. We can handle that relatively easily with hwasan by using tag 0 to represent allocations from the system allocator. If hwasan's realloc or free functions are passed a pointer with tag 0, the system allocator is called. One limitation is that this scheme doesn't work in reverse: if an instrumented library allocates memory, it must free the memory itself and cannot pass ownership to a system library. In a future change, we may want to expose an API for calling the system allocator so that instrumented libraries can safely transfer ownership of memory to system libraries. Differential Revision: https://reviews.llvm.org/D55986 llvm-svn: 350427
1 parent 0743cda commit fcbcc61

File tree

8 files changed

+106
-14
lines changed

8 files changed

+106
-14
lines changed
 

‎compiler-rt/lib/hwasan/hwasan.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ void InitializeInterceptors();
7777

7878
void HwasanAllocatorInit();
7979
void HwasanAllocatorThreadFinish();
80-
void HwasanDeallocate(StackTrace *stack, void *ptr);
8180

8281
void *hwasan_malloc(uptr size, StackTrace *stack);
8382
void *hwasan_calloc(uptr nmemb, uptr size, StackTrace *stack);
@@ -88,6 +87,7 @@ void *hwasan_aligned_alloc(uptr alignment, uptr size, StackTrace *stack);
8887
void *hwasan_memalign(uptr alignment, uptr size, StackTrace *stack);
8988
int hwasan_posix_memalign(void **memptr, uptr alignment, uptr size,
9089
StackTrace *stack);
90+
void hwasan_free(void *ptr, StackTrace *stack);
9191

9292
void InstallTrapHandler();
9393
void InstallAtExitHandler();

‎compiler-rt/lib/hwasan/hwasan_allocator.cc

+37-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121
#include "hwasan_thread.h"
2222
#include "hwasan_report.h"
2323

24+
#if HWASAN_WITH_INTERCEPTORS
25+
DEFINE_REAL(void *, realloc, void *ptr, uptr size)
26+
DEFINE_REAL(void, free, void *ptr)
27+
#endif
28+
2429
namespace __hwasan {
2530

2631
static Allocator allocator;
@@ -199,7 +204,7 @@ static bool PointerAndMemoryTagsMatch(void *tagged_ptr) {
199204
return ptr_tag == mem_tag;
200205
}
201206

202-
void HwasanDeallocate(StackTrace *stack, void *tagged_ptr) {
207+
static void HwasanDeallocate(StackTrace *stack, void *tagged_ptr) {
203208
CHECK(tagged_ptr);
204209
HWASAN_FREE_HOOK(tagged_ptr);
205210

@@ -253,8 +258,8 @@ void HwasanDeallocate(StackTrace *stack, void *tagged_ptr) {
253258
}
254259
}
255260

256-
void *HwasanReallocate(StackTrace *stack, void *tagged_ptr_old, uptr new_size,
257-
uptr alignment) {
261+
static void *HwasanReallocate(StackTrace *stack, void *tagged_ptr_old,
262+
uptr new_size, uptr alignment) {
258263
if (!PointerAndMemoryTagsMatch(tagged_ptr_old))
259264
ReportInvalidFree(stack, reinterpret_cast<uptr>(tagged_ptr_old));
260265

@@ -271,7 +276,7 @@ void *HwasanReallocate(StackTrace *stack, void *tagged_ptr_old, uptr new_size,
271276
return tagged_ptr_new;
272277
}
273278

274-
void *HwasanCalloc(StackTrace *stack, uptr nmemb, uptr size) {
279+
static void *HwasanCalloc(StackTrace *stack, uptr nmemb, uptr size) {
275280
if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
276281
if (AllocatorMayReturnNull())
277282
return nullptr;
@@ -315,6 +320,14 @@ void *hwasan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
315320
void *hwasan_realloc(void *ptr, uptr size, StackTrace *stack) {
316321
if (!ptr)
317322
return SetErrnoOnNull(HwasanAllocate(stack, size, sizeof(u64), false));
323+
324+
#if HWASAN_WITH_INTERCEPTORS
325+
// A tag of 0 means that this is a system allocator allocation, so we must use
326+
// the system allocator to realloc it.
327+
if (!flags()->disable_allocator_tagging && GetTagFromPointer((uptr)ptr) == 0)
328+
return REAL(realloc)(ptr, size);
329+
#endif
330+
318331
if (size == 0) {
319332
HwasanDeallocate(stack, ptr);
320333
return nullptr;
@@ -376,6 +389,17 @@ int hwasan_posix_memalign(void **memptr, uptr alignment, uptr size,
376389
return 0;
377390
}
378391

392+
void hwasan_free(void *ptr, StackTrace *stack) {
393+
#if HWASAN_WITH_INTERCEPTORS
394+
// A tag of 0 means that this is a system allocator allocation, so we must use
395+
// the system allocator to free it.
396+
if (!flags()->disable_allocator_tagging && GetTagFromPointer((uptr)ptr) == 0)
397+
return REAL(free)(ptr);
398+
#endif
399+
400+
return HwasanDeallocate(stack, ptr);
401+
}
402+
379403
} // namespace __hwasan
380404

381405
using namespace __hwasan;
@@ -385,6 +409,15 @@ void __hwasan_enable_allocator_tagging() {
385409
}
386410

387411
void __hwasan_disable_allocator_tagging() {
412+
#if HWASAN_WITH_INTERCEPTORS
413+
// Allocator tagging must be enabled for the system allocator fallback to work
414+
// correctly. This means that we can't disable it at runtime if it was enabled
415+
// at startup since that might result in our deallocations going to the system
416+
// allocator. If tagging was disabled at startup we avoid this problem by
417+
// disabling the fallback altogether.
418+
CHECK(flags()->disable_allocator_tagging);
419+
#endif
420+
388421
atomic_store_relaxed(&hwasan_allocator_tagging_enabled, 0);
389422
}
390423

‎compiler-rt/lib/hwasan/hwasan_allocator.h

+6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#ifndef HWASAN_ALLOCATOR_H
1515
#define HWASAN_ALLOCATOR_H
1616

17+
#include "interception/interception.h"
1718
#include "sanitizer_common/sanitizer_allocator.h"
1819
#include "sanitizer_common/sanitizer_allocator_checks.h"
1920
#include "sanitizer_common/sanitizer_allocator_interface.h"
@@ -26,6 +27,11 @@
2627
#error Unsupported platform
2728
#endif
2829

30+
#if HWASAN_WITH_INTERCEPTORS
31+
DECLARE_REAL(void *, realloc, void *ptr, uptr size)
32+
DECLARE_REAL(void, free, void *ptr)
33+
#endif
34+
2935
namespace __hwasan {
3036

3137
struct Metadata {

‎compiler-rt/lib/hwasan/hwasan_interceptors.cc

+5-7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "interception/interception.h"
1919
#include "hwasan.h"
20+
#include "hwasan_allocator.h"
2021
#include "hwasan_mapping.h"
2122
#include "hwasan_thread.h"
2223
#include "hwasan_poisoning.h"
@@ -44,11 +45,6 @@ using __sanitizer::atomic_load;
4445
using __sanitizer::atomic_store;
4546
using __sanitizer::atomic_uintptr_t;
4647

47-
DECLARE_REAL(SIZE_T, strlen, const char *s)
48-
DECLARE_REAL(SIZE_T, strnlen, const char *s, SIZE_T maxlen)
49-
DECLARE_REAL(void *, memcpy, void *dest, const void *src, uptr n)
50-
DECLARE_REAL(void *, memset, void *dest, int c, uptr n)
51-
5248
bool IsInInterceptorScope() {
5349
Thread *t = GetCurrentThread();
5450
return t && t->InInterceptorScope();
@@ -130,13 +126,13 @@ void * __sanitizer_pvalloc(uptr size) {
130126
void __sanitizer_free(void *ptr) {
131127
GET_MALLOC_STACK_TRACE;
132128
if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) return;
133-
HwasanDeallocate(&stack, ptr);
129+
hwasan_free(ptr, &stack);
134130
}
135131

136132
void __sanitizer_cfree(void *ptr) {
137133
GET_MALLOC_STACK_TRACE;
138134
if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) return;
139-
HwasanDeallocate(&stack, ptr);
135+
hwasan_free(ptr, &stack);
140136
}
141137

142138
uptr __sanitizer_malloc_usable_size(const void *ptr) {
@@ -290,6 +286,8 @@ void InitializeInterceptors() {
290286

291287
#if HWASAN_WITH_INTERCEPTORS
292288
INTERCEPT_FUNCTION(pthread_create);
289+
INTERCEPT_FUNCTION(realloc);
290+
INTERCEPT_FUNCTION(free);
293291
#endif
294292

295293
inited = 1;

‎compiler-rt/lib/hwasan/hwasan_linux.cc

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "sanitizer_common/sanitizer_procmaps.h"
4141

4242
#if HWASAN_WITH_INTERCEPTORS && !SANITIZER_ANDROID
43+
SANITIZER_INTERFACE_ATTRIBUTE
4344
THREADLOCAL uptr __hwasan_tls;
4445
#endif
4546

‎compiler-rt/lib/hwasan/hwasan_new_delete.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ void *operator new[](size_t size, std::nothrow_t const&) {
5151

5252
#define OPERATOR_DELETE_BODY \
5353
GET_MALLOC_STACK_TRACE; \
54-
if (ptr) HwasanDeallocate(&stack, ptr)
54+
if (ptr) hwasan_free(ptr, &stack)
5555

5656
INTERCEPTOR_ATTRIBUTE
5757
void operator delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// RUN: %clangxx %s -o %t -ldl
2+
// RUN: %clangxx_hwasan -shared %s -o %t.so -DSHARED_LIB -shared-libsan -Wl,-rpath,%compiler_rt_libdir
3+
// RUN: %env_hwasan_opts=disable_allocator_tagging=0 %run %t
4+
5+
#include <stddef.h>
6+
7+
// Test that allocations made by the system allocator can be realloc'd and freed
8+
// by the hwasan allocator.
9+
10+
typedef void run_test_fn(void *(*system_malloc)(size_t size));
11+
12+
#ifdef SHARED_LIB
13+
14+
// Call the __sanitizer_ versions of these functions so that the test
15+
// doesn't require the Android dynamic loader.
16+
extern "C" void *__sanitizer_realloc(void *ptr, size_t size);
17+
extern "C" void __sanitizer_free(void *ptr);
18+
19+
extern "C" run_test_fn run_test;
20+
void run_test(void *(*system_malloc)(size_t size)) {
21+
void *mem = system_malloc(64);
22+
mem = __sanitizer_realloc(mem, 128);
23+
__sanitizer_free(mem);
24+
}
25+
26+
#else
27+
28+
#include <dlfcn.h>
29+
#include <stdlib.h>
30+
#include <string>
31+
32+
int main(int argc, char **argv) {
33+
std::string path = argv[0];
34+
path += ".so";
35+
void *lib = dlopen(path.c_str(), RTLD_NOW);
36+
if (!lib) {
37+
printf("error in dlopen(): %s\n", dlerror());
38+
return 1;
39+
}
40+
41+
auto run_test = reinterpret_cast<run_test_fn *>(dlsym(lib, "run_test"));
42+
if (!run_test) {
43+
printf("failed dlsym\n");
44+
return 1;
45+
}
46+
47+
run_test(malloc);
48+
}
49+
50+
#endif

‎compiler-rt/test/hwasan/lit.cfg

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@ config.name = 'HWAddressSanitizer' + getattr(config, 'name_suffix', 'default')
99
config.test_source_root = os.path.dirname(__file__)
1010

1111
# Setup default compiler flags used with -fsanitize=memory option.
12-
clang_hwasan_cflags = ["-fsanitize=hwaddress", "-mllvm", "-hwasan-generate-tags-with-calls", config.target_cflags] + config.debug_info_flags
12+
clang_cflags = [config.target_cflags] + config.debug_info_flags
13+
clang_cxxflags = config.cxx_mode_flags + clang_cflags
14+
clang_hwasan_cflags = ["-fsanitize=hwaddress", "-mllvm", "-hwasan-generate-tags-with-calls"] + clang_cflags
1315
clang_hwasan_cxxflags = config.cxx_mode_flags + clang_hwasan_cflags
1416

1517
def build_invocation(compile_flags):
1618
return " " + " ".join([config.clang] + compile_flags) + " "
1719

20+
config.substitutions.append( ("%clangxx ", build_invocation(clang_cxxflags)) )
1821
config.substitutions.append( ("%clang_hwasan ", build_invocation(clang_hwasan_cflags)) )
1922
config.substitutions.append( ("%clangxx_hwasan ", build_invocation(clang_hwasan_cxxflags)) )
23+
config.substitutions.append( ("%compiler_rt_libdir", config.compiler_rt_libdir) )
2024

2125
default_hwasan_opts_str = ':'.join(['disable_allocator_tagging=1', 'random_tags=0'] + config.default_sanitizer_opts)
2226
if default_hwasan_opts_str:

0 commit comments

Comments
 (0)
Please sign in to comment.