johnsonjh/duma

View on GitHub
duma.h

Summary

Maintainability
Test Coverage
/*
 * DUMA - Red-Zone memory allocator.
 * Copyright (C) 2020-2022 Jeffrey H. Johnson <trnsz@pobox.com>
 * Copyright (C) 2006 Michael Eddington <meddington@gmail.com>
 * Copyright (C) 2002-2021 Hayati Ayguen <h_ayguen@web.de>, Procitec GmbH
 * Copyright (C) 1987-1999 Bruce Perens <bruce@perens.com>
 * License: GNU GPL (GNU General Public License, see COPYING-GPL)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *
 * FILE CONTENTS:
 * header file for inclusion from YOUR application code
 */

/* explicitly no "#ifndef _DUMA_H_" to allow mutliple inclusions
 * within single source file with inclusion of noduma.h in between
 */

#include <stdlib.h>

/*
 * #include <stdlib.h>
 *
 * You must include <stdlib.h> before including <duma.h> !
 *
 */

/* for enabling inclusion of duma.h after inclusion of efencint.h */
/* remove previous definitions */
#define SKIP_DUMA_NO_CXX
#include "noduma.h"
#undef SKIP_DUMA_NO_CXX

#include "duma_config.h"

#ifdef __cplusplus
#define DUMA_EXTERN_C extern "C"
#else
#define DUMA_EXTERN_C extern
#endif

#ifdef DUMA_NO_DUMA

/* enable these macros even in release code, but do nothing */
#define DUMA_newFrame()                                                        \
  do {                                                                         \
  } while (0)
#define DUMA_delFrame()                                                        \
  do {                                                                         \
  } while (0)

#define DUMA_SET_ALIGNMENT(V)                                                  \
  do {                                                                         \
  } while (0)
#define DUMA_SET_PROTECT_BELOW(V)                                              \
  do {                                                                         \
  } while (0)
#define DUMA_SET_FILL(V)                                                       \
  do {                                                                         \
  } while (0)

#define DUMA_ASSERT(EXPR)                                                      \
  do {                                                                         \
  } while (0)

#define DUMA_CHECK(BASEADR)                                                    \
  do {                                                                         \
  } while (0)
#define DUMA_CHECKALL()                                                        \
  do {                                                                         \
  } while (0)

#define CA_DECLARE(NAME, SIZE)                                                 \
  do {                                                                         \
  } while (0)
#define CA_DEFINE(TYPE, NAME, SIZE) TYPE NAME[SIZE]
#define CA_REF(NAME, INDEX) NAME[INDEX]

#else /* ifndef DUMA_NO_DUMA */

#ifndef DUMA_EXTERNS_DECLARED
#define DUMA_EXTERNS_DECLARED
/* global DUMA variables */
DUMA_EXTERN_C int DUMA_OUTPUT_DEBUG;
DUMA_EXTERN_C int DUMA_OUTPUT_STDOUT;
DUMA_EXTERN_C int DUMA_OUTPUT_STDERR;
DUMA_EXTERN_C char *DUMA_OUTPUT_FILE;
DUMA_EXTERN_C int DUMA_OUTPUT_STACKTRACE;
#endif /* DUMA_EXTERNS_DECLARED */

/* set Maximum Delete Depth (depth of recursive destructor calls) */
#ifndef DUMA_MAX_DEL_DEPTH
#define DUMA_MAX_DEL_DEPTH 256
#endif

#ifndef DUMA_TLSVARTYPE_DEFINED
#define DUMA_TLSVARTYPE_DEFINED
/* GitHub #82 - following variables should exist per thread ("thread-local") */
typedef struct {
  /*
   * ALIGNMENT is a global variable used to control the default alignment
   * of buffers returned by malloc(), calloc(), and realloc(). It is all-caps
   * so that its name matches the name of the environment variable that is used
   * to set it. This gives the programmer one less name to remember.
   */
  int ALIGNMENT;

  /*
   * PROTECT_BELOW is used to modify the behavior of the allocator. When
   * its value is non-zero, the allocator will place an inaccessable page
   * immediately _before_ the malloc buffer in the address space, instead
   * of _after_ it. Use this to detect malloc buffer under-runs, rather than
   * over-runs. It won't detect both at the same time, so you should test your
   * software twice, once with this value clear, and once with it set.
   */
  int PROTECT_BELOW;

  /*
   * FILL is set to 0-255 if DUMA should fill all new allocated
   * memory with the specified value. Set to -1 when DUMA should not
   * initialise allocated memory.
   * default is set to initialise with 255, cause many programs rely on
   * initialisation to 0!
   */
  int FILL;

#if !defined(DUMA_NO_CPP_SUPPORT) && !defined(DUMA_NO_LEAKDETECTION)
  int Magic;
  int DelPtr;
  const char *DelFile[DUMA_MAX_DEL_DEPTH];
  int DelLine[DUMA_MAX_DEL_DEPTH];
#endif
} DUMA_TLSVARS_T;
#endif

