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