Skip to content

Commit 257cba6

Browse files
committed
Add tests demonstrating the issue around closed source libraries and instrumented apps that both modify containers
and the proposed solutions to this. stack_container_dynamic_lib.cpp uses the approach that library providers do the work to remove the container overflow instrumentation stack_container_dynamic_lib_redef.cpp shows three alternate approaches to providing the solution in the santizer headers when __ASAN_DISABLE_CONTAINER_OVERFLOW__ is defined: - provide an alternate empty body that is force inlined - provide an alternate empty body that is declared with static linkage - use a #define to remove the calls from the library sources These rely on the library providers including the sanitizer interface headers rather than declaring the functions themselves.
1 parent ccc6f58 commit 257cba6

File tree

2 files changed

+251
-0
lines changed

2 files changed

+251
-0
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Test to demonstrate compile-time disabling of container-overflow checks
2+
// in order to handle uninstrumented libraries
3+
4+
// Mimic a closed-source library compiled without ASan
5+
// RUN: %clangxx_asan -fno-sanitize=address -DSHARED_LIB %s -fPIC -shared -o %t-so.so
6+
7+
// RUN: %clangxx_asan %s %libdl -o %t
8+
// RUN: not %run %t 2>&1 | FileCheck %s
9+
10+
// RUN: %clangxx_asan %s %libdl -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ -o %t
11+
// RUN: %run %t 2>&1 | FileCheck --check-prefix=CHECK-NO-CONTAINER-OVERFLOW %s
12+
13+
#include <stdio.h>
14+
#include <assert.h>
15+
#include <sanitizer/common_interface_defs.h>
16+
17+
template <typename T>
18+
class Stack {
19+
private:
20+
T data[5];
21+
size_t size;
22+
23+
public:
24+
Stack() : size(0) {
25+
#if __has_feature(address_sanitizer) && !__ASAN_DISABLE_CONTAINER_OVERFLOW__
26+
// Mark entire storage as unaddressable initially
27+
__sanitizer_annotate_contiguous_container(data, data + 5, data + 5, data);
28+
#endif
29+
}
30+
31+
~Stack() {
32+
#if __has_feature(address_sanitizer) && !__ASAN_DISABLE_CONTAINER_OVERFLOW__
33+
__sanitizer_annotate_contiguous_container(data, data + 5, data + size, data + 5);
34+
#endif
35+
}
36+
37+
void push(const T& value) {
38+
assert(size < 5 && "Stack overflow");
39+
#if __has_feature(address_sanitizer) && !__ASAN_DISABLE_CONTAINER_OVERFLOW__
40+
__sanitizer_annotate_contiguous_container(data, data + 5, data + size, data + size + 1);
41+
#endif
42+
data[size++] = value;
43+
}
44+
45+
T pop() {
46+
assert(size > 0 && "Cannot pop from empty stack");
47+
T result = data[--size];
48+
#if __has_feature(address_sanitizer) && !__ASAN_DISABLE_CONTAINER_OVERFLOW__
49+
__sanitizer_annotate_contiguous_container(data, data + 5, data + size + 1, data + size);
50+
#endif
51+
return result;
52+
}
53+
};
54+
55+
#ifdef SHARED_LIB
56+
// Mimics a closed-source library compiled without ASan
57+
58+
extern "C" void push_value_to_stack(Stack<int>& stack) {
59+
stack.push(42);
60+
}
61+
#else // SHARED_LIB
62+
63+
#include <dlfcn.h>
64+
#include <string>
65+
66+
typedef void (*push_func_t)(Stack<int>&);
67+
68+
int main(int argc, char *argv[]) {
69+
std::string path = std::string(argv[0]) + "-so.so";
70+
printf("Loading library: %s\n", path.c_str());
71+
72+
void *lib = dlopen(path.c_str(), RTLD_NOW);
73+
assert(lib);
74+
75+
push_func_t push_value = (push_func_t)dlsym(lib, "push_value_to_stack");
76+
assert(push_value);
77+
78+
Stack<int> stack;
79+
push_value(stack);
80+
81+
// BOOM! uninstrumented library didn't update container bounds
82+
int value = stack.pop();
83+
// CHECK: AddressSanitizer: container-overflow
84+
printf("Popped value: %d\n", value);
85+
assert(value == 42 && "Expected value 42");
86+
87+
dlclose(lib);
88+
printf("SUCCESS\n");
89+
// CHECK-NO-CONTAINER-OVERFLOW: SUCCESS
90+
return 0;
91+
}
92+
93+
#endif // SHARED_LIB
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
// Test to demonstrate compile-time disabling of container-overflow checks
2+
// in order to handle uninstrumented libraries
3+
//
4+
// Explore three options discussed as implementable in the santizer headers to reduce the need
5+
// for changes in library code if they include the sanitizer interface header sanitizer/common_interface_defs.h
6+
// instead of having their own forward declaration
7+
//
8+
// - force inlined alternative body - minimizes the need for optimizations to reduce bloat
9+
// - static declaration of alternate body - potential bloat if optimizer is not run
10+
// - use of #define to remove calls completely
11+
12+
// Mimic a closed-source library compiled without ASan
13+
// RUN: %clangxx_asan -fno-sanitize=address -DSHARED_LIB %s -dynamiclib -o %t-closedsource.dylib
14+
15+
// Mimic multiple files being linked into a single executable,
16+
// %t-object.o and %t-main compiled seperately and then linked together
17+
//
18+
// -fsanitize=address with container overflow turned on (default)
19+
// RUN: %clangxx_asan -DMULTI_SOURCE %s -c -o %t-object.o
20+
// RUN: %clangxx_asan %s -c -o %t-main.o
21+
// RUN: %clangxx_asan -o %t %t-main.o %t-object.o -framework %t-closedsource.dylib
22+
// RUN: not %run %t 2>&1 | FileCheck %s
23+
//
24+
// -fsanitize=address with container overflow turned off using ALWAYS_INLINE
25+
// RUN: %clangxx_asan -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ -DMULTI_SOURCE %s -c -o %t-object.o
26+
// RUN: %clangxx_asan -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ %s -c -o %t-main.o
27+
// RUN: %clangxx_asan -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ -o %t %t-main.o %t-object.o -framework %t-closedsource.dylib
28+
// RUN: %run %t 2>&1 | FileCheck --check-prefix=CHECK-NO-CONTAINER-OVERFLOW %s
29+
//
30+
// -fsanitize=address with container overflow turned off using static linkage
31+
// RUN: %clangxx_asan -DUSE_STATIC -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ -DMULTI_SOURCE %s -c -o %t-object.o
32+
// RUN: %clangxx_asan -DUSE_STATIC -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ %s -c -o %t-main.o
33+
// RUN: %clangxx_asan -DUSE_STATIC -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ -o %t %t-main.o %t-object.o -framework %t-closedsource.dylib
34+
// RUN: %run %t 2>&1 | FileCheck --check-prefix=CHECK-NO-CONTAINER-OVERFLOW %s
35+
//
36+
// -fsanitize=address with container overflow turned off using #define to remove the function calls
37+
// RUN: %clangxx_asan -DUSE_DEFINE -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ -DMULTI_SOURCE %s -c -o %t-object.o
38+
// RUN: %clangxx_asan -DUSE_DEFINE -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ %s -c -o %t-main.o
39+
// RUN: %clangxx_asan -DUSE_DEFINE -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ -o %t %t-main.o %t-object.o -framework %t-closedsource.dylib
40+
// RUN: %run %t 2>&1 | FileCheck --check-prefix=CHECK-NO-CONTAINER-OVERFLOW %s
41+
42+
#include <assert.h>
43+
#include <stdio.h>
44+
45+
// Mimic sanitizer/common_interface_defs disabling the container overflow calls
46+
#if !__has_feature(address_sanitizer) || __ASAN_DISABLE_CONTAINER_OVERFLOW__
47+
48+
# if USE_DEFINE
49+
# define __sanitizer_annotate_contiguous_container(...)
50+
# else
51+
52+
// in this test match the extern "C" declaration as <string.h> pulls in copies of the
53+
// declarations
54+
extern "C" {
55+
# if USE_STATIC
56+
static
57+
# else
58+
59+
// clone of the ALWAYS_INLINE macro
60+
# if defined(_MSC_VER)
61+
# define ALWAYS_INLINE __forceinline
62+
# else // _MSC_VER
63+
# define ALWAYS_INLINE inline __attribute__((always_inline))
64+
# endif // _MSC_VER
65+
66+
ALWAYS_INLINE
67+
# endif
68+
void
69+
__sanitizer_annotate_contiguous_container(const void *beg, const void *end,
70+
const void *old_mid,
71+
const void *new_mid) {};
72+
}
73+
# endif
74+
75+
#else
76+
77+
# include <sanitizer/common_interface_defs.h>
78+
79+
#endif
80+
81+
// Mimic template based container library header
82+
template <typename T> class Stack {
83+
private:
84+
T data[5];
85+
size_t size;
86+
87+
public:
88+
Stack() : size(0) {
89+
// Mark entire storage as unaddressable initially
90+
#if __has_feature(address_sanitizer)
91+
__sanitizer_annotate_contiguous_container(data, data + 5, data + 5, data);
92+
#endif
93+
}
94+
95+
~Stack() {
96+
#if __has_feature(address_sanitizer)
97+
__sanitizer_annotate_contiguous_container(data, data + 5, data + size,
98+
data + 5);
99+
#endif
100+
}
101+
102+
void push(const T &value) {
103+
assert(size < 5 && "Stack overflow");
104+
#if __has_feature(address_sanitizer)
105+
__sanitizer_annotate_contiguous_container(data, data + 5, data + size,
106+
data + size + 1);
107+
#endif
108+
data[size++] = value;
109+
}
110+
111+
T pop() {
112+
assert(size > 0 && "Cannot pop from empty stack");
113+
T result = data[--size];
114+
#if __has_feature(address_sanitizer)
115+
__sanitizer_annotate_contiguous_container(data, data + 5, data + size + 1,
116+
data + size);
117+
#endif
118+
return result;
119+
}
120+
};
121+
122+
#if defined(SHARED_LIB)
123+
// Mimics a closed-source library compiled without ASan
124+
125+
extern "C" void push_value_to_stack(Stack<int> &stack) { stack.push(42); }
126+
127+
#elif defined(MULTI_SOURCE)
128+
129+
// Mimic multiple source files in a single project compiled seperately
130+
extern "C" void push_value_to_stack(Stack<int> &stack);
131+
132+
extern "C" void do_push_value_to_stack(Stack<int> &stack) {
133+
push_value_to_stack(stack);
134+
}
135+
136+
#else
137+
138+
extern "C" void do_push_value_to_stack(Stack<int> &stack);
139+
140+
# include <string>
141+
142+
int main(int argc, char *argv[]) {
143+
144+
Stack<int> stack;
145+
do_push_value_to_stack(stack);
146+
147+
// BOOM! uninstrumented library didn't update container bounds
148+
int value = stack.pop();
149+
// CHECK: AddressSanitizer: container-overflow
150+
printf("Popped value: %d\n", value);
151+
assert(value == 42 && "Expected value 42");
152+
153+
printf("SUCCESS\n");
154+
// CHECK-NO-CONTAINER-OVERFLOW: SUCCESS
155+
return 0;
156+
}
157+
158+
#endif // SHARED_LIB

0 commit comments

Comments
 (0)