Home | History | Annotate | Line # | Download | only in util
      1 /*	$NetBSD: mymalloc.c,v 1.4 2022/10/08 16:12:50 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	mymalloc 3
      6 /* SUMMARY
      7 /*	memory management wrappers
      8 /* SYNOPSIS
      9 /*	#include <mymalloc.h>
     10 /*
     11 /*	void	*mymalloc(len)
     12 /*	ssize_t	len;
     13 /*
     14 /*	void	*myrealloc(ptr, len)
     15 /*	void	*ptr;
     16 /*	ssize_t	len;
     17 /*
     18 /*	void	myfree(ptr)
     19 /*	void	*ptr;
     20 /*
     21 /*	char	*mystrdup(str)
     22 /*	const char *str;
     23 /*
     24 /*	char	*mystrndup(str, len)
     25 /*	const char *str;
     26 /*	ssize_t	len;
     27 /*
     28 /*	void	*mymemdup(ptr, len)
     29 /*	const void *ptr;
     30 /*	ssize_t	len;
     31 /* DESCRIPTION
     32 /*	This module performs low-level memory management with error
     33 /*	handling. A call of these functions either succeeds or it does
     34 /*	not return at all.
     35 /*
     36 /*	To save memory, zero-length strings are shared and read-only.
     37 /*	The caller must not attempt to modify the null terminator.
     38 /*	This code is enabled unless NO_SHARED_EMPTY_STRINGS is
     39 /*	defined at compile time (for example, you have an sscanf()
     40 /*	routine that pushes characters back into its input).
     41 /*
     42 /*	mymalloc() allocates the requested amount of memory. The memory
     43 /*	is not set to zero.
     44 /*
     45 /*	myrealloc() resizes memory obtained from mymalloc() or myrealloc()
     46 /*	to the requested size. The result pointer value may differ from
     47 /*	that given via the \fIptr\fR argument.
     48 /*
     49 /*	myfree() takes memory obtained from mymalloc() or myrealloc()
     50 /*	and makes it available for other use.
     51 /*
     52 /*	mystrdup() returns a dynamic-memory copy of its null-terminated
     53 /*	argument. This routine uses mymalloc().
     54 /*
     55 /*	mystrndup() returns a dynamic-memory copy of at most \fIlen\fR
     56 /*	leading characters of its null-terminated
     57 /*	argument. The result is null-terminated. This routine uses mymalloc().
     58 /*
     59 /*	mymemdup() makes a copy of the memory pointed to by \fIptr\fR
     60 /*	with length \fIlen\fR. The result is NOT null-terminated.
     61 /*	This routine uses mymalloc().
     62 /* SEE ALSO
     63 /*	msg(3) diagnostics interface
     64 /* DIAGNOSTICS
     65 /*	Problems are reported via the msg(3) diagnostics routines:
     66 /*	the requested amount of memory is not available; improper use
     67 /*	is detected; other fatal errors.
     68 /* LICENSE
     69 /* .ad
     70 /* .fi
     71 /*	The Secure Mailer license must be distributed with this software.
     72 /* AUTHOR(S)
     73 /*	Wietse Venema
     74 /*	IBM T.J. Watson Research
     75 /*	P.O. Box 704
     76 /*	Yorktown Heights, NY 10598, USA
     77 /*
     78 /*	Wietse Venema
     79 /*	Google, Inc.
     80 /*	111 8th Avenue
     81 /*	New York, NY 10011, USA
     82 /*--*/
     83 
     84 /* System libraries. */
     85 
     86 #include "sys_defs.h"
     87 #include <stdlib.h>
     88 #include <stddef.h>
     89 #include <string.h>
     90 
     91 /* Application-specific. */
     92 
     93 #include "msg.h"
     94 #include "mymalloc.h"
     95 
     96  /*
     97   * Structure of an annotated memory block. In order to detect spurious
     98   * free() calls we prepend a signature to memory given to the application.
     99   * In order to detect access to free()d blocks, overwrite each block as soon
    100   * as it is passed to myfree(). With the code below, the user data has
    101   * integer alignment or better.
    102   */
    103 typedef struct MBLOCK {
    104     int     signature;			/* set when block is active */
    105     ssize_t length;			/* user requested length */
    106     union {
    107 	ALIGN_TYPE align;
    108 	char    payload[1];		/* actually a bunch of bytes */
    109     }       u;
    110 } MBLOCK;
    111 
    112 #define SIGNATURE	0xdead
    113 #define FILLER		0xff
    114 
    115 #define CHECK_IN_PTR(ptr, real_ptr, len, fname) { \
    116     if (ptr == 0) \
    117 	msg_panic("%s: null pointer input", fname); \
    118     real_ptr = (MBLOCK *) (ptr - offsetof(MBLOCK, u.payload[0])); \
    119     if (real_ptr->signature != SIGNATURE) \
    120 	msg_panic("%s: corrupt or unallocated memory block", fname); \
    121     real_ptr->signature = 0; \
    122     if ((len = real_ptr->length) < 1) \
    123 	msg_panic("%s: corrupt memory block length", fname); \
    124 }
    125 
    126 #define CHECK_OUT_PTR(ptr, real_ptr, len) { \
    127     real_ptr->signature = SIGNATURE; \
    128     real_ptr->length = len; \
    129     ptr = real_ptr->u.payload; \
    130 }
    131 
    132 #define SPACE_FOR(len)	(offsetof(MBLOCK, u.payload[0]) + len)
    133 
    134  /*
    135   * Optimization for short strings. We share one copy with multiple callers.
    136   * This differs from normal heap memory in two ways, because the memory is
    137   * shared:
    138   *
    139   * -  It must be read-only to avoid horrible bugs. This is OK because there is
    140   * no legitimate reason to modify the null terminator.
    141   *
    142   * - myfree() cannot overwrite the memory with a filler pattern like it can do
    143   * with heap memory. Therefore, some dangling pointer bugs will be masked.
    144   */
    145 #ifndef NO_SHARED_EMPTY_STRINGS
    146 static const char empty_string[] = "";
    147 
    148 #endif
    149 
    150 /* mymalloc - allocate memory or bust */
    151 
    152 void   *mymalloc(ssize_t len)
    153 {
    154     void   *ptr;
    155     MBLOCK *real_ptr;
    156 
    157     /*
    158      * Note: for safety reasons the request length is a signed type. This
    159      * allows us to catch integer overflow problems that weren't already
    160      * caught up-stream.
    161      */
    162     if (len < 1)
    163 	msg_panic("mymalloc: requested length %ld", (long) len);
    164 #ifdef MYMALLOC_FUZZ
    165     len += MYMALLOC_FUZZ;
    166 #endif
    167     if ((real_ptr = (MBLOCK *) malloc(SPACE_FOR(len))) == 0)
    168 	msg_fatal("mymalloc: insufficient memory for %ld bytes: %m",
    169 		  (long) len);
    170     CHECK_OUT_PTR(ptr, real_ptr, len);
    171     memset(ptr, FILLER, len);
    172     return (ptr);
    173 }
    174 
    175 /* myrealloc - reallocate memory or bust */
    176 
    177 void   *myrealloc(void *ptr, ssize_t len)
    178 {
    179     MBLOCK *real_ptr;
    180     ssize_t old_len;
    181 
    182 #ifndef NO_SHARED_EMPTY_STRINGS
    183     if (ptr == empty_string)
    184 	return (mymalloc(len));
    185 #endif
    186 
    187     /*
    188      * Note: for safety reasons the request length is a signed type. This
    189      * allows us to catch integer overflow problems that weren't already
    190      * caught up-stream.
    191      */
    192     if (len < 1)
    193 	msg_panic("myrealloc: requested length %ld", (long) len);
    194 #ifdef MYMALLOC_FUZZ
    195     len += MYMALLOC_FUZZ;
    196 #endif
    197     CHECK_IN_PTR(ptr, real_ptr, old_len, "myrealloc");
    198     if ((real_ptr = (MBLOCK *) realloc((void *) real_ptr, SPACE_FOR(len))) == 0)
    199 	msg_fatal("myrealloc: insufficient memory for %ld bytes: %m",
    200 		  (long) len);
    201     CHECK_OUT_PTR(ptr, real_ptr, len);
    202     if (len > old_len)
    203 	memset(ptr + old_len, FILLER, len - old_len);
    204     return (ptr);
    205 }
    206 
    207 /* myfree - release memory */
    208 
    209 void    myfree(void *ptr)
    210 {
    211     MBLOCK *real_ptr;
    212     ssize_t len;
    213 
    214 #ifndef NO_SHARED_EMPTY_STRINGS
    215     if (ptr != empty_string) {
    216 #endif
    217 	CHECK_IN_PTR(ptr, real_ptr, len, "myfree");
    218 	memset((void *) real_ptr, FILLER, SPACE_FOR(len));
    219 	free((void *) real_ptr);
    220 #ifndef NO_SHARED_EMPTY_STRINGS
    221     }
    222 #endif
    223 }
    224 
    225 /* mystrdup - save string to heap */
    226 
    227 char   *mystrdup(const char *str)
    228 {
    229     size_t  len;
    230 
    231     if (str == 0)
    232 	msg_panic("mystrdup: null pointer argument");
    233 #ifndef NO_SHARED_EMPTY_STRINGS
    234     if (*str == 0)
    235 	return ((char *) empty_string);
    236 #endif
    237     if ((len = strlen(str) + 1) > SSIZE_T_MAX)
    238 	msg_panic("mystrdup: string length >= SSIZE_T_MAX");
    239     return (memcpy(mymalloc(len), str, len));
    240 }
    241 
    242 /* mystrndup - save substring to heap */
    243 
    244 char   *mystrndup(const char *str, ssize_t len)
    245 {
    246     char   *result;
    247     char   *cp;
    248 
    249     if (str == 0)
    250 	msg_panic("mystrndup: null pointer argument");
    251     if (len < 0)
    252 	msg_panic("mystrndup: requested length %ld", (long) len);
    253 #ifndef NO_SHARED_EMPTY_STRINGS
    254     if (*str == 0)
    255 	return ((char *) empty_string);
    256 #endif
    257     if ((cp = memchr(str, 0, len)) != 0)
    258 	len = cp - str;
    259     result = memcpy(mymalloc(len + 1), str, len);
    260     result[len] = 0;
    261     return (result);
    262 }
    263 
    264 /* mymemdup - copy memory */
    265 
    266 void   *mymemdup(const void *ptr, ssize_t len)
    267 {
    268     if (ptr == 0)
    269 	msg_panic("mymemdup: null pointer argument");
    270     return (memcpy(mymalloc(len), ptr, len));
    271 }
    272