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 */ 103typedef 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 146static const char empty_string[] = ""; 147 148#endif 149 150/* mymalloc - allocate memory or bust */ 151 152void *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 177void *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 209void 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 227char *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 244char *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 266void *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