//===-- sanitizer_linux.cc ------------------------------------------------===// // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries and implements linux-specific functions from // sanitizer_libc.h. //===----------------------------------------------------------------------===// #ifdef __linux__ #include "sanitizer_common.h" #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" #include #include #include #include #include #include #include #include #include #include #include namespace __sanitizer { // --------------- sanitizer_libc.h void *internal_mmap(void *addr, uptr length, int prot, int flags, int fd, u64 offset) { #if defined __x86_64__ return (void *)syscall(__NR_mmap, addr, length, prot, flags, fd, offset); #else return (void *)syscall(__NR_mmap2, addr, length, prot, flags, fd, offset); #endif } int internal_munmap(void *addr, uptr length) { return syscall(__NR_munmap, addr, length); } int internal_close(fd_t fd) { return syscall(__NR_close, fd); } fd_t internal_open(const char *filename, bool write) { return syscall(__NR_open, filename, write ? O_WRONLY | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660); } uptr internal_read(fd_t fd, void *buf, uptr count) { sptr res; HANDLE_EINTR(res, (sptr)syscall(__NR_read, fd, buf, count)); return res; } uptr internal_write(fd_t fd, const void *buf, uptr count) { sptr res; HANDLE_EINTR(res, (sptr)syscall(__NR_write, fd, buf, count)); return res; } uptr internal_filesize(fd_t fd) { #if defined __x86_64__ struct stat st; if (syscall(__NR_fstat, fd, &st)) return -1; #else struct stat64 st; if (syscall(__NR_fstat64, fd, &st)) return -1; #endif return (uptr)st.st_size; } int internal_dup2(int oldfd, int newfd) { return syscall(__NR_dup2, oldfd, newfd); } uptr internal_readlink(const char *path, char *buf, uptr bufsize) { return (uptr)syscall(__NR_readlink, path, buf, bufsize); } int internal_sched_yield() { return syscall(__NR_sched_yield); } // ----------------- sanitizer_common.h uptr GetTid() { return syscall(__NR_gettid); } void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, uptr *stack_bottom) { static const uptr kMaxThreadStackSize = 256 * (1 << 20); // 256M CHECK(stack_top); CHECK(stack_bottom); if (at_initialization) { // This is the main thread. Libpthread may not be initialized yet. struct rlimit rl; CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0); // Find the mapping that contains a stack variable. MemoryMappingLayout proc_maps; uptr start, end, offset; uptr prev_end = 0; while (proc_maps.Next(&start, &end, &offset, 0, 0)) { if ((uptr)&rl < end) break; prev_end = end; } CHECK((uptr)&rl >= start && (uptr)&rl < end); // Get stacksize from rlimit, but clip it so that it does not overlap // with other mappings. uptr stacksize = rl.rlim_cur; if (stacksize > end - prev_end) stacksize = end - prev_end; // When running with unlimited stack size, we still want to set some limit. // The unlimited stack size is caused by 'ulimit -s unlimited'. // Also, for some reason, GNU make spawns subprocesses with unlimited stack. if (stacksize > kMaxThreadStackSize) stacksize = kMaxThreadStackSize; *stack_top = end; *stack_bottom = end - stacksize; return; } pthread_attr_t attr; CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0); uptr stacksize = 0; void *stackaddr = 0; pthread_attr_getstack(&attr, &stackaddr, (size_t*)&stacksize); pthread_attr_destroy(&attr); *stack_top = (uptr)stackaddr + stacksize; *stack_bottom = (uptr)stackaddr; CHECK(stacksize < kMaxThreadStackSize); // Sanity check. } // Like getenv, but reads env directly from /proc and does not use libc. // This function should be called first inside __asan_init. const char *GetEnv(const char *name) { static char *environ; static uptr len; static bool inited; if (!inited) { inited = true; uptr environ_size; len = ReadFileToBuffer("/proc/self/environ", &environ, &environ_size, 1 << 26); } if (!environ || len == 0) return 0; uptr namelen = internal_strlen(name); const char *p = environ; while (*p != '\0') { // will happen at the \0\0 that terminates the buffer // proc file has the format NAME=value\0NAME=value\0NAME=value\0... const char* endp = (char*)internal_memchr(p, '\0', len - (p - environ)); if (endp == 0) // this entry isn't NUL terminated return 0; else if (!internal_memcmp(p, name, namelen) && p[namelen] == '=') // Match. return p + namelen + 1; // point after = p = endp + 1; } return 0; // Not found. } void ReExec() { static const int kMaxArgv = 100; InternalScopedBuffer argv(kMaxArgv + 1); static char *buff; uptr buff_size = 0; ReadFileToBuffer("/proc/self/cmdline", &buff, &buff_size, 1024 * 1024); argv[0] = buff; int argc, i; for (argc = 1, i = 1; ; i++) { if (buff[i] == 0) { if (buff[i+1] == 0) break; argv[argc] = &buff[i+1]; CHECK_LE(argc, kMaxArgv); // FIXME: make this more flexible. argc++; } } argv[argc] = 0; execv(argv[0], argv.data()); } // ----------------- sanitizer_procmaps.h MemoryMappingLayout::MemoryMappingLayout() { proc_self_maps_buff_len_ = ReadFileToBuffer("/proc/self/maps", &proc_self_maps_buff_, &proc_self_maps_buff_mmaped_size_, 1 << 26); CHECK_GT(proc_self_maps_buff_len_, 0); // internal_write(2, proc_self_maps_buff_, proc_self_maps_buff_len_); Reset(); } MemoryMappingLayout::~MemoryMappingLayout() { UnmapOrDie(proc_self_maps_buff_, proc_self_maps_buff_mmaped_size_); } void MemoryMappingLayout::Reset() { current_ = proc_self_maps_buff_; } // Parse a hex value in str and update str. static uptr ParseHex(char **str) { uptr x = 0; char *s; for (s = *str; ; s++) { char c = *s; uptr v = 0; if (c >= '0' && c <= '9') v = c - '0'; else if (c >= 'a' && c <= 'f') v = c - 'a' + 10; else if (c >= 'A' && c <= 'F') v = c - 'A' + 10; else break; x = x * 16 + v; } *str = s; return x; } static bool IsOnOf(char c, char c1, char c2) { return c == c1 || c == c2; } static bool IsDecimal(char c) { return c >= '0' && c <= '9'; } bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset, char filename[], uptr filename_size) { char *last = proc_self_maps_buff_ + proc_self_maps_buff_len_; if (current_ >= last) return false; uptr dummy; if (!start) start = &dummy; if (!end) end = &dummy; if (!offset) offset = &dummy; char *next_line = (char*)internal_memchr(current_, '\n', last - current_); if (next_line == 0) next_line = last; // Example: 08048000-08056000 r-xp 00000000 03:0c 64593 /foo/bar *start = ParseHex(¤t_); CHECK_EQ(*current_++, '-'); *end = ParseHex(¤t_); CHECK_EQ(*current_++, ' '); CHECK(IsOnOf(*current_++, '-', 'r')); CHECK(IsOnOf(*current_++, '-', 'w')); CHECK(IsOnOf(*current_++, '-', 'x')); CHECK(IsOnOf(*current_++, 's', 'p')); CHECK_EQ(*current_++, ' '); *offset = ParseHex(¤t_); CHECK_EQ(*current_++, ' '); ParseHex(¤t_); CHECK_EQ(*current_++, ':'); ParseHex(¤t_); CHECK_EQ(*current_++, ' '); while (IsDecimal(*current_)) current_++; CHECK_EQ(*current_++, ' '); // Skip spaces. while (current_ < next_line && *current_ == ' ') current_++; // Fill in the filename. uptr i = 0; while (current_ < next_line) { if (filename && i < filename_size - 1) filename[i++] = *current_; current_++; } if (filename && i < filename_size) filename[i] = 0; current_ = next_line + 1; return true; } // Gets the object name and the offset by walking MemoryMappingLayout. bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset, char filename[], uptr filename_size) { return IterateForObjectNameAndOffset(addr, offset, filename, filename_size); } } // namespace __sanitizer #endif // __linux__