blob: 650d2f6482afa80b4fcc267ed0e08e93dc4b1bfc [file] [log] [blame]
/**
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include "memutils.h"
void exit_handler(void) {
size_t page_size = getpagesize();
for (int i = 0; i < s_mem_map_index; i++) {
if (NULL != s_mem_map[i].start_ptr) {
ENABLE_MEM_ACCESS(s_mem_map[i].start_ptr,
(s_mem_map[i].num_pages * page_size));
}
}
#ifdef CHECK_USE_AFTER_FREE_WITH_WINDOW_SIZE
for (int i = 0; i < MAX_ENTRIES; i++) {
if (NULL != s_free_list[i].start_ptr) {
ENABLE_MEM_ACCESS(s_free_list[i].start_ptr,
(s_free_list[i].num_pages * page_size));
real_free(s_free_list[i].start_ptr);
memset(&s_free_list[i], 0, sizeof(map_struct_t));
}
}
#endif /* CHECK_USE_AFTER_FREE_WITH_WINDOW_SIZE */
}
void sigsegv_handler(int signum, siginfo_t *info, void* context) {
exit_handler();
(*old_sa.sa_sigaction)(signum, info, context);
}
void sighandler_init(void) {
sigemptyset(&new_sa.sa_mask);
new_sa.sa_flags = SA_SIGINFO;
new_sa.sa_sigaction = sigsegv_handler;
sigaction(SIGSEGV, &new_sa, &old_sa);
}
void memutils_init(void) {
real_memalign = dlsym(RTLD_NEXT, "memalign");
if (NULL == real_memalign) {
return;
}
real_calloc = dlsym(RTLD_NEXT, "calloc");
if (NULL == real_calloc) {
return;
}
real_malloc = dlsym(RTLD_NEXT, "malloc");
if (NULL == real_malloc) {
return;
}
real_realloc = dlsym(RTLD_NEXT, "realloc");
if (NULL == real_realloc) {
return;
}
real_free = dlsym(RTLD_NEXT, "free");
if (NULL == real_free) {
return;
}
memset(&s_mem_map, 0, MAX_ENTRIES * sizeof(map_struct_t));
sighandler_init();
atexit(exit_handler);
s_memutils_initialized = 1;
}
void *memalign(size_t alignment, size_t size) {
if (s_memutils_initialized == 0) {
memutils_init();
}
#ifdef ENABLE_SELECTIVE_OVERLOADING
if ((enable_selective_overload & ENABLE_MEMALIGN_CHECK) != ENABLE_MEMALIGN_CHECK) {
return real_memalign(alignment, size);
}
#endif /* ENABLE_SELECTIVE_OVERLOADING */
char* start_ptr;
char* mem_ptr;
size_t total_size;
size_t aligned_size = size;
size_t num_pages;
size_t page_size = getpagesize();
/* User specified alignment is not respected and is overridden by
* "new_alignment". This is required to catch OOB read when read offset is
* less than user specified alignment. "new_alignment" is derived based on
* size_t, and helps to avoid bus errors due to non-aligned memory.
* "new_alignment", whenever used, is checked to ensure sizeof(size_t)
* has returned proper value */
size_t new_alignment = sizeof(size_t);
if (s_mem_map_index == MAX_ENTRIES) {
return real_memalign(alignment, size);
}
if (alignment > page_size) {
return real_memalign(alignment, size);
}
if ((0 == page_size) || (0 == alignment) || (0 == size)
|| (0 == new_alignment)) {
return real_memalign(alignment, size);
}
#ifdef CHECK_OVERFLOW
if (0 != (size % new_alignment)) {
aligned_size = size + (new_alignment - (size % new_alignment));
}
#endif
if (0 != (aligned_size % page_size)) {
num_pages = (aligned_size / page_size) + 2;
} else {
num_pages = (aligned_size / page_size) + 1;
}
total_size = (num_pages * page_size);
start_ptr = (char *) real_memalign(page_size, total_size);
#ifdef CHECK_OVERFLOW
#ifdef FORCE_UNALIGN
mem_ptr = (char *) start_ptr + ((num_pages - 1) * page_size) - size;
#else
mem_ptr = (char *) start_ptr + ((num_pages - 1) * page_size) - aligned_size;
#endif /* FORCE_UNALIGN */
DISABLE_MEM_ACCESS((start_ptr + ((num_pages - 1) * page_size)), page_size);
#endif /* CHECK_OVERFLOW */
#ifdef CHECK_UNDERFLOW
mem_ptr = (char *) start_ptr + page_size;
DISABLE_MEM_ACCESS(start_ptr, page_size);
#endif /* CHECK_UNDERFLOW */
s_mem_map[s_mem_map_index].start_ptr = start_ptr;
s_mem_map[s_mem_map_index].mem_ptr = mem_ptr;
s_mem_map[s_mem_map_index].num_pages = num_pages;
s_mem_map[s_mem_map_index].mem_size = size;
s_mem_map_index++;
memset(mem_ptr, INITIAL_VAL, size);
return mem_ptr;
}
void *malloc(size_t size) {
if (s_memutils_initialized == 0) {
memutils_init();
}
#ifdef ENABLE_SELECTIVE_OVERLOADING
if ((enable_selective_overload & ENABLE_MALLOC_CHECK) != ENABLE_MALLOC_CHECK) {
return real_malloc(size);
}
#endif /* ENABLE_SELECTIVE_OVERLOADING */
return memalign(sizeof(size_t), size);
}
void *calloc(size_t nitems, size_t size) {
if (s_memutils_initialized == 0) {
memutils_init();
}
#ifdef ENABLE_SELECTIVE_OVERLOADING
if ((enable_selective_overload & ENABLE_CALLOC_CHECK) != ENABLE_CALLOC_CHECK) {
return real_calloc(nitems, size);
}
#endif /* ENABLE_SELECTIVE_OVERLOADING */
void *ptr = memalign(sizeof(size_t), (nitems * size));
if (ptr)
memset(ptr, 0, (nitems * size));
return ptr;
}
void *realloc(void *ptr, size_t size) {
if (s_memutils_initialized == 0) {
memutils_init();
}
#ifdef ENABLE_SELECTIVE_OVERLOADING
if ((enable_selective_overload & ENABLE_REALLOC_CHECK) != ENABLE_REALLOC_CHECK) {
return real_realloc(ptr, size);
}
#endif /* ENABLE_SELECTIVE_OVERLOADING */
if (ptr != NULL) {
int i = 0;
for (i = 0; i < s_mem_map_index; i++) {
if (ptr == s_mem_map[i].mem_ptr) {
void* temp = malloc(size);
if (temp == NULL) {
return NULL;
}
if (s_mem_map[i].mem_size > size) {
memcpy(temp, ptr, size);
} else {
memcpy(temp, ptr, s_mem_map[i].mem_size);
}
free(s_mem_map[i].mem_ptr);
return temp;
}
}
}
return real_realloc(ptr, size);
}
void free(void *ptr) {
if (s_memutils_initialized == 0) {
memutils_init();
}
#ifdef ENABLE_SELECTIVE_OVERLOADING
if ((enable_selective_overload & ENABLE_FREE_CHECK) != ENABLE_FREE_CHECK) {
return real_free(ptr);
}
#endif /* ENABLE_SELECTIVE_OVERLOADING */
if (ptr != NULL) {
int i = 0;
size_t page_size = getpagesize();
for (i = 0; i < s_mem_map_index; i++) {
if (ptr == s_mem_map[i].mem_ptr) {
#ifdef CHECK_USE_AFTER_FREE_WITH_WINDOW_SIZE
s_free_list[s_free_write_index].start_ptr =
s_mem_map[i].start_ptr;
s_free_list[s_free_write_index].mem_ptr = s_mem_map[i].mem_ptr;
s_free_list[s_free_write_index].num_pages =
s_mem_map[i].num_pages;
s_free_list[s_free_write_index].mem_size = s_mem_map[i].mem_size;
s_free_write_index++;
s_free_list_size += s_mem_map[i].mem_size;
DISABLE_MEM_ACCESS(s_mem_map[i].start_ptr,
(s_mem_map[i].num_pages * page_size));
memset(&s_mem_map[i], 0, sizeof(map_struct_t));
while (s_free_list_size > CHECK_USE_AFTER_FREE_WITH_WINDOW_SIZE) {
ENABLE_MEM_ACCESS(
s_free_list[s_free_read_index].start_ptr,
(s_free_list[s_free_read_index].num_pages * page_size));
real_free(s_free_list[s_free_read_index].start_ptr);
s_free_list_size -= s_free_list[s_free_read_index].mem_size;
memset(&s_free_list[s_free_read_index], 0,
sizeof(map_struct_t));
s_free_read_index++;
if ((s_free_read_index == MAX_ENTRIES)
|| (s_free_read_index >= s_free_write_index)) {
break;
}
}
return;
#else
ENABLE_MEM_ACCESS(s_mem_map[i].start_ptr,
(s_mem_map[i].num_pages * page_size));
real_free(s_mem_map[i].start_ptr);
memset(&s_mem_map[i], 0, sizeof(map_struct_t));
return;
#endif /* CHECK_USE_AFTER_FREE_WITH_WINDOW_SIZE */
}
}
}
real_free(ptr);
return;
}