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