static uintptr_t __get_main_stack_startstack() { FILE* fp = fopen("/proc/self/stat", "re"); if (fp == nullptr) { __libc_fatal("couldn't open /proc/self/stat: %s", strerror(errno)); } char line[BUFSIZ]; if (fgets(line, sizeof(line), fp) == nullptr) { __libc_fatal("couldn't read /proc/self/stat: %s", strerror(errno)); } fclose(fp); // See man 5 proc. There's no reason comm can't contain ' ' or ')', // so we search backwards for the end of it. We're looking for this field: // // startstack %lu (28) The address of the start (i.e., bottom) of the stack. uintptr_t startstack = 0; const char* end_of_comm = strrchr(line, ')'); if (sscanf(end_of_comm + 1, " %*c " "%*d %*d %*d %*d %*d " "%*u %*u %*u %*u %*u %*u %*u " "%*d %*d %*d %*d %*d %*d " "%*u %*u %*d %*u %*u %*u %" SCNuPTR, &startstack) != 1) { __libc_fatal("couldn't parse /proc/self/stat"); } return startstack; }
static int __pthread_attr_getstack_main_thread(void** stack_base, size_t* stack_size) { ErrnoRestorer errno_restorer; rlimit stack_limit; if (getrlimit(RLIMIT_STACK, &stack_limit) == -1) { return errno; } // If the current RLIMIT_STACK is RLIM_INFINITY, only admit to an 8MiB stack for sanity's sake. if (stack_limit.rlim_cur == RLIM_INFINITY) { stack_limit.rlim_cur = 8 * 1024 * 1024; } // Ask the kernel where our main thread's stack started. uintptr_t startstack = __get_main_stack_startstack(); // Hunt for the region that contains that address. FILE* fp = fopen("/proc/self/maps", "re"); if (fp == nullptr) { __libc_fatal("couldn't open /proc/self/maps"); } char line[BUFSIZ]; while (fgets(line, sizeof(line), fp) != NULL) { uintptr_t lo, hi; if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR, &lo, &hi) == 2) { if (lo <= startstack && startstack <= hi) { *stack_size = stack_limit.rlim_cur; *stack_base = reinterpret_cast<void*>(hi - *stack_size); fclose(fp); return 0; } } } __libc_fatal("Stack not found in /proc/self/maps"); }
static void faulted (void) { struct { mach_msg_header_t head; char buf[64]; } request; mig_reply_header_t reply; extern int _hurdsig_fault_exc_server (mach_msg_header_t *, mach_msg_header_t *); /* Wait for the exception_raise message forwarded by the proc server. */ if (__mach_msg (&request.head, MACH_RCV_MSG, 0, sizeof request, forward_sigexc, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL) != MACH_MSG_SUCCESS) __libc_fatal ("msg receive failed on signal thread exc\n"); /* Run the exc demuxer which should call the server function above. That function returns 0 if the exception was expected. */ _hurdsig_fault_exc_server (&request.head, &reply.Head); if (reply.Head.msgh_remote_port != MACH_PORT_NULL) __mach_msg (&reply.Head, MACH_SEND_MSG, reply.Head.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); if (reply.RetCode == MIG_BAD_ID) __mach_msg_destroy (&request.head); if (reply.RetCode) __libc_fatal ("BUG: unexpected fault in signal thread\n"); _hurdsig_fault_preemptor.signals = 0; longjmp (_hurdsig_fault_env, 1); }
static void __initialize_personality() { #if !defined(__LP64__) int old_value = personality(0xffffffff); if (old_value == -1) { __libc_fatal("error getting old personality value: %s", strerror(errno)); } if (personality((static_cast<unsigned int>(old_value) & ~PER_MASK) | PER_LINUX32) == -1) { __libc_fatal("error setting PER_LINUX32 personality: %s", strerror(errno)); } #endif }
static void unwind_cleanup (_Unwind_Reason_Code reason, struct _Unwind_Exception *exc) { /* When we get here a C++ catch block didn't rethrow the object. We cannot handle this case and therefore abort. */ __libc_fatal ("FATAL: exception not rethrown\n"); }
static void mabort (enum mcheck_status status) { const char *msg; switch (status) { case MCHECK_OK: msg = _ ("memory is consistent, library is buggy\n"); break; case MCHECK_HEAD: msg = _ ("memory clobbered before allocated block\n"); break; case MCHECK_TAIL: msg = _ ("memory clobbered past end of allocated block\n"); break; case MCHECK_FREE: msg = _ ("block freed twice\n"); break; default: msg = _ ("bogus mcheck_status, library is buggy\n"); break; } #ifdef _LIBC __libc_fatal (msg); #else fprintf (stderr, "mcheck: %s", msg); fflush (stderr); abort (); #endif }
static void init (void) { void *resume, *personality; void *handle; handle = __libc_dlopen (LIBGCC_S_SO); if (handle == NULL || (resume = __libc_dlsym (handle, "_Unwind_Resume")) == NULL || (personality = __libc_dlsym (handle, "__gcc_personality_v0")) == NULL) __libc_fatal (LIBGCC_S_SO " must be installed for pthread_cancel to work\n"); libgcc_s_resume = resume; libgcc_s_personality = personality; atomic_write_barrier (); /* At the point at which any thread writes the handle to libgcc_s_handle, the initialization is complete. The writing of libgcc_s_handle is atomic. All other threads reading libgcc_s_handle do so atomically. Any thread that does not execute this function must issue a read barrier to ensure that all of the above has actually completed and that the values of the function pointers are correct. */ libgcc_s_handle = handle; }
void attribute_hidden _IO_vtable_check (void) { #ifdef SHARED /* Honor the compatibility flag. */ void (*flag) (void) = atomic_load_relaxed (&IO_accept_foreign_vtables); #ifdef PTR_DEMANGLE PTR_DEMANGLE (flag); #endif if (flag == &_IO_vtable_check) return; /* In case this libc copy is in a non-default namespace, we always need to accept foreign vtables because there is always a possibility that FILE * objects are passed across the linking boundary. */ { Dl_info di; struct link_map *l; if (_dl_open_hook != NULL || (_dl_addr (_IO_vtable_check, &di, &l, NULL) != 0 && l->l_ns != LM_ID_BASE)) return; } #else /* !SHARED */ /* We cannot perform vtable validation in the static dlopen case because FILE * handles might be passed back and forth across the boundary. Therefore, we disable checking in this case. */ if (__dlopen != NULL) return; #endif __libc_fatal ("Fatal error: glibc detected an invalid stdio handle\n"); }
void pthread_cancel_init (void) { void *resume, *personality, *forcedunwind, *getcfa; void *handle; if (__builtin_expect (libgcc_s_getcfa != NULL, 1)) return; handle = __libc_dlopen ("libgcc_s.so.1"); if (handle == NULL || (resume = __libc_dlsym (handle, "_Unwind_Resume")) == NULL || (personality = __libc_dlsym (handle, "__gcc_personality_v0")) == NULL || (forcedunwind = __libc_dlsym (handle, "_Unwind_ForcedUnwind")) == NULL || (getcfa = __libc_dlsym (handle, "_Unwind_GetCFA")) == NULL #ifdef ARCH_CANCEL_INIT || ARCH_CANCEL_INIT (handle) #endif ) __libc_fatal ("libgcc_s.so.1 must be installed for pthread_cancel to work\n"); libgcc_s_resume = resume; libgcc_s_personality = personality; libgcc_s_forcedunwind = forcedunwind; libgcc_s_getcfa = getcfa; }
void* LinkerMemoryAllocator::realloc(void* ptr, size_t size) { if (ptr == nullptr) { return alloc(size); } if (size == 0) { free(ptr); return nullptr; } page_info* info = get_page_info(ptr); size_t old_size = 0; if (info->type == kLargeObject) { old_size = info->allocated_size - sizeof(page_info); } else { LinkerSmallObjectAllocator* allocator = get_small_object_allocator(info->type); if (allocator != info->allocator_addr) { __libc_fatal("invalid pointer %p (page signature mismatch)", ptr); } old_size = allocator->get_block_size(); } if (old_size < size) { void *result = alloc(size); memcpy(result, ptr, old_size); free(ptr); return result; } return ptr; }
void LinkerSmallObjectAllocator::alloc_page() { void* map_ptr = mmap(nullptr, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); if (map_ptr == MAP_FAILED) { __libc_fatal("mmap failed"); } prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_ptr, PAGE_SIZE, name_); memset(map_ptr, 0, PAGE_SIZE); page_info* info = reinterpret_cast<page_info*>(map_ptr); memcpy(info->signature, kSignature, sizeof(kSignature)); info->type = type_; info->allocator_addr = this; size_t free_blocks_cnt = (PAGE_SIZE - sizeof(page_info))/block_size_; create_page_record(map_ptr, free_blocks_cnt); small_object_block_record* first_block = reinterpret_cast<small_object_block_record*>(info + 1); first_block->next = free_blocks_list_; first_block->free_blocks_cnt = free_blocks_cnt; free_blocks_list_ = first_block; }
LinkerSmallObjectAllocator* LinkerMemoryAllocator::get_small_object_allocator(uint32_t type) { if (type < kSmallObjectMinSizeLog2 || type > kSmallObjectMaxSizeLog2) { __libc_fatal("invalid type: %u", type); } return &allocators_[type - kSmallObjectMinSizeLog2]; }
void LinkerSmallObjectAllocator::free(void* ptr) { auto page_record = find_page_record(ptr); ssize_t offset = reinterpret_cast<uintptr_t>(ptr) - sizeof(page_info); if (offset % block_size_ != 0) { __libc_fatal("invalid pointer: %p (block_size=%zd)", ptr, block_size_); } memset(ptr, 0, block_size_); small_object_block_record* block_record = reinterpret_cast<small_object_block_record*>(ptr); block_record->next = free_blocks_list_; block_record->free_blocks_cnt = 1; free_blocks_list_ = block_record; page_record->free_blocks_cnt++; page_record->allocated_blocks_cnt--; if (page_record->allocated_blocks_cnt == 0) { if (free_pages_cnt_++ > 1) { // if we already have a free page - unmap this one. free_page(page_record); } } }
page_info* LinkerMemoryAllocator::get_page_info(void* ptr) { page_info* info = reinterpret_cast<page_info*>(PAGE_START(reinterpret_cast<size_t>(ptr))); if (memcmp(info->signature, kSignature, sizeof(kSignature)) != 0) { __libc_fatal("invalid pointer %p (page signature mismatch)", ptr); } return info; }
void __libc_alloc_buffer_create_failure (void *start, size_t size) { char buf[200]; __snprintf (buf, sizeof (buf), "Fatal glibc error: " "invalid allocation buffer of size %zu\n", size); __libc_fatal (buf); }
void __libc_dynarray_at_failure (size_t size, size_t index) { char buf[200]; __snprintf (buf, sizeof (buf), "Fatal glibc error: " "array index %zu not less than array length %zu\n", index, size); __libc_fatal (buf); }
struct hurd_sigstate * _hurd_thread_sigstate (thread_t thread) { struct hurd_sigstate *ss; __mutex_lock (&_hurd_siglock); for (ss = _hurd_sigstates; ss != NULL; ss = ss->next) if (ss->thread == thread) break; if (ss == NULL) { ss = malloc (sizeof (*ss)); if (ss == NULL) __libc_fatal ("hurd: Can't allocate sigstate\n"); ss->thread = thread; __spin_lock_init (&ss->lock); /* Initialize default state. */ __sigemptyset (&ss->blocked); __sigemptyset (&ss->pending); memset (&ss->sigaltstack, 0, sizeof (ss->sigaltstack)); ss->preemptors = NULL; ss->suspended = MACH_PORT_NULL; ss->intr_port = MACH_PORT_NULL; ss->context = NULL; if (thread == MACH_PORT_NULL) { /* Process-wide sigstate, use the system defaults. */ default_sigaction (ss->actions); /* The global sigstate is not added to the _hurd_sigstates list. It is created with _hurd_thread_sigstate (MACH_PORT_NULL) but should be accessed through _hurd_global_sigstate. */ } else { /* Use the global actions as a default for new threads. */ struct hurd_sigstate *s = _hurd_global_sigstate; if (s) { __spin_lock (&s->lock); memcpy (ss->actions, s->actions, sizeof (s->actions)); __spin_unlock (&s->lock); } else default_sigaction (ss->actions); ss->next = _hurd_sigstates; _hurd_sigstates = ss; } } __mutex_unlock (&_hurd_siglock); return ss; }
struct hurd_sigstate * _hurd_thread_sigstate (thread_t thread) { struct hurd_sigstate *ss; __mutex_lock (&_hurd_siglock); for (ss = _hurd_sigstates; ss != NULL; ss = ss->next) if (ss->thread == thread) break; if (ss == NULL) { ss = malloc (sizeof (*ss)); if (ss == NULL) __libc_fatal ("hurd: Can't allocate thread sigstate\n"); ss->thread = thread; __spin_lock_init (&ss->lock); /* Initialize default state. */ __sigemptyset (&ss->blocked); __sigemptyset (&ss->pending); memset (&ss->sigaltstack, 0, sizeof (ss->sigaltstack)); ss->sigaltstack.ss_flags |= SS_DISABLE; ss->preemptors = NULL; ss->suspended = MACH_PORT_NULL; ss->intr_port = MACH_PORT_NULL; ss->context = NULL; /* Initialize the sigaction vector from the default signal receiving thread's state, and its from the system defaults. */ if (thread == _hurd_sigthread) default_sigaction (ss->actions); else { struct hurd_sigstate *s; for (s = _hurd_sigstates; s != NULL; s = s->next) if (s->thread == _hurd_sigthread) break; if (s) { __spin_lock (&s->lock); memcpy (ss->actions, s->actions, sizeof (s->actions)); __spin_unlock (&s->lock); } else default_sigaction (ss->actions); } ss->next = _hurd_sigstates; _hurd_sigstates = ss; } __mutex_unlock (&_hurd_siglock); return ss; }
linker_vector_t::iterator LinkerSmallObjectAllocator::find_page_record(void* ptr) { void* addr = reinterpret_cast<void*>(PAGE_START(reinterpret_cast<uintptr_t>(ptr))); small_object_page_record boundary; boundary.page_addr = addr; linker_vector_t::iterator it = std::lower_bound( page_records_.begin(), page_records_.end(), boundary); if (it == page_records_.end() || it->page_addr != addr) { // not found... __libc_fatal("page record for %p was not found (block_size=%zd)", ptr, block_size_); } return it; }
int __fsetlocking(FILE* fp, int type) { int old_state = _EXT(fp)->_stdio_handles_locking ? FSETLOCKING_INTERNAL : FSETLOCKING_BYCALLER; if (type == FSETLOCKING_QUERY) { return old_state; } if (type != FSETLOCKING_INTERNAL && type != FSETLOCKING_BYCALLER) { // The API doesn't let us report an error, so blow up. __libc_fatal("Bad type (%d) passed to __fsetlocking", type); } _EXT(fp)->_stdio_handles_locking = (type == FSETLOCKING_INTERNAL); return old_state; }
static void init (void) { void *resume, *personality; void *handle; handle = __libc_dlopen ("libgcc_s.so.1"); if (handle == NULL || (resume = __libc_dlsym (handle, "_Unwind_Resume")) == NULL || (personality = __libc_dlsym (handle, "__gcc_personality_v0")) == NULL) __libc_fatal ("libgcc_s.so.1 must be installed for pthread_cancel to work\n"); libgcc_s_resume = resume; libgcc_s_personality = personality; }
void __attribute_noinline__ pthread_cancel_init (void) { void *resume; void *personality; void *forcedunwind; void *getcfa; void *handle; if (__builtin_expect (libgcc_s_handle != NULL, 1)) { /* Force gcc to reload all values. */ asm volatile ("" ::: "memory"); return; } handle = __libc_dlopen (LIBGCC_S_SO); if (handle == NULL || (resume = __libc_dlsym (handle, "_Unwind_Resume")) == NULL || (personality = __libc_dlsym (handle, "__gcc_personality_v0")) == NULL || (forcedunwind = __libc_dlsym (handle, "_Unwind_ForcedUnwind")) == NULL || (getcfa = __libc_dlsym (handle, "_Unwind_GetCFA")) == NULL #ifdef ARCH_CANCEL_INIT || ARCH_CANCEL_INIT (handle) #endif ) __libc_fatal (LIBGCC_S_SO " must be installed for pthread_cancel to work\n"); PTR_MANGLE (resume); libgcc_s_resume = resume; PTR_MANGLE (personality); libgcc_s_personality = personality; PTR_MANGLE (forcedunwind); libgcc_s_forcedunwind = forcedunwind; PTR_MANGLE (getcfa); libgcc_s_getcfa = getcfa; /* Make sure libgcc_s_handle is written last. Otherwise, pthread_cancel_init might return early even when the pointer the caller is interested in is not initialized yet. */ atomic_write_barrier (); libgcc_s_handle = handle; }
const prop_info* __system_property_find_nth(unsigned n) { #if !MB_OMIT_API_VERSION_CHECK if (bionic_get_application_target_sdk_version() >= __ANDROID_API_O__) { __libc_fatal( "__system_property_find_nth is not supported since Android O," " please use __system_property_foreach instead."); } #endif find_nth_cookie cookie(n); const int err = mb__system_property_foreach(find_nth_fn, &cookie); if (err < 0) { return nullptr; } return cookie.pi; }
void LinkerMemoryAllocator::free(void* ptr) { if (ptr == nullptr) { return; } page_info* info = get_page_info(ptr); if (info->type == kLargeObject) { munmap(info, info->allocated_size); } else { LinkerSmallObjectAllocator* allocator = get_small_object_allocator(info->type); if (allocator != info->allocator_addr) { __libc_fatal("invalid pointer %p (invalid allocator address for the page)", ptr); } allocator->free(ptr); } }
void* LinkerMemoryAllocator::alloc_mmap(size_t size) { size_t allocated_size = PAGE_END(size + sizeof(page_info)); void* map_ptr = mmap(nullptr, allocated_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); if (map_ptr == MAP_FAILED) { __libc_fatal("mmap failed"); } prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_ptr, allocated_size, "linker_alloc_lob"); memset(map_ptr, 0, allocated_size); page_info* info = reinterpret_cast<page_info*>(map_ptr); memcpy(info->signature, kSignature, sizeof(kSignature)); info->type = kLargeObject; info->allocated_size = allocated_size; return info + 1; }
/* -1 == not found 0 == adjusted for next function 1 == finished */ int __nss_next2 (service_user **ni, const char *fct_name, const char *fct2_name, void **fctp, int status, int all_values) { if (all_values) { if (nss_next_action (*ni, NSS_STATUS_TRYAGAIN) == NSS_ACTION_RETURN && nss_next_action (*ni, NSS_STATUS_UNAVAIL) == NSS_ACTION_RETURN && nss_next_action (*ni, NSS_STATUS_NOTFOUND) == NSS_ACTION_RETURN && nss_next_action (*ni, NSS_STATUS_SUCCESS) == NSS_ACTION_RETURN) return 1; } else { /* This is really only for debugging. */ if (__builtin_expect (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_RETURN, 0)) __libc_fatal ("illegal status in __nss_next"); if (nss_next_action (*ni, status) == NSS_ACTION_RETURN) return 1; } if ((*ni)->next == NULL) return -1; do { *ni = (*ni)->next; *fctp = __nss_lookup_function (*ni, fct_name); if (*fctp == NULL && fct2_name != NULL) *fctp = __nss_lookup_function (*ni, fct2_name); } while (*fctp == NULL && nss_next_action (*ni, NSS_STATUS_UNAVAIL) == NSS_ACTION_CONTINUE && (*ni)->next != NULL); return *fctp != NULL ? 0 : -1; }
static int __pthread_attr_getstack_main_thread(void** stack_base, size_t* stack_size) { ErrnoRestorer errno_restorer; rlimit stack_limit; if (getrlimit(RLIMIT_STACK, &stack_limit) == -1) { return errno; } // If the current RLIMIT_STACK is RLIM_INFINITY, only admit to an 8MiB stack for sanity's sake. if (stack_limit.rlim_cur == RLIM_INFINITY) { stack_limit.rlim_cur = 8 * 1024 * 1024; } // It shouldn't matter which thread we are because we're just looking for "[stack]", but // valgrind seems to mess with the stack enough that the kernel will report "[stack:pid]" // instead if you look in /proc/self/maps, so we need to look in /proc/pid/task/pid/maps. char path[64]; snprintf(path, sizeof(path), "/proc/self/task/%d/maps", getpid()); FILE* fp = fopen(path, "re"); if (fp == NULL) { return errno; } char line[BUFSIZ]; while (fgets(line, sizeof(line), fp) != NULL) { if (ends_with(line, " [stack]\n")) { uintptr_t lo, hi; if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR, &lo, &hi) == 2) { *stack_size = stack_limit.rlim_cur; *stack_base = reinterpret_cast<void*>(hi - *stack_size); fclose(fp); return 0; } } } __libc_fatal("No [stack] line found in \"%s\"!", path); }
static int internal_getgrouplist (const char *user, gid_t group, long int *size, gid_t **groupsp, long int limit) { #ifdef USE_NSCD if (__nss_not_use_nscd_group > 0 && ++__nss_not_use_nscd_group > NSS_NSCD_RETRY) __nss_not_use_nscd_group = 0; if (!__nss_not_use_nscd_group && !__nss_database_custom[NSS_DBSIDX_group]) { int n = __nscd_getgrouplist (user, group, size, groupsp, limit); if (n >= 0) return n; /* nscd is not usable. */ __nss_not_use_nscd_group = 1; } #endif enum nss_status status = NSS_STATUS_UNAVAIL; int no_more = 0; /* Never store more than the starting *SIZE number of elements. */ assert (*size > 0); (*groupsp)[0] = group; /* Start is one, because we have the first group as parameter. */ long int start = 1; if (__nss_initgroups_database == NULL) { if (__nss_database_lookup ("initgroups", NULL, "", &__nss_initgroups_database) < 0) { if (__nss_group_database == NULL) no_more = __nss_database_lookup ("group", NULL, "compat files", &__nss_group_database); __nss_initgroups_database = __nss_group_database; } else use_initgroups_entry = true; } else /* __nss_initgroups_database might have been set through __nss_configure_lookup in which case use_initgroups_entry was not set here. */ use_initgroups_entry = __nss_initgroups_database != __nss_group_database; service_user *nip = __nss_initgroups_database; while (! no_more) { long int prev_start = start; initgroups_dyn_function fct = __nss_lookup_function (nip, "initgroups_dyn"); if (fct == NULL) status = compat_call (nip, user, group, &start, size, groupsp, limit, &errno); else status = DL_CALL_FCT (fct, (user, group, &start, size, groupsp, limit, &errno)); /* Remove duplicates. */ long int cnt = prev_start; while (cnt < start) { long int inner; for (inner = 0; inner < prev_start; ++inner) if ((*groupsp)[inner] == (*groupsp)[cnt]) break; if (inner < prev_start) (*groupsp)[cnt] = (*groupsp)[--start]; else ++cnt; } /* This is really only for debugging. */ if (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_RETURN) __libc_fatal ("illegal status in internal_getgrouplist"); /* For compatibility reason we will continue to look for more entries using the next service even though data has already been found if the nsswitch.conf file contained only a 'groups' line and no 'initgroups' line. If the latter is available we always respect the status. This means that the default for successful lookups is to return. */ if ((use_initgroups_entry || status != NSS_STATUS_SUCCESS) && nss_next_action (nip, status) == NSS_ACTION_RETURN) break; if (nip->next == NULL) no_more = -1; else nip = nip->next; } return start; }
static void __bionic_heap_usage_error(const char* function, void* address) { __libc_fatal("@@@ ABORTING: invalid address or address of corrupt block %p passed to %s", address, function); // So that we can get a memory dump around the specific address. *((int**) 0xdeadbaad) = (int*) address; }
static void __bionic_heap_corruption_error(const char* function) { __libc_fatal("@@@ ABORTING: heap corruption detected by %s", function); }