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