#ifndef DUMA_GLOBALS_DEFINED
#define DUMA_GLOBALS_DEFINED

typedef struct {
  /* Protection Space A */
  char acSpaceA[2 * DUMA_PAGE_SIZE];

  /* Variable: _duma_allocList
   *
   * _DUMA_allocList points to the array of slot structures used to manage the
   * malloc arena.
   */
  struct _DUMA_Slot *allocList;

  /* Variable: _duma_null_addr
   *
   * _duma_null_addr is the address malloc() or C++ operator new returns, when
   * size is 0 two pages get reserved and protected
   */
  void *null_addr;

  /* Variable */
  DUMA_TLSVARS_T TLS;

  /* Protection Space B */
  char acSpaceB[2 * DUMA_PAGE_SIZE];

} DUMA_GLOBALVARS_T;

DUMA_EXTERN_C DUMA_GLOBALVARS_T _duma_g;

#endif /* DUMA_GLOBALS_DEFINED */

#define GET_DUMA_TLSVARS() (&_duma_g.TLS)

#ifndef DUMA_SET_ALIGNMENT
#define DUMA_SET_ALIGNMENT(V) GET_DUMA_TLSVARS()->ALIGNMENT = (V)
#endif
#ifndef DUMA_SET_PROTECT_BELOW
#define DUMA_SET_PROTECT_BELOW(V) GET_DUMA_TLSVARS()->PROTECT_BELOW = (V)
#endif
#ifndef DUMA_SET_FILL
#define DUMA_SET_FILL(V) GET_DUMA_TLSVARS()->FILL = (V)
#endif

#ifndef DUMA_ENUMS_DECLARED
#define DUMA_ENUMS_DECLARED

/* allocator defines the type of calling allocator/deallocator function */
enum _DUMA_Allocator {
  EFA_INT_ALLOC,
  EFA_INT_DEALLOC,
  EFA_MALLOC,
  EFA_CALLOC,
  EFA_FREE,
  EFA_MEMALIGN,
  EFA_POSIX_MEMALIGN,
  EFA_REALLOC,
  EFA_VALLOC,
  EFA_STRDUP,
  EFA_NEW_ELEM,
  EFA_DEL_ELEM,
  EFA_NEW_ARRAY,
  EFA_DEL_ARRAY

  /* use following enums when calling _duma_allocate()/_duma_deallocate()
   * from user defined member operators
   */
  ,
  EFA_MEMBER_NEW_ELEM,
  EFA_MEMBER_DEL_ELEM,
  EFA_MEMBER_NEW_ARRAY,
  EFA_MEMBER_DEL_ARRAY
};

enum _DUMA_FailReturn { DUMA_FAIL_NULL, DUMA_FAIL_ENV };

#endif /* DUMA_ENUMS_DECLARED */

#ifndef DUMA_FUNCTIONS_DECLARED
#define DUMA_FUNCTIONS_DECLARED

DUMA_EXTERN_C void _duma_init(void);
DUMA_EXTERN_C void _duma_assert(const char *exprstr, const char *filename,
                                int lineno);

DUMA_EXTERN_C void duma_check(void *address);
DUMA_EXTERN_C void duma_checkAll();

DUMA_EXTERN_C void *duma_alloc_return(void *address);

#ifdef DUMA_EXPLICIT_INIT
DUMA_EXTERN_C void duma_init(void);
#endif

#ifndef DUMA_NO_LEAKDETECTION
DUMA_EXTERN_C void *_duma_allocate(size_t alignment, size_t userSize,
                                   int protectBelow, int fillByte,
                                   int protectAllocList,
                                   enum _DUMA_Allocator allocator,
                                   enum _DUMA_FailReturn fail,
                                   const char *filename, int lineno);
DUMA_EXTERN_C void _duma_deallocate(void *baseAdr, int protectAllocList,
                                    enum _DUMA_Allocator allocator,
                                    const char *filename, int lineno);
DUMA_EXTERN_C void *_duma_malloc(size_t size, const char *filename, int lineno);
DUMA_EXTERN_C void *_duma_calloc(size_t elemCount, size_t elemSize,
                                 const char *filename, int lineno);
DUMA_EXTERN_C void _duma_free(void *baseAdr, const char *filename, int lineno);
DUMA_EXTERN_C void *_duma_memalign(size_t alignment, size_t userSize,
                                   const char *filename, int lineno);
