Skip to content

Commit

Permalink
Added poisoning, integrity check, tests
Browse files Browse the repository at this point in the history
To enable integrity check, build with `-DUMM_INTEGRITY_CHECK`.

To enable poisoning, build with `-DUMM_POISON`. You may see other
possible adjustments in `umm_malloc_cfg_example.h`

Plus, added tests, build and run with:

  `make -C test`
  • Loading branch information
dimonomid committed Jan 26, 2016
1 parent ccceaac commit e497d24
Show file tree
Hide file tree
Showing 6 changed files with 892 additions and 137 deletions.
33 changes: 33 additions & 0 deletions test/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

all: test test_poison test_integrity test_poison_integrity

INCDIRS = -I.. -I.

test:
@echo NORMAL
gcc --std=c99 $(CFLAGS) $(INCDIRS) -g3 -m32 \
../umm_malloc.c umm_malloc_test.c \
-o test_umm
./test_umm

test_poison:
@echo POISON
gcc --std=c99 $(CFLAGS) $(INCDIRS) -DUMM_POISON -g3 -m32 \
../umm_malloc.c umm_malloc_test.c \
-o test_umm
./test_umm

test_integrity:
@echo INTEGRITY
gcc --std=c99 $(CFLAGS) $(INCDIRS) -DUMM_INTEGRITY_CHECK -g3 -m32 \
../umm_malloc.c umm_malloc_test.c \
-o test_umm
./test_umm

test_poison_integrity:
@echo POISON + INTEGRITY
gcc --std=c99 $(CFLAGS) $(INCDIRS) -DUMM_POISON -DUMM_INTEGRITY_CHECK -g3 -m32 \
../umm_malloc.c umm_malloc_test.c \
-o test_umm
./test_umm

140 changes: 140 additions & 0 deletions test/umm_malloc_cfg.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* Copyright (c) 2016 Cesanta Software Limited
* All rights reserved
*/

/*
* Smartjs-specific configuration for umm_malloc
*/

#ifndef _UMM_MALLOC_CFG_H
#define _UMM_MALLOC_CFG_H

/*
* There are a number of defines you can set at compile time that affect how
* the memory allocator will operate.
* You can set them in your config file umm_malloc_cfg.h.
* In GNU C, you also can set these compile time defines like this:
*
* -D UMM_TEST_MAIN
*
* Set this if you want to compile in the test suite at the end of this file.
*
* If you leave this define unset, then you might want to set another one:
*
* -D UMM_REDEFINE_MEM_FUNCTIONS
*
* If you leave this define unset, then the function names are left alone as
* umm_malloc() umm_free() and umm_realloc() so that they cannot be confused
* with the C runtime functions malloc() free() and realloc()
*
* If you do set this define, then the function names become malloc()
* free() and realloc() so that they can be used as the C runtime functions
* in an embedded environment.
*
* -D UMM_BEST_FIT (defualt)
*
* Set this if you want to use a best-fit algorithm for allocating new
* blocks
*
* -D UMM_FIRST_FIT
*
* Set this if you want to use a first-fit algorithm for allocating new
* blocks
*
* -D UMM_DBG_LOG_LEVEL=n
*
* Set n to a value from 0 to 6 depending on how verbose you want the debug
* log to be
*
* ----------------------------------------------------------------------------
*
* Support for this library in a multitasking environment is provided when
* you add bodies to the UMM_CRITICAL_ENTRY and UMM_CRITICAL_EXIT macros
* (see below)
*
* ----------------------------------------------------------------------------
*/

extern char test_umm_heap[];
extern void umm_corruption(void);

/* Start and end addresses of the heap */
#define UMM_MALLOC_CFG__HEAP_ADDR (test_umm_heap)
#define UMM_MALLOC_CFG__HEAP_SIZE 0x10000

/* A couple of macros to make packing structures less compiler dependent */

#define UMM_H_ATTPACKPRE
#define UMM_H_ATTPACKSUF __attribute__((__packed__))

/*
* Callback that is called whenever a heap corruption is detected
*/
#define UMM_HEAP_CORRUPTION_CB() umm_corruption();

/*
* A couple of macros to make it easier to protect the memory allocator
* in a multitasking system. You should set these macros up to use whatever
* your system uses for this purpose. You can disable interrupts entirely, or
* just disable task switching - it's up to you
*
* NOTE WELL that these macros MUST be allowed to nest, because umm_free() is
* called from within umm_malloc()
*/

#define UMM_CRITICAL_ENTRY()
#define UMM_CRITICAL_EXIT()

/*
* -D UMM_INTEGRITY_CHECK :
*
* Enables heap integrity check before any heap operation. It affects
* performance, but does NOT consume extra memory.
*
* If integrity violation is detected, the message is printed and user-provided
* callback is called: `UMM_HEAP_CORRUPTION_CB()`
*
* Note that not all buffer overruns are detected: each buffer is aligned by
* 4 bytes, so there might be some trailing "extra" bytes which are not checked
* for corruption.
*/
/*
#define UMM_INTEGRITY_CHECK
*/

