asan_interceptors.cc revision de496f451bce322b6cde100456591f1f50ab3477
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 <ctype.h> 24#include <dlfcn.h> 25#include <string.h> 26#include <strings.h> 27 28namespace __asan { 29 30index_f real_index; 31memcmp_f real_memcmp; 32memcpy_f real_memcpy; 33memmove_f real_memmove; 34memset_f real_memset; 35strcasecmp_f real_strcasecmp; 36strcat_f real_strcat; 37strchr_f real_strchr; 38strcmp_f real_strcmp; 39strcpy_f real_strcpy; 40strdup_f real_strdup; 41strlen_f real_strlen; 42strncasecmp_f real_strncasecmp; 43strncmp_f real_strncmp; 44strncpy_f real_strncpy; 45strnlen_f real_strnlen; 46 47// Instruments read/write access to a single byte in memory. 48// On error calls __asan_report_error, which aborts the program. 49__attribute__((noinline)) 50static void AccessAddress(uintptr_t address, bool isWrite) { 51 if (__asan_address_is_poisoned((void*)address)) { 52 GET_BP_PC_SP; 53 __asan_report_error(pc, bp, sp, address, isWrite, /* access_size */ 1); 54 } 55} 56 57// We implement ACCESS_MEMORY_RANGE, ASAN_READ_RANGE, 58// and ASAN_WRITE_RANGE as macro instead of function so 59// that no extra frames are created, and stack trace contains 60// relevant information only. 61 62// Instruments read/write access to a memory range. 63// More complex implementation is possible, for now just 64// checking the first and the last byte of a range. 65#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \ 66 if (size > 0) { \ 67 uintptr_t ptr = (uintptr_t)(offset); \ 68 AccessAddress(ptr, isWrite); \ 69 AccessAddress(ptr + (size) - 1, isWrite); \ 70 } \ 71} while (0) 72 73#define ASAN_READ_RANGE(offset, size) do { \ 74 ACCESS_MEMORY_RANGE(offset, size, false); \ 75} while (0) 76 77#define ASAN_WRITE_RANGE(offset, size) do { \ 78 ACCESS_MEMORY_RANGE(offset, size, true); \ 79} while (0) 80 81// Behavior of functions like "memcpy" or "strcpy" is undefined 82// if memory intervals overlap. We report error in this case. 83// Macro is used to avoid creation of new frames. 84static inline bool RangesOverlap(const char *offset1, size_t length1, 85 const char *offset2, size_t length2) { 86 return !((offset1 + length1 <= offset2) || (offset2 + length2 <= offset1)); 87} 88#define CHECK_RANGES_OVERLAP(name, _offset1, length1, _offset2, length2) do { \ 89 const char *offset1 = (const char*)_offset1; \ 90 const char *offset2 = (const char*)_offset2; \ 91 if (RangesOverlap(offset1, length1, offset2, length2)) { \ 92 Report("ERROR: AddressSanitizer %s-param-overlap: " \ 93 "memory ranges [%p,%p) and [%p, %p) overlap\n", \ 94 name, offset1, offset1 + length1, offset2, offset2 + length2); \ 95 PRINT_CURRENT_STACK(); \ 96 ShowStatsAndAbort(); \ 97 } \ 98} while (0) 99 100#define ENSURE_ASAN_INITED() do { \ 101 CHECK(!asan_init_is_running); \ 102 if (!asan_inited) { \ 103 __asan_init(); \ 104 } \ 105} while (0) 106 107size_t internal_strlen(const char *s) { 108 size_t i = 0; 109 while (s[i]) i++; 110 return i; 111} 112 113size_t internal_strnlen(const char *s, size_t maxlen) { 114 if (real_strnlen != NULL) { 115 return real_strnlen(s, maxlen); 116 } 117 size_t i = 0; 118 while (i < maxlen && s[i]) i++; 119 return i; 120} 121 122void* internal_memchr(const void* s, int c, size_t n) { 123 const char* t = (char*)s; 124 for (size_t i = 0; i < n; ++i, ++t) 125 if (*t == c) 126 return (void*)t; 127 return NULL; 128} 129 130int internal_memcmp(const void* s1, const void* s2, size_t n) { 131 const char* t1 = (char*)s1; 132 const char* t2 = (char*)s2; 133 for (size_t i = 0; i < n; ++i, ++t1, ++t2) 134 if (*t1 != *t2) 135 return *t1 < *t2 ? -1 : 1; 136 return 0; 137} 138 139void InitializeAsanInterceptors() { 140#ifndef __APPLE__ 141 INTERCEPT_FUNCTION(index); 142#else 143 OVERRIDE_FUNCTION(index, WRAP(strchr)); 144#endif 145 INTERCEPT_FUNCTION(memcmp); 146 INTERCEPT_FUNCTION(memcpy); 147 INTERCEPT_FUNCTION(memmove); 148 INTERCEPT_FUNCTION(memset); 149 INTERCEPT_FUNCTION(strcasecmp); 150 INTERCEPT_FUNCTION(strcat); // NOLINT 151 INTERCEPT_FUNCTION(strchr); 152 INTERCEPT_FUNCTION(strcmp); 153 INTERCEPT_FUNCTION(strcpy); // NOLINT 154 INTERCEPT_FUNCTION(strdup); 155 INTERCEPT_FUNCTION(strlen); 156 INTERCEPT_FUNCTION(strncasecmp); 157 INTERCEPT_FUNCTION(strncmp); 158 INTERCEPT_FUNCTION(strncpy); 159#ifndef __APPLE__ 160 INTERCEPT_FUNCTION(strnlen); 161#endif 162 if (FLAG_v > 0) { 163 Printf("AddressSanitizer: libc interceptors initialized\n"); 164 } 165} 166 167} // namespace __asan 168 169// ---------------------- Wrappers ---------------- {{{1 170using namespace __asan; // NOLINT 171 172static inline int CharCmp(unsigned char c1, unsigned char c2) { 173 return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1; 174} 175 176static inline int CharCaseCmp(unsigned char c1, unsigned char c2) { 177 int c1_low = tolower(c1); 178 int c2_low = tolower(c2); 179 return c1_low - c2_low; 180} 181 182int WRAP(memcmp)(const void *a1, const void *a2, size_t size) { 183 ENSURE_ASAN_INITED(); 184 unsigned char c1 = 0, c2 = 0; 185 const unsigned char *s1 = (const unsigned char*)a1; 186 const unsigned char *s2 = (const unsigned char*)a2; 187 size_t i; 188 for (i = 0; i < size; i++) { 189 c1 = s1[i]; 190 c2 = s2[i]; 191 if (c1 != c2) break; 192 } 193 ASAN_READ_RANGE(s1, Min(i + 1, size)); 194 ASAN_READ_RANGE(s2, Min(i + 1, size)); 195 return CharCmp(c1, c2); 196} 197 198void *WRAP(memcpy)(void *to, const void *from, size_t size) { 199 // memcpy is called during __asan_init() from the internals 200 // of printf(...). 201 if (asan_init_is_running) { 202 return real_memcpy(to, from, size); 203 } 204 ENSURE_ASAN_INITED(); 205 if (FLAG_replace_intrin) { 206 CHECK_RANGES_OVERLAP("memcpy", to, size, from, size); 207 ASAN_WRITE_RANGE(from, size); 208 ASAN_READ_RANGE(to, size); 209 } 210 return real_memcpy(to, from, size); 211} 212 213void *WRAP(memmove)(void *to, const void *from, size_t size) { 214 ENSURE_ASAN_INITED(); 215 if (FLAG_replace_intrin) { 216 ASAN_WRITE_RANGE(from, size); 217 ASAN_READ_RANGE(to, size); 218 } 219 return real_memmove(to, from, size); 220} 221 222void *WRAP(memset)(void *block, int c, size_t size) { 223 // memset is called inside INTERCEPT_FUNCTION on Mac. 224 if (asan_init_is_running) { 225 return real_memset(block, c, size); 226 } 227 ENSURE_ASAN_INITED(); 228 if (FLAG_replace_intrin) { 229 ASAN_WRITE_RANGE(block, size); 230 } 231 return real_memset(block, c, size); 232} 233 234// Note that on Linux index and strchr are definined differently depending on 235// the compiler (gcc vs clang). 236// see __CORRECT_ISO_CPP_STRING_H_PROTO in /usr/include/string.h 237 238#ifndef __APPLE__ 239char *WRAP(index)(const char *str, int c) 240 __attribute__((alias(WRAPPER_NAME(strchr)))); 241#endif 242 243char *WRAP(strchr)(const char *str, int c) { 244 ENSURE_ASAN_INITED(); 245 char *result = real_strchr(str, c); 246 if (FLAG_replace_str) { 247 size_t bytes_read = (result ? result - str : real_strlen(str)) + 1; 248 ASAN_READ_RANGE(str, bytes_read); 249 } 250 return result; 251} 252 253int WRAP(strcasecmp)(const char *s1, const char *s2) { 254 ENSURE_ASAN_INITED(); 255 unsigned char c1, c2; 256 size_t i; 257 for (i = 0; ; i++) { 258 c1 = (unsigned char)s1[i]; 259 c2 = (unsigned char)s2[i]; 260 if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; 261 } 262 ASAN_READ_RANGE(s1, i + 1); 263 ASAN_READ_RANGE(s2, i + 1); 264 return CharCaseCmp(c1, c2); 265} 266 267char *WRAP(strcat)(char *to, const char *from) { // NOLINT 268 ENSURE_ASAN_INITED(); 269 if (FLAG_replace_str) { 270 size_t from_length = real_strlen(from); 271 ASAN_READ_RANGE(from, from_length + 1); 272 if (from_length > 0) { 273 size_t to_length = real_strlen(to); 274 ASAN_READ_RANGE(to, to_length); 275 ASAN_WRITE_RANGE(to + to_length, from_length + 1); 276 CHECK_RANGES_OVERLAP("strcat", to, to_length + 1, from, from_length + 1); 277 } 278 } 279 return real_strcat(to, from); 280} 281 282int WRAP(strcmp)(const char *s1, const char *s2) { 283 // strcmp is called from malloc_default_purgeable_zone() 284 // in __asan::ReplaceSystemAlloc() on Mac. 285 if (asan_init_is_running) { 286 return real_strcmp(s1, s2); 287 } 288 unsigned char c1, c2; 289 size_t i; 290 for (i = 0; ; i++) { 291 c1 = (unsigned char)s1[i]; 292 c2 = (unsigned char)s2[i]; 293 if (c1 != c2 || c1 == '\0') break; 294 } 295 ASAN_READ_RANGE(s1, i + 1); 296 ASAN_READ_RANGE(s2, i + 1); 297 return CharCmp(c1, c2); 298} 299 300char *WRAP(strcpy)(char *to, const char *from) { // NOLINT 301 // strcpy is called from malloc_default_purgeable_zone() 302 // in __asan::ReplaceSystemAlloc() on Mac. 303 if (asan_init_is_running) { 304 return real_strcpy(to, from); 305 } 306 ENSURE_ASAN_INITED(); 307 if (FLAG_replace_str) { 308 size_t from_size = real_strlen(from) + 1; 309 CHECK_RANGES_OVERLAP("strcpy", to, from_size, from, from_size); 310 ASAN_READ_RANGE(from, from_size); 311 ASAN_WRITE_RANGE(to, from_size); 312 } 313 return real_strcpy(to, from); 314} 315 316char *WRAP(strdup)(const char *s) { 317 ENSURE_ASAN_INITED(); 318 if (FLAG_replace_str) { 319 size_t length = real_strlen(s); 320 ASAN_READ_RANGE(s, length + 1); 321 } 322 return real_strdup(s); 323} 324 325size_t WRAP(strlen)(const char *s) { 326 // strlen is called from malloc_default_purgeable_zone() 327 // in __asan::ReplaceSystemAlloc() on Mac. 328 if (asan_init_is_running) { 329 return real_strlen(s); 330 } 331 ENSURE_ASAN_INITED(); 332 size_t length = real_strlen(s); 333 if (FLAG_replace_str) { 334 ASAN_READ_RANGE(s, length + 1); 335 } 336 return length; 337} 338 339int WRAP(strncasecmp)(const char *s1, const char *s2, size_t size) { 340 ENSURE_ASAN_INITED(); 341 unsigned char c1 = 0, c2 = 0; 342 size_t i; 343 for (i = 0; i < size; i++) { 344 c1 = (unsigned char)s1[i]; 345 c2 = (unsigned char)s2[i]; 346 if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; 347 } 348 ASAN_READ_RANGE(s1, Min(i + 1, size)); 349 ASAN_READ_RANGE(s2, Min(i + 1, size)); 350 return CharCaseCmp(c1, c2); 351} 352 353int WRAP(strncmp)(const char *s1, const char *s2, size_t size) { 354 // strncmp is called from malloc_default_purgeable_zone() 355 // in __asan::ReplaceSystemAlloc() on Mac. 356 if (asan_init_is_running) { 357 return real_strncmp(s1, s2, size); 358 } 359 unsigned char c1 = 0, c2 = 0; 360 size_t i; 361 for (i = 0; i < size; i++) { 362 c1 = (unsigned char)s1[i]; 363 c2 = (unsigned char)s2[i]; 364 if (c1 != c2 || c1 == '\0') break; 365 } 366 ASAN_READ_RANGE(s1, Min(i + 1, size)); 367 ASAN_READ_RANGE(s2, Min(i + 1, size)); 368 return CharCmp(c1, c2); 369} 370 371char *WRAP(strncpy)(char *to, const char *from, size_t size) { 372 ENSURE_ASAN_INITED(); 373 if (FLAG_replace_str) { 374 size_t from_size = Min(size, internal_strnlen(from, size) + 1); 375 CHECK_RANGES_OVERLAP("strncpy", to, from_size, from, from_size); 376 ASAN_READ_RANGE(from, from_size); 377 ASAN_WRITE_RANGE(to, size); 378 } 379 return real_strncpy(to, from, size); 380} 381 382#ifndef __APPLE__ 383size_t WRAP(strnlen)(const char *s, size_t maxlen) { 384 ENSURE_ASAN_INITED(); 385 size_t length = real_strnlen(s, maxlen); 386 if (FLAG_replace_str) { 387 ASAN_READ_RANGE(s, Min(length + 1, maxlen)); 388 } 389 return length; 390} 391#endif 392