DUMA_EXTERN_C int _duma_posix_memalign(void **memptr, size_t alignment,
                                       size_t userSize, const char *filename,
                                       int lineno);
DUMA_EXTERN_C void *_duma_realloc(void *baseAdr, size_t newSize,
                                  const char *filename, int lineno);
DUMA_EXTERN_C void *_duma_valloc(size_t size, const char *filename, int lineno);
DUMA_EXTERN_C size_t _duma_malloc_usable_size(void *ptr, const char *filename,
                                              int lineno);
DUMA_EXTERN_C char *_duma_strdup(const char *str, const char *filename,
                                 int lineno);
DUMA_EXTERN_C void *_duma_memcpy(void *dest, const void *src, size_t size,
                                 const char *filename, int lineno);
DUMA_EXTERN_C void *_duma_memmove(void *dest, const void *src, size_t size);
DUMA_EXTERN_C char *_duma_strcpy(char *dest, const char *src,
                                 const char *filename, int lineno);
DUMA_EXTERN_C char *_duma_strncpy(char *dest, const char *src, size_t size,
                                  const char *filename, int lineno);
DUMA_EXTERN_C char *_duma_strcat(char *dest, const char *src,
                                 const char *filename, int lineno);
DUMA_EXTERN_C char *_duma_strncat(char *dest, const char *src, size_t size,
                                  const char *filename, int lineno);
DUMA_EXTERN_C void DUMA_newFrame(void);
DUMA_EXTERN_C void DUMA_delFrame(void);
#else /* DUMA_NO_LEAKDETECTION */
DUMA_EXTERN_C void *_duma_allocate(size_t alignment, size_t userSize,
                                   int protectBelow, int fillByte,
                                   int protectAllocList,
                                   enum _DUMA_Allocator allocator,
                                   enum _DUMA_FailReturn fail);
DUMA_EXTERN_C void _duma_deallocate(void *baseAdr, int protectAllocList,
                                    enum _DUMA_Allocator allocator);
DUMA_EXTERN_C void *_duma_malloc(size_t size);
DUMA_EXTERN_C void *_duma_calloc(size_t elemCount, size_t elemSize);
DUMA_EXTERN_C void _duma_free(void *baseAdr);
DUMA_EXTERN_C void *_duma_memalign(size_t alignment, size_t userSize);
DUMA_EXTERN_C int _duma_posix_memalign(void **memptr, size_t alignment,
                                       size_t userSize);
DUMA_EXTERN_C void *_duma_realloc(void *baseAdr, size_t newSize);
DUMA_EXTERN_C void *_duma_valloc(size_t size);
DUMA_EXTERN_C size_t _duma_malloc_usable_size(void *ptr);
DUMA_EXTERN_C char *_duma_strdup(const char *str);
DUMA_EXTERN_C void *_duma_memcpy(void *dest, const void *src, size_t size);
DUMA_EXTERN_C void *_duma_memmove(void *dest, const void *src, size_t size);
DUMA_EXTERN_C char *_duma_strcpy(char *dest, const char *src);
DUMA_EXTERN_C char *_duma_strncpy(char *dest, const char *src, size_t size);
DUMA_EXTERN_C char *_duma_strcat(char *dest, const char *src);
DUMA_EXTERN_C char *_duma_strncat(char *dest, const char *src, size_t size);
#endif /* DUMA_NO_LEAKDETECTION */

#endif /* DUMA_FUNCTIONS_DECLARED */

#ifndef DUMA_SKIP_SETUP
#ifndef DUMA_NO_LEAKDETECTION
#define malloc(SIZE) _duma_malloc(SIZE, __FILE__, __LINE__)
#define calloc(ELEMCOUNT, ELEMSIZE)                                            \
  _duma_calloc(ELEMCOUNT, ELEMSIZE, __FILE__, __LINE__)
#define free(BASEADR) _duma_free(BASEADR, __FILE__, __LINE__)
#define memalign(ALIGNMENT, SIZE)                                              \
  _duma_memalign(ALIGNMENT, SIZE, __FILE__, __LINE__)
#define posix_memalign(MEMPTR, ALIGNMENT, SIZE)                                \
  _duma_posix_memalign(MEMPTR, ALIGNMENT, SIZE, __FILE__, __LINE__)
#define realloc(BASEADR, NEWSIZE)                                              \
  _duma_realloc(BASEADR, NEWSIZE, __FILE__, __LINE__)
#define valloc(SIZE) _duma_valloc(SIZE, __FILE__, __LINE__)
#define malloc_usable_size(PTR) \
  _duma_malloc_usable_size(PTR, __FILE__, __LINE__)
