xref: /openbsd-src/lib/libc/stdlib/recallocarray.c (revision 2cb7cdfc213f61a57def00eb11d6dce9bd9d43ed)
1*2cb7cdfcSclaudio /*	$OpenBSD: recallocarray.c,v 1.2 2021/03/18 11:16:58 claudio Exp $	*/
23f0eb563Sotto /*
33f0eb563Sotto  * Copyright (c) 2008, 2017 Otto Moerbeek <otto@drijf.net>
43f0eb563Sotto  *
53f0eb563Sotto  * Permission to use, copy, modify, and distribute this software for any
63f0eb563Sotto  * purpose with or without fee is hereby granted, provided that the above
73f0eb563Sotto  * copyright notice and this permission notice appear in all copies.
83f0eb563Sotto  *
93f0eb563Sotto  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
103f0eb563Sotto  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
113f0eb563Sotto  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
123f0eb563Sotto  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
133f0eb563Sotto  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
143f0eb563Sotto  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
153f0eb563Sotto  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
163f0eb563Sotto  */
173f0eb563Sotto 
183f0eb563Sotto #include <errno.h>
193f0eb563Sotto #include <stdlib.h>
203f0eb563Sotto #include <stdint.h>
213f0eb563Sotto #include <string.h>
223f0eb563Sotto #include <unistd.h>
233f0eb563Sotto 
243f0eb563Sotto /*
253f0eb563Sotto  * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
263f0eb563Sotto  * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
273f0eb563Sotto  */
283f0eb563Sotto #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
293f0eb563Sotto 
303f0eb563Sotto void *
recallocarray(void * ptr,size_t oldnmemb,size_t newnmemb,size_t size)313f0eb563Sotto recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size)
323f0eb563Sotto {
333f0eb563Sotto 	size_t oldsize, newsize;
343f0eb563Sotto 	void *newptr;
353f0eb563Sotto 
363f0eb563Sotto 	if (ptr == NULL)
373f0eb563Sotto 		return calloc(newnmemb, size);
383f0eb563Sotto 
393f0eb563Sotto 	if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
403f0eb563Sotto 	    newnmemb > 0 && SIZE_MAX / newnmemb < size) {
413f0eb563Sotto 		errno = ENOMEM;
423f0eb563Sotto 		return NULL;
433f0eb563Sotto 	}
443f0eb563Sotto 	newsize = newnmemb * size;
453f0eb563Sotto 
463f0eb563Sotto 	if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
473f0eb563Sotto 	    oldnmemb > 0 && SIZE_MAX / oldnmemb < size) {
483f0eb563Sotto 		errno = EINVAL;
493f0eb563Sotto 		return NULL;
503f0eb563Sotto 	}
513f0eb563Sotto 	oldsize = oldnmemb * size;
523f0eb563Sotto 
533f0eb563Sotto 	/*
543f0eb563Sotto 	 * Don't bother too much if we're shrinking just a bit,
553f0eb563Sotto 	 * we do not shrink for series of small steps, oh well.
563f0eb563Sotto 	 */
573f0eb563Sotto 	if (newsize <= oldsize) {
583f0eb563Sotto 		size_t d = oldsize - newsize;
593f0eb563Sotto 
60*2cb7cdfcSclaudio 		if (d < oldsize / 2 && d < (size_t)getpagesize()) {
613f0eb563Sotto 			memset((char *)ptr + newsize, 0, d);
623f0eb563Sotto 			return ptr;
633f0eb563Sotto 		}
643f0eb563Sotto 	}
653f0eb563Sotto 
663f0eb563Sotto 	newptr = malloc(newsize);
673f0eb563Sotto 	if (newptr == NULL)
683f0eb563Sotto 		return NULL;
693f0eb563Sotto 
703f0eb563Sotto 	if (newsize > oldsize) {
713f0eb563Sotto 		memcpy(newptr, ptr, oldsize);
723f0eb563Sotto 		memset((char *)newptr + oldsize, 0, newsize - oldsize);
733f0eb563Sotto 	} else
743f0eb563Sotto 		memcpy(newptr, ptr, newsize);
753f0eb563Sotto 
763f0eb563Sotto 	explicit_bzero(ptr, oldsize);
773f0eb563Sotto 	free(ptr);
783f0eb563Sotto 
793f0eb563Sotto 	return newptr;
803f0eb563Sotto }
813f0eb563Sotto DEF_WEAK(recallocarray);
82