91 lines
2.4 KiB
C
91 lines
2.4 KiB
C
|
#include "tests/internal.h"
|
||
|
#include <malloc.h>
|
||
|
#include <stdarg.h>
|
||
|
|
||
|
// Internal prototypes / static symbols.
|
||
|
static void my_malloc_init_hook(void);
|
||
|
static void *my_malloc_hook(size_t size, const void *caller);
|
||
|
static void *old_malloc_hook;
|
||
|
static int hook_dyn_fail;
|
||
|
static int hook_status;
|
||
|
|
||
|
//
|
||
|
// my_malloc_hook()
|
||
|
// This function will replace the current malloc hook by our own
|
||
|
// hook (which can return NULL if we want, it allows us to check
|
||
|
// the behavior of our code).
|
||
|
//
|
||
|
// NOTE:
|
||
|
// "man __malloc_hook" indicate to use "__malloc_initialize_hook"
|
||
|
// which will execute the initialization routine. But from glibc 2.24
|
||
|
// onwards, this variable has been removed from the API.
|
||
|
// So, this is why we use "constructor" attribute to call our routine
|
||
|
// at the beginning of the test.
|
||
|
//
|
||
|
__attribute__((constructor))
|
||
|
static void my_malloc_init_hook(void)
|
||
|
{
|
||
|
old_malloc_hook = __malloc_hook;
|
||
|
__malloc_hook = my_malloc_hook;
|
||
|
hook_status = FAIL_NONE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// my_malloc_hook()
|
||
|
// For some tests we need to simulate (properly) a malloc fail; to do this
|
||
|
// we use a malloc hook and add static variables to force him to return
|
||
|
// NULL address.
|
||
|
//
|
||
|
// NOTE:
|
||
|
// All hooks of the glibc will be removed because of their poor behavior
|
||
|
// in multi threaded environment.
|
||
|
// But recently we have seen that the malloc() function is defined by the
|
||
|
// weak symbol, so it is probably possible to override malloc() and do
|
||
|
// the same job.
|
||
|
//
|
||
|
static void *my_malloc_hook(size_t size, const void *caller)
|
||
|
{
|
||
|
void *result;
|
||
|
|
||
|
if (hook_status != FAIL_NONE && hook_status != FAIL_NEXT_DYN){
|
||
|
if (hook_status == FAIL_NEXT)
|
||
|
hook_status = FAIL_NONE;
|
||
|
return (NULL);
|
||
|
}
|
||
|
if (hook_status == FAIL_NEXT_DYN){
|
||
|
hook_dyn_fail -= 1;
|
||
|
if (hook_dyn_fail <= 0){
|
||
|
hook_status = FAIL_NONE;
|
||
|
return (NULL);
|
||
|
}
|
||
|
}
|
||
|
__malloc_hook = old_malloc_hook;
|
||
|
result = malloc(size);
|
||
|
(void)caller;
|
||
|
//printf("caller: %p - area: %p\n", caller, result);
|
||
|
old_malloc_hook = __malloc_hook;
|
||
|
__malloc_hook = my_malloc_hook;
|
||
|
return (result);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// malloc_hook_udpate()
|
||
|
// This function is the only way to interact with the hook; it will
|
||
|
// define the behavior of malloc:
|
||
|
// * FAIL_NEXT : force the next call of malloc to return NULL.
|
||
|
// * FAIL_ALWAYS : force all malloc return NULL.
|
||
|
// * NORMAL : force malloc to work normally.
|
||
|
//
|
||
|
void malloc_hook_update(status_t status, ...)
|
||
|
{
|
||
|
va_list ap;
|
||
|
|
||
|
hook_dyn_fail = 0;
|
||
|
if (status == FAIL_NEXT_DYN){
|
||
|
va_start(ap, status);
|
||
|
hook_dyn_fail = va_arg(ap, int);
|
||
|
va_end(ap);
|
||
|
}
|
||
|
hook_status = status;
|
||
|
}
|