asan_interceptors.cc revision 2d8b3bdb112ebb8ed3f15ee41d4cebcd683b41b0
1//===-- asan_interceptors.cc ------------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file is a part of AddressSanitizer, an address sanity checker.
11//
12// Intercept various libc functions to catch buggy memory accesses there.
13//===----------------------------------------------------------------------===//
14#include "asan_interceptors.h"
15
16#include "asan_allocator.h"
17#include "asan_interface.h"
18#include "asan_internal.h"
19#include "asan_mapping.h"
20#include "asan_stack.h"
21#include "asan_stats.h"
22
23#include <dlfcn.h>
24#include <string.h>
25
26namespace __asan {
27
28index_f       real_index;
29memcpy_f      real_memcpy;
30memmove_f     real_memmove;
31memset_f      real_memset;
32strchr_f      real_strchr;
33strcmp_f      real_strcmp;
34strcpy_f      real_strcpy;
35strdup_f      real_strdup;
36strlen_f      real_strlen;
37strncmp_f     real_strncmp;
38strncpy_f     real_strncpy;
39strnlen_f     real_strnlen;
40
41// Instruments read/write access to a single byte in memory.
42// On error calls __asan_report_error, which aborts the program.
43__attribute__((noinline))
44static void AccessAddress(uintptr_t address, bool isWrite) {
45  if (__asan_address_is_poisoned((void*)address)) {
46    GET_BP_PC_SP;
47    __asan_report_error(pc, bp, sp, address, isWrite, /* access_size */ 1);
48  }
49}
50
51// We implement ACCESS_MEMORY_RANGE, ASAN_READ_RANGE,
52// and ASAN_WRITE_RANGE as macro instead of function so
53// that no extra frames are created, and stack trace contains
54// relevant information only.
55
56// Instruments read/write access to a memory range.
57// More complex implementation is possible, for now just
58// checking the first and the last byte of a range.
59#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \
60  if (size > 0) { \
61    uintptr_t ptr = (uintptr_t)(offset); \
62    AccessAddress(ptr, isWrite); \
63    AccessAddress(ptr + (size) - 1, isWrite); \
64  } \
65} while (0);
66
67#define ASAN_READ_RANGE(offset, size) do { \
68  ACCESS_MEMORY_RANGE(offset, size, false); \
69} while (0);
70
71#define ASAN_WRITE_RANGE(offset, size) do { \
72  ACCESS_MEMORY_RANGE(offset, size, true); \
73} while (0);
74
75// Behavior of functions like "memcpy" or "strcpy" is undefined
76// if memory intervals overlap. We report error in this case.
77// Macro is used to avoid creation of new frames.
78static inline bool RangesOverlap(const char *offset1, const char *offset2,
79                                 size_t length) {
80  return !((offset1 + length <= offset2) || (offset2 + length <= offset1));
81}
82#define CHECK_RANGES_OVERLAP(_offset1, _offset2, length) do { \
83  const char *offset1 = (const char*)_offset1; \
84  const char *offset2 = (const char*)_offset2; \
85  if (RangesOverlap((const char*)offset1, (const char*)offset2, \
86                    length)) { \
87    Printf("ERROR: AddressSanitizer strcpy-param-overlap: " \
88           "memory ranges [%p,%p) and [%p, %p) overlap\n", \
89           offset1, offset1 + length, offset2, offset2 + length); \
90    PRINT_CURRENT_STACK(); \
91    ShowStatsAndAbort(); \
92  } \
93} while (0);
94
95static inline void ensure_asan_inited() {
96  CHECK(!asan_init_is_running);
97  if (!asan_inited) {
98    __asan_init();
99  }
100}
101
102
103size_t internal_strlen(const char *s) {
104  size_t i = 0;
105  while (s[i]) i++;
106  return i;
107}
108
109size_t internal_strnlen(const char *s, size_t maxlen) {
110  if (real_strnlen != NULL) {
111    return real_strnlen(s, maxlen);
112  }
113  size_t i = 0;
114  while (i < maxlen && s[i]) i++;
115  return i;
116}
117
118void InitializeAsanInterceptors() {
119#ifndef __APPLE__
120  INTERCEPT_FUNCTION(index);
121#else
122  OVERRIDE_FUNCTION(index, WRAP(strchr));
123#endif
124#ifndef __APPLE__
125  INTERCEPT_FUNCTION(memcpy);
126  INTERCEPT_FUNCTION(memmove);
127  INTERCEPT_FUNCTION(memset);
128#else
129  real_memcpy = memcpy;
130  real_memmove = memmove;
131  real_memset = memset;
132#endif
133  INTERCEPT_FUNCTION(strchr);
134  INTERCEPT_FUNCTION(strcmp);
135  INTERCEPT_FUNCTION(strcpy);  // NOLINT
136  INTERCEPT_FUNCTION(strdup);
137  INTERCEPT_FUNCTION(strlen);
138  INTERCEPT_FUNCTION(strncmp);
139  INTERCEPT_FUNCTION(strncpy);
140#ifndef __APPLE__
141  INTERCEPT_FUNCTION(strnlen);
142#endif
143  if (FLAG_v > 0) {
144    Printf("AddressSanitizer: libc interceptors initialized\n");
145  }
146}
147
148}  // namespace __asan
149
150// ---------------------- Wrappers ---------------- {{{1
151using namespace __asan;  // NOLINT
152
153void *WRAP(memcpy)(void *to, const void *from, size_t size) {
154  // memcpy is called during __asan_init() from the internals
155  // of printf(...).
156  if (asan_init_is_running) {
157    return real_memcpy(to, from, size);
158  }
159  ensure_asan_inited();
160  if (FLAG_replace_intrin) {
161    CHECK_RANGES_OVERLAP(to, from, size);
162    ASAN_WRITE_RANGE(from, size);
163    ASAN_READ_RANGE(to, size);
164  }
165  return real_memcpy(to, from, size);
166}
167
168void *WRAP(memmove)(void *to, const void *from, size_t size) {
169  ensure_asan_inited();
170  if (FLAG_replace_intrin) {
171    ASAN_WRITE_RANGE(from, size);
172    ASAN_READ_RANGE(to, size);
173  }
174  return real_memmove(to, from, size);
175}
176
177void *WRAP(memset)(void *block, int c, size_t size) {
178  ensure_asan_inited();
179  if (FLAG_replace_intrin) {
180    ASAN_WRITE_RANGE(block, size);
181  }
182  return real_memset(block, c, size);
183}
184
185// Note that on Linux index and strchr are definined differently depending on
186// the compiler (gcc vs clang).
187// see __CORRECT_ISO_CPP_STRING_H_PROTO in /usr/include/string.h
188
189#ifndef __APPLE__
190char *WRAP(index)(const char *str, int c)
191  __attribute__((alias(WRAPPER_NAME(strchr))));
192#endif
193
194char *WRAP(strchr)(const char *str, int c) {
195  ensure_asan_inited();
196  char *result = real_strchr(str, c);
197  if (FLAG_replace_str) {
198    size_t bytes_read = (result ? result - str : real_strlen(str)) + 1;
199    ASAN_READ_RANGE(str, bytes_read);
200  }
201  return result;
202}
203
204static inline int CharCmp(unsigned char c1, unsigned char c2) {
205  return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
206}
207
208int WRAP(strcmp)(const char *s1, const char *s2) {
209  // strcmp is called from malloc_default_purgeable_zone()
210  // in __asan::ReplaceSystemAlloc() on Mac.
211  if (asan_init_is_running) {
212    return real_strcmp(s1, s2);
213  }
214  unsigned char c1, c2;
215  size_t i;
216  for (i = 0; ; i++) {
217    c1 = (unsigned char)s1[i];
218    c2 = (unsigned char)s2[i];
219    if (c1 != c2 || c1 == '\0') break;
220  }
221  ASAN_READ_RANGE(s1, i + 1);
222  ASAN_READ_RANGE(s2, i + 1);
223  return CharCmp(c1, c2);
224}
225
226char *WRAP(strcpy)(char *to, const char *from) {  // NOLINT
227  // strcpy is called from malloc_default_purgeable_zone()
228  // in __asan::ReplaceSystemAlloc() on Mac.
229  if (asan_init_is_running) {
230    return real_strcpy(to, from);
231  }
232  ensure_asan_inited();
233  if (FLAG_replace_str) {
234    size_t from_size = real_strlen(from) + 1;
235    CHECK_RANGES_OVERLAP(to, from, from_size);
236    ASAN_READ_RANGE(from, from_size);
237    ASAN_WRITE_RANGE(to, from_size);
238  }
239  return real_strcpy(to, from);
240}
241
242char *WRAP(strdup)(const char *s) {
243  ensure_asan_inited();
244  if (FLAG_replace_str) {
245    size_t length = real_strlen(s);
246    ASAN_READ_RANGE(s, length + 1);
247  }
248  return real_strdup(s);
249}
250
251size_t WRAP(strlen)(const char *s) {
252  // strlen is called from malloc_default_purgeable_zone()
253  // in __asan::ReplaceSystemAlloc() on Mac.
254  if (asan_init_is_running) {
255    return real_strlen(s);
256  }
257  ensure_asan_inited();
258  size_t length = real_strlen(s);
259  if (FLAG_replace_str) {
260    ASAN_READ_RANGE(s, length + 1);
261  }
262  return length;
263}
264
265int WRAP(strncmp)(const char *s1, const char *s2, size_t size) {
266  // strncmp is called from malloc_default_purgeable_zone()
267  // in __asan::ReplaceSystemAlloc() on Mac.
268  if (asan_init_is_running) {
269    return real_strncmp(s1, s2, size);
270  }
271  unsigned char c1 = 0, c2 = 0;
272  size_t i;
273  for (i = 0; i < size; i++) {
274    c1 = (unsigned char)s1[i];
275    c2 = (unsigned char)s2[i];
276    if (c1 != c2 || c1 == '\0') break;
277  }
278  ASAN_READ_RANGE(s1, Min(i + 1, size));
279  ASAN_READ_RANGE(s2, Min(i + 1, size));
280  return CharCmp(c1, c2);
281}
282
283char *WRAP(strncpy)(char *to, const char *from, size_t size) {
284  ensure_asan_inited();
285  if (FLAG_replace_str) {
286    size_t from_size = Min(size, internal_strnlen(from, size) + 1);
287    CHECK_RANGES_OVERLAP(to, from, from_size);
288    ASAN_READ_RANGE(from, from_size);
289    ASAN_WRITE_RANGE(to, size);
290  }
291  return real_strncpy(to, from, size);
292}
293
294#ifndef __APPLE__
295size_t WRAP(strnlen)(const char *s, size_t maxlen) {
296  ensure_asan_inited();
297  size_t length = real_strnlen(s, maxlen);
298  if (FLAG_replace_str) {
299    ASAN_READ_RANGE(s, Min(length + 1, maxlen));
300  }
301  return length;
302}
303#endif
304