1 /* $NetBSD: mymalloc.c,v 1.3 2020/03/18 19:05:21 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 /* char *mymemdup(ptr, len) 29 /* const char *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 char *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