diff options
Diffstat (limited to 'src/util/mymalloc.c')
-rw-r--r-- | src/util/mymalloc.c | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/src/util/mymalloc.c b/src/util/mymalloc.c new file mode 100644 index 0000000..1c8199e --- /dev/null +++ b/src/util/mymalloc.c @@ -0,0 +1,269 @@ +/*++ +/* NAME +/* mymalloc 3 +/* SUMMARY +/* memory management wrappers +/* SYNOPSIS +/* #include <mymalloc.h> +/* +/* void *mymalloc(len) +/* ssize_t len; +/* +/* void *myrealloc(ptr, len) +/* void *ptr; +/* ssize_t len; +/* +/* void myfree(ptr) +/* void *ptr; +/* +/* char *mystrdup(str) +/* const char *str; +/* +/* char *mystrndup(str, len) +/* const char *str; +/* ssize_t len; +/* +/* char *mymemdup(ptr, len) +/* const char *ptr; +/* ssize_t len; +/* DESCRIPTION +/* This module performs low-level memory management with error +/* handling. A call of these functions either succeeds or it does +/* not return at all. +/* +/* To save memory, zero-length strings are shared and read-only. +/* The caller must not attempt to modify the null terminator. +/* This code is enabled unless NO_SHARED_EMPTY_STRINGS is +/* defined at compile time (for example, you have an sscanf() +/* routine that pushes characters back into its input). +/* +/* mymalloc() allocates the requested amount of memory. The memory +/* is not set to zero. +/* +/* myrealloc() resizes memory obtained from mymalloc() or myrealloc() +/* to the requested size. The result pointer value may differ from +/* that given via the \fIptr\fR argument. +/* +/* myfree() takes memory obtained from mymalloc() or myrealloc() +/* and makes it available for other use. +/* +/* mystrdup() returns a dynamic-memory copy of its null-terminated +/* argument. This routine uses mymalloc(). +/* +/* mystrndup() returns a dynamic-memory copy of at most \fIlen\fR +/* leading characters of its null-terminated +/* argument. The result is null-terminated. This routine uses mymalloc(). +/* +/* mymemdup() makes a copy of the memory pointed to by \fIptr\fR +/* with length \fIlen\fR. The result is NOT null-terminated. +/* This routine uses mymalloc(). +/* SEE ALSO +/* msg(3) diagnostics interface +/* DIAGNOSTICS +/* Problems are reported via the msg(3) diagnostics routines: +/* the requested amount of memory is not available; improper use +/* is detected; other fatal errors. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System libraries. */ + +#include "sys_defs.h" +#include <stdlib.h> +#include <stddef.h> +#include <string.h> + +/* Application-specific. */ + +#include "msg.h" +#include "mymalloc.h" + + /* + * Structure of an annotated memory block. In order to detect spurious + * free() calls we prepend a signature to memory given to the application. + * In order to detect access to free()d blocks, overwrite each block as soon + * as it is passed to myfree(). With the code below, the user data has + * integer alignment or better. + */ +typedef struct MBLOCK { + int signature; /* set when block is active */ + ssize_t length; /* user requested length */ + union { + ALIGN_TYPE align; + char payload[1]; /* actually a bunch of bytes */ + } u; +} MBLOCK; + +#define SIGNATURE 0xdead +#define FILLER 0xff + +#define CHECK_IN_PTR(ptr, real_ptr, len, fname) { \ + if (ptr == 0) \ + msg_panic("%s: null pointer input", fname); \ + real_ptr = (MBLOCK *) (ptr - offsetof(MBLOCK, u.payload[0])); \ + if (real_ptr->signature != SIGNATURE) \ + msg_panic("%s: corrupt or unallocated memory block", fname); \ + real_ptr->signature = 0; \ + if ((len = real_ptr->length) < 1) \ + msg_panic("%s: corrupt memory block length", fname); \ +} + +#define CHECK_OUT_PTR(ptr, real_ptr, len) { \ + real_ptr->signature = SIGNATURE; \ + real_ptr->length = len; \ + ptr = real_ptr->u.payload; \ +} + +#define SPACE_FOR(len) (offsetof(MBLOCK, u.payload[0]) + len) + + /* + * Optimization for short strings. We share one copy with multiple callers. + * This differs from normal heap memory in two ways, because the memory is + * shared: + * + * - It must be read-only to avoid horrible bugs. This is OK because there is + * no legitimate reason to modify the null terminator. + * + * - myfree() cannot overwrite the memory with a filler pattern like it can do + * with heap memory. Therefore, some dangling pointer bugs will be masked. + */ +#ifndef NO_SHARED_EMPTY_STRINGS +static const char empty_string[] = ""; + +#endif + +/* mymalloc - allocate memory or bust */ + +void *mymalloc(ssize_t len) +{ + void *ptr; + MBLOCK *real_ptr; + + /* + * Note: for safety reasons the request length is a signed type. This + * allows us to catch integer overflow problems that weren't already + * caught up-stream. + */ + if (len < 1) + msg_panic("mymalloc: requested length %ld", (long) len); +#ifdef MYMALLOC_FUZZ + len += MYMALLOC_FUZZ; +#endif + if ((real_ptr = (MBLOCK *) malloc(SPACE_FOR(len))) == 0) + msg_fatal("mymalloc: insufficient memory for %ld bytes: %m", + (long) len); + CHECK_OUT_PTR(ptr, real_ptr, len); + memset(ptr, FILLER, len); + return (ptr); +} + +/* myrealloc - reallocate memory or bust */ + +void *myrealloc(void *ptr, ssize_t len) +{ + MBLOCK *real_ptr; + ssize_t old_len; + +#ifndef NO_SHARED_EMPTY_STRINGS + if (ptr == empty_string) + return (mymalloc(len)); +#endif + + /* + * Note: for safety reasons the request length is a signed type. This + * allows us to catch integer overflow problems that weren't already + * caught up-stream. + */ + if (len < 1) + msg_panic("myrealloc: requested length %ld", (long) len); +#ifdef MYMALLOC_FUZZ + len += MYMALLOC_FUZZ; +#endif + CHECK_IN_PTR(ptr, real_ptr, old_len, "myrealloc"); + if ((real_ptr = (MBLOCK *) realloc((void *) real_ptr, SPACE_FOR(len))) == 0) + msg_fatal("myrealloc: insufficient memory for %ld bytes: %m", + (long) len); + CHECK_OUT_PTR(ptr, real_ptr, len); + if (len > old_len) + memset(ptr + old_len, FILLER, len - old_len); + return (ptr); +} + +/* myfree - release memory */ + +void myfree(void *ptr) +{ + MBLOCK *real_ptr; + ssize_t len; + +#ifndef NO_SHARED_EMPTY_STRINGS + if (ptr != empty_string) { +#endif + CHECK_IN_PTR(ptr, real_ptr, len, "myfree"); + memset((void *) real_ptr, FILLER, SPACE_FOR(len)); + free((void *) real_ptr); +#ifndef NO_SHARED_EMPTY_STRINGS + } +#endif +} + +/* mystrdup - save string to heap */ + +char *mystrdup(const char *str) +{ + size_t len; + + if (str == 0) + msg_panic("mystrdup: null pointer argument"); +#ifndef NO_SHARED_EMPTY_STRINGS + if (*str == 0) + return ((char *) empty_string); +#endif + if ((len = strlen(str) + 1) > SSIZE_T_MAX) + msg_panic("mystrdup: string length >= SSIZE_T_MAX"); + return (strcpy(mymalloc(len), str)); +} + +/* mystrndup - save substring to heap */ + +char *mystrndup(const char *str, ssize_t len) +{ + char *result; + char *cp; + + if (str == 0) + msg_panic("mystrndup: null pointer argument"); + if (len < 0) + msg_panic("mystrndup: requested length %ld", (long) len); +#ifndef NO_SHARED_EMPTY_STRINGS + if (*str == 0) + return ((char *) empty_string); +#endif + if ((cp = memchr(str, 0, len)) != 0) + len = cp - str; + result = memcpy(mymalloc(len + 1), str, len); + result[len] = 0; + return (result); +} + +/* mymemdup - copy memory */ + +char *mymemdup(const void *ptr, ssize_t len) +{ + if (ptr == 0) + msg_panic("mymemdup: null pointer argument"); + return (memcpy(mymalloc(len), ptr, len)); +} |