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
mymalloc(ssize_t len)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
myrealloc(void * ptr,ssize_t len)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
myfree(void * ptr)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
mystrdup(const char * str)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
mystrndup(const char * str,ssize_t len)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
mymemdup(const void * ptr,ssize_t len)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