]> eyrie.org Git - kerberos/krb5-strength.git/blob - tests/util/xmalloc.c
New upstream version 3.2
[kerberos/krb5-strength.git] / tests / util / xmalloc.c
1 /*
2  * Test suite for xmalloc and family.
3  *
4  * The canonical version of this file is maintained in the rra-c-util package,
5  * which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
6  *
7  * Copyright 2000-2001, 2006, 2017, 2020 Russ Allbery <eagle@eyrie.org>
8  * Copyright 2008, 2012-2014
9  *     The Board of Trustees of the Leland Stanford Junior University
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included in
19  * all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  *
29  * SPDX-License-Identifier: MIT
30  */
31
32 #line 1 "xmalloc.c"
33
34 #include <config.h>
35 #include <portable/system.h>
36
37 #include <assert.h>
38 #include <ctype.h>
39 #include <errno.h>
40 #ifdef HAVE_SYS_TIME_H
41 #    include <sys/time.h>
42 #endif
43 #include <time.h>
44
45 /* Linux requires sys/time.h be included before sys/resource.h. */
46 #include <sys/resource.h>
47
48 #include <util/messages.h>
49 #include <util/xmalloc.h>
50
51
52 /*
53  * A customized error handler for checking xmalloc's support of them.  Prints
54  * out the error message and exits with status 1.
55  */
56 static void __attribute__((__noreturn__))
57 test_handler(const char *function, size_t size, const char *file, int line)
58 {
59     die("%s %lu %s %d", function, (unsigned long) size, file, line);
60 }
61
62
63 /*
64  * Allocate the amount of memory given and write to all of it to make sure we
65  * can, returning true if that succeeded and false on any sort of detectable
66  * error.
67  */
68 static int
69 test_malloc(size_t size)
70 {
71     char *buffer;
72     size_t i;
73
74     buffer = xmalloc(size);
75     if (buffer == NULL)
76         return 0;
77     if (size > 0)
78         memset(buffer, 1, size);
79     for (i = 0; i < size; i++)
80         if (buffer[i] != 1)
81             return 0;
82     free(buffer);
83     return 1;
84 }
85
86
87 /*
88  * Allocate 10 bytes of memory given, write to it, then reallocate to the
89  * desired size, writing to the rest and then checking it all.  Returns true
90  * on success, false on any failure.
91  */
92 static int
93 test_realloc(size_t size)
94 {
95     char *buffer;
96     size_t i;
97
98     assert(size > 10);
99     buffer = xmalloc(10);
100     if (buffer == NULL)
101         return 0;
102     memset(buffer, 1, 10);
103     buffer = xrealloc(buffer, size);
104     if (buffer == NULL)
105         return 0;
106     memset(buffer + 10, 2, size - 10);
107     for (i = 0; i < 10; i++)
108         if (buffer[i] != 1)
109             return 0;
110     for (i = 10; i < size; i++)
111         if (buffer[i] != 2)
112             return 0;
113     free(buffer);
114     return 1;
115 }
116
117
118 /*
119  * Like test_realloc, but test allocating an array instead.  Returns true on
120  * success, false on any failure.
121  */
122 static int
123 test_reallocarray(size_t n, size_t size)
124 {
125     char *buffer;
126     size_t i;
127
128     buffer = xmalloc(10);
129     if (buffer == NULL)
130         return 0;
131     memset(buffer, 1, 10);
132     buffer = xreallocarray(buffer, n, size);
133     if (buffer == NULL)
134         return 0;
135     if (n > 0 && size > 0)
136         memset(buffer + 10, 2, (n * size) - 10);
137     for (i = 0; i < 10; i++)
138         if (buffer[i] != 1)
139             return 0;
140     for (i = 10; i < n * size; i++)
141         if (buffer[i] != 2)
142             return 0;
143     free(buffer);
144     return 1;
145 }
146
147
148 /*
149  * Generate a string of the size indicated, call xstrdup on it, and then
150  * ensure the result matches.  Returns true on success, false on any failure.
151  */
152 static int
153 test_strdup(size_t size)
154 {
155     char *string, *copy;
156     int match;
157
158     string = xmalloc(size);
159     if (string == NULL)
160         return 0;
161     memset(string, 1, size - 1);
162     string[size - 1] = '\0';
163     copy = xstrdup(string);
164     if (copy == NULL)
165         return 0;
166     match = strcmp(string, copy);
167     free(string);
168     free(copy);
169     return (match == 0);
170 }
171
172
173 /*
174  * Generate a string of the size indicated plus some, call xstrndup on it, and
175  * then ensure the result matches.  Also test xstrdup on a string that's
176  * shorter than the specified size and ensure that we don't copy too much, and
177  * on a string that's not nul-terminated.  Returns true on success, false on
178  * any failure.
179  */
180 static int
181 test_strndup(size_t size)
182 {
183     char *string, *copy;
184     int shortmatch, nonulmatch, match, toomuch;
185
186     /* Copy a short string. */
187     string = xmalloc(5);
188     memcpy(string, "test", 5);
189     copy = xstrndup(string, size);
190     shortmatch = strcmp(string, copy);
191     free(string);
192     free(copy);
193
194     /* Copy a string that's not nul-terminated. */
195     string = xmalloc(4);
196     memcpy(string, "test", 4);
197     copy = xstrndup(string, 4);
198     nonulmatch = strcmp(copy, "test");
199     free(string);
200     free(copy);
201
202     /* Now the test of running out of memory. */
203     string = xmalloc(size + 1);
204     if (string == NULL)
205         return 0;
206     memset(string, 1, size - 1);
207     string[size - 1] = 2;
208     string[size] = '\0';
209     copy = xstrndup(string, size - 1);
210     if (copy == NULL)
211         return 0;
212     match = strncmp(string, copy, size - 1);
213     toomuch = strcmp(string, copy);
214     free(string);
215     free(copy);
216     return (shortmatch == 0 && nonulmatch == 0 && match == 0 && toomuch != 0);
217 }
218
219
220 /*
221  * Allocate the amount of memory given and check that it's all zeroed,
222  * returning true if that succeeded and false on any sort of detectable error.
223  */
224 static int
225 test_calloc(size_t size)
226 {
227     char *buffer;
228     size_t i, nelems;
229
230     nelems = size / 4;
231     if (nelems * 4 != size)
232         return 0;
233     buffer = xcalloc(nelems, 4);
234     if (buffer == NULL)
235         return 0;
236     for (i = 0; i < size; i++)
237         if (buffer[i] != 0)
238             return 0;
239     free(buffer);
240     return 1;
241 }
242
243
244 /*
245  * Test asprintf with a large string (essentially using it as strdup).
246  * Returns true if successful, false otherwise.
247  */
248 static int
249 test_asprintf(size_t size)
250 {
251     char *copy, *string;
252     size_t i;
253
254     string = xmalloc(size);
255     memset(string, 42, size - 1);
256     string[size - 1] = '\0';
257     xasprintf(&copy, "%s", string);
258     free(string);
259     for (i = 0; i < size - 1; i++)
260         if (copy[i] != 42)
261             return 0;
262     if (copy[size - 1] != '\0')
263         return 0;
264     free(copy);
265     return 1;
266 }
267
268
269 /* Wrapper around vasprintf to do the va_list stuff. */
270 static void __attribute__((__format__(printf, 2, 3)))
271 xvasprintf_wrapper(char **strp, const char *format, ...)
272 {
273     va_list args;
274
275     va_start(args, format);
276     xvasprintf(strp, format, args);
277     va_end(args);
278 }
279
280
281 /*
282  * Test vasprintf with a large string (essentially using it as strdup).
283  * Returns true if successful, false otherwise.
284  */
285 static int
286 test_vasprintf(size_t size)
287 {
288     char *copy, *string;
289     size_t i;
290
291     string = xmalloc(size);
292     memset(string, 42, size - 1);
293     string[size - 1] = '\0';
294     xvasprintf_wrapper(&copy, "%s", string);
295     free(string);
296     for (i = 0; i < size - 1; i++)
297         if (copy[i] != 42)
298             return 0;
299     if (copy[size - 1] != '\0')
300         return 0;
301     free(copy);
302     return 1;
303 }
304
305
306 /*
307  * Take the amount of memory to allocate in bytes as a command-line argument
308  * and call test_malloc with that amount of memory.
309  */
310 int
311 main(int argc, char *argv[])
312 {
313     size_t size, max;
314     size_t limit = 0;
315     int willfail = 0;
316     unsigned char code;
317
318     if (argc < 3)
319         die("Usage error.  Type, size, and limit must be given.");
320     errno = 0;
321     size = strtol(argv[2], 0, 10);
322     if (size == 0 && errno != 0)
323         sysdie("Invalid size");
324     errno = 0;
325     limit = strtol(argv[3], 0, 10);
326     if (limit == 0 && errno != 0)
327         sysdie("Invalid limit");
328
329     /* If the code is capitalized, install our customized error handler. */
330     code = argv[1][0];
331     if (isupper(code)) {
332         xmalloc_error_handler = test_handler;
333         code = (unsigned char) tolower(code);
334     }
335
336     /*
337      * Decide if the allocation should fail.  If it should, set willfail to 2,
338      * so that if it unexpectedly succeeds, we exit with a status indicating
339      * that the test should be skipped.
340      */
341     max = size;
342     if (code == 's' || code == 'n' || code == 'a' || code == 'v') {
343         max += size;
344         if (limit > 0)
345             limit += size;
346     }
347     if (limit > 0 && max > limit)
348         willfail = 2;
349
350     /*
351      * If a memory limit was given and we can set memory limits, set it.
352      * Otherwise, exit 2, signalling to the driver that the test should be
353      * skipped.  We do this here rather than in the driver due to some
354      * pathological problems with Linux (setting ulimit in the shell caused
355      * the shell to die).
356      */
357     if (limit > 0) {
358 #if HAVE_SETRLIMIT && defined(RLIMIT_AS)
359         struct rlimit rl;
360         void *tmp;
361         size_t test_size;
362
363         rl.rlim_cur = limit;
364         rl.rlim_max = limit;
365         if (setrlimit(RLIMIT_AS, &rl) < 0) {
366             syswarn("Can't set data limit to %lu", (unsigned long) limit);
367             exit(2);
368         }
369         if (size < limit || code == 'r' || code == 'y') {
370             test_size = (code == 'r' || code == 'y') ? 10 : size;
371             if (test_size == 0)
372                 test_size = 1;
373             tmp = malloc(test_size);
374             if (tmp == NULL) {
375                 syswarn("Can't allocate initial memory of %lu (limit %lu)",
376                         (unsigned long) test_size, (unsigned long) limit);
377                 exit(2);
378             }
379             free(tmp);
380         }
381 #else
382         warn("Data limits aren't supported.");
383         exit(2);
384 #endif
385     }
386
387     /* clang-format off */
388     switch (code) {
389     case 'c': exit(test_calloc(size) ? willfail : 1);
390     case 'm': exit(test_malloc(size) ? willfail : 1);
391     case 'r': exit(test_realloc(size) ? willfail : 1);
392     case 'y': exit(test_reallocarray(4, size / 4) ? willfail : 1);
393     case 's': exit(test_strdup(size) ? willfail : 1);
394     case 'n': exit(test_strndup(size) ? willfail : 1);
395     case 'a': exit(test_asprintf(size) ? willfail : 1);
396     case 'v': exit(test_vasprintf(size) ? willfail : 1);
397     default:  die("Unknown mode %c", argv[1][0]);
398     }
399     /* clang-format on */
400
401     exit(1);
402 }