#define strdup(STR) _duma_strdup(STR, __FILE__, __LINE__)
#define memcpy(DEST, SRC, SIZE)                                                \
  _duma_memcpy(DEST, SRC, SIZE, __FILE__, __LINE__)
#define memmove(DEST, SRC, SIZE) _duma_memmove(DEST, SRC, SIZE)
#define strcpy(DEST, SRC) _duma_strcpy(DEST, SRC, __FILE__, __LINE__)
#define strncpy(DEST, SRC, SIZE)                                               \
  _duma_strncpy(DEST, SRC, SIZE, __FILE__, __LINE__)
#define strcat(DEST, SRC) _duma_strcat(DEST, SRC, __FILE__, __LINE__)
#define strncat(DEST, SRC, SIZE)                                               \
  _duma_strncat(DEST, SRC, SIZE, __FILE__, __LINE__)
#else /* DUMA_NO_LEAKDETECTION */
#define DUMA_newFrame()                                                        \
  do {                                                                         \
  } while (0)
#define DUMA_delFrame()                                                        \
  do {                                                                         \
  } while (0)
#endif /* DUMA_NO_LEAKDETECTION */
#endif // DUMA_SKIP_SETUP

#ifndef DUMA_ASSERT
#define DUMA_ASSERT(EXPR)                                                      \
  ((EXPR) || (_duma_assert(#EXPR, __FILE__, __LINE__), 0))
#endif
#ifndef DUMA_CHECK
#define DUMA_CHECK(BASEADR) duma_check(BASEADR)
#endif
#ifndef DUMA_CHECKALL
#define DUMA_CHECKALL() duma_checkAll()
#endif

/*
 * protection of functions return address
 */
#ifdef __GNUC__
#define DUMA_FN_PROT_START                                                     \
  const void *DUMA_RET_ADDR = __builtin_return_address(0);                     \
  {
#define DUMA_FN_PROT_END                                                       \
  }                                                                            \
  DUMA_ASSERT(__builtin_return_address(0) == DUMA_RET_ADDR);

#define DUMA_FN_PROT_RET(EXPR)                                                 \
  do {                                                                         \
    DUMA_ASSERT(__builtin_return_address(0) == DUMA_RET_ADDR);                 \
    return (EXPR);                                                             \
  } while (0)
#define DUMA_FN_PROT_RET_VOID()                                                \
  do {                                                                         \
    DUMA_ASSERT(__builtin_return_address(0) == DUMA_RET_ADDR);                 \
    return;                                                                    \
  } while (0)
#else
#define DUMA_FN_PROT_START                                                     \
  int aiDUMA_PROT[4] = {'E', 'F', 'P', 'R'};                                   \
  {
#define DUMA_FN_PROT_END                                                       \
  }                                                                            \
  DUMA_ASSERT('E' == aiDUMA_PROT[0] && 'F' == aiDUMA_PROT[1] &&                \
              'P' == aiDUMA_PROT[2] && 'R' == aiDUMA_PROT[3]);
#define DUMA_FN_PROT_RET(EXPR)                                                 \
  do {                                                                         \
    DUMA_ASSERT('E' == aiDUMA_PROT[0] && 'F' == aiDUMA_PROT[1] &&              \
                'P' == aiDUMA_PROT[2] && 'R' == aiDUMA_PROT[3]);               \
    return (EXPR);                                                             \
  } while (0)
#define DUMA_FN_PROT_RET_VOID()                                                \
  do {                                                                         \
    DUMA_ASSERT('E' == aiDUMA_PROT[0] && 'F' == aiDUMA_PROT[1] &&              \
                'P' == aiDUMA_PROT[2] && 'R' == aiDUMA_PROT[3]);               \
    return;                                                                    \
  } while (0)
#endif

/* declaration of an already defined array to enable checking at every reference
 * when using CA_REF()
 */
#define CA_DECLARE(NAME, SIZE)                                                 \
  const unsigned long NAME##_checkedsize = (SIZE);                             \
  unsigned long NAME##_checkedidx

/* definition of a checked array adds definitions for its size and an extra
 * temporary. every array gets its own temporary to avoid problems with
 * threading a global temporary would have.
 */
#define CA_DEFINE(TYPE, NAME, SIZE)                                            \
  TYPE NAME[SIZE];                                                             \
  CA_DECLARE(NAME, SIZE)

/* every access to a checked array is preceded an assert() on the index;
 * the index parameter is stored to a temporary to avoid double execution of
 * index, when index contains f.e. a "++".
 */
#define CA_REF(NAME, INDEX)                                                    \
  NAME[DUMA_ASSERT((NAME##_checkedidx = (INDEX)) < NAME##_checkedsize),        \
       NAME##_checkedidx]

#endif /* end ifdef DUMA_NO_DUMA */