/*
* -D UMM_POISON :
*
* Enables heap poisoning: add predefined value (poison) before and after each
* allocation, and check before each heap operation that no poison is
* corrupted.
*
* Other than the poison itself, we need to store exact user-requested length
* for each buffer, so that overrun by just 1 byte will be always noticed.
*
* Customizations:
*
* UMM_POISON_SIZE_BEFORE:
* Number of poison bytes before each block, e.g. 2
* UMM_POISON_SIZE_AFTER:
* Number of poison bytes after each block e.g. 2
* UMM_POISONED_BLOCK_LEN_TYPE
* Type of the exact buffer length, e.g. `short`
*
* NOTE: each allocated buffer is aligned by 4 bytes. But when poisoning is
* enabled, actual pointer returned to user is shifted by
* `(sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE)`.
* It's your responsibility to make resulting pointers aligned appropriately.
*
* If poison corruption is detected, the message is printed and user-provided
* callback is called: `UMM_HEAP_CORRUPTION_CB()`
*/
/*
#define UMM_POISON
*/
#define UMM_POISON_SIZE_BEFORE 4
#define UMM_POISON_SIZE_AFTER 4
#define UMM_POISONED_BLOCK_LEN_TYPE short

#endif /* _UMM_MALLOC_CFG_H */
202 changes: 202 additions & 0 deletions test/umm_malloc_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>

#include "umm_malloc.h"

#define TRY(v) do { \
bool res = v;\
if (!res) {\
printf("assert failed: " #v "\n");\
abort();\
}\
} while (0)

char test_umm_heap[UMM_MALLOC_CFG__HEAP_SIZE];
static int corruption_cnt = 0;

void umm_corruption(void) {
corruption_cnt++;
}

#if defined(UMM_POISON)
bool test_poison(void) {

size_t size;
for (size = 1; size <= 16; size++) {

{
umm_init();
corruption_cnt = 0;
char *ptr = umm_malloc(size);
ptr[size]++;

umm_free(ptr);

if (corruption_cnt == 0) {
printf("corruption_cnt should not be 0, but it is\n");
return false;
}
}

{
umm_init();
corruption_cnt = 0;
char *ptr = umm_calloc(1, size);
ptr[-1]++;

umm_free(ptr);

if (corruption_cnt == 0) {
printf("corruption_cnt should not be 0, but it is\n");
return false;
}
}
}

return true;
}
#endif

#if defined(UMM_INTEGRITY_CHECK)
bool test_integrity_check(void) {

size_t size;
for (size = 1; size <= 16; size++) {

{
umm_init();
corruption_cnt = 0;
char *ptr = umm_malloc(size);
memset(ptr, 0xfe, size + 8/* size of umm_block*/);

umm_free(ptr);

if (corruption_cnt == 0) {
printf("corruption_cnt should not be 0, but it is\n");
return false;
}
}

{
umm_init();
corruption_cnt = 0;
char *ptr = umm_calloc(1, size);
ptr[-1]++;

umm_free(ptr);

if (corruption_cnt == 0) {
printf("corruption_cnt should not be 0, but it is\n");
return false;
}
}
}

return true;
}
#endif

bool random_stress(void) {
void * ptr_array[256];
size_t i;
int idx;

corruption_cnt = 0;

printf( "Size of umm_heap is %u\n", (unsigned int)sizeof(test_umm_heap) );

umm_init();

umm_info( NULL, 1 );

for( idx=0; idx<256; ++idx )
ptr_array[idx] = (void *)NULL;

for( idx=0; idx<100000; ++idx ) {
i = rand()%256;

switch( rand() % 16 ) {

case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
{
ptr_array[i] = umm_realloc(ptr_array[i], 0);
break;
}
case 7:
case 8:
{
size_t size = rand()%40;
ptr_array[i] = umm_realloc(ptr_array[i], size );
memset(ptr_array[i], 0xfe, size);
break;
}

case 9:
case 10:
case 11:
case 12:
{
size_t size = rand()%100;
ptr_array[i] = umm_realloc(ptr_array[i], size );
memset(ptr_array[i], 0xfe, size);
break;
}

case 13:
case 14:
{
size_t size = rand()%200;
umm_free(ptr_array[i]);
ptr_array[i] = umm_calloc( 1, size );
if (ptr_array[i] != NULL){
int a;
for (a = 0; a < size; a++) {
if (((char *)ptr_array[i])[a] != 0x00) {
printf("calloc returned non-zeroed memory\n");
return false;
}
}
}
memset(ptr_array[i], 0xfe, size);
break;
}

default:
{
size_t size = rand()%400;
umm_free(ptr_array[i]);
ptr_array[i] = umm_malloc( size );
memset(ptr_array[i], 0xfe, size);
break;
}
}

}


return (corruption_cnt == 0);
}

int main(void) {
#if defined(UMM_INTEGRITY_CHECK)
TRY(test_integrity_check());
#endif

#if defined(UMM_POISON)
TRY(test_poison());
#endif

TRY(random_stress());

return 0;
}

Loading

0 comments on commit e497d24

Please sign in to comment.