xref: /freebsd/contrib/libdiff/compat/recallocarray.c (revision 0d0c8621fd181e507f0fb50ffcca606faf66a8c2)
1  /*	$OpenBSD: recallocarray.c,v 1.1 2017/03/06 18:44:21 otto Exp $	*/
2  /*
3   * Copyright (c) 2008, 2017 Otto Moerbeek <otto@drijf.net>
4   *
5   * Permission to use, copy, modify, and distribute this software for any
6   * purpose with or without fee is hereby granted, provided that the above
7   * copyright notice and this permission notice appear in all copies.
8   *
9   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16   */
17  
18  #include <errno.h>
19  #include <stdlib.h>
20  #include <stdint.h>
21  #include <string.h>
22  #include <unistd.h>
23  
24  /*
25   * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
26   * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
27   */
28  #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
29  
30  void *
31  recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size)
32  {
33  	size_t oldsize, newsize;
34  	void *newptr;
35  
36  	if (ptr == NULL)
37  		return calloc(newnmemb, size);
38  
39  	if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
40  	    newnmemb > 0 && SIZE_MAX / newnmemb < size) {
41  		errno = ENOMEM;
42  		return NULL;
43  	}
44  	newsize = newnmemb * size;
45  
46  	if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
47  	    oldnmemb > 0 && SIZE_MAX / oldnmemb < size) {
48  		errno = EINVAL;
49  		return NULL;
50  	}
51  	oldsize = oldnmemb * size;
52  
53  	/*
54  	 * Don't bother too much if we're shrinking just a bit,
55  	 * we do not shrink for series of small steps, oh well.
56  	 */
57  	if (newsize <= oldsize) {
58  		size_t d = oldsize - newsize;
59  
60  		if (d < oldsize / 2 && d < getpagesize()) {
61  			memset((char *)ptr + newsize, 0, d);
62  			return ptr;
63  		}
64  	}
65  
66  	newptr = malloc(newsize);
67  	if (newptr == NULL)
68  		return NULL;
69  
70  	if (newsize > oldsize) {
71  		memcpy(newptr, ptr, oldsize);
72  		memset((char *)newptr + oldsize, 0, newsize - oldsize);
73  	} else
74  		memcpy(newptr, ptr, newsize);
75  
76  	explicit_bzero(ptr, oldsize);
77  	free(ptr);
78  
79  	return newptr;
80  }
81