Browse Source

realloc: handle zero-length arrays.

We saw a problem in devacpi.c when realloc was called
to create a zero-length array: it returned a nil pointer,
not a pointer to a zero-length array, which confused the caller.

Consider the difference between:
char *s = "";
char *s = nil;

The first is a pointer to a zero-length string, the second
a NULL pointer. We want realloc to implement the first,
rather than the second.

I realize the comment is a bit long but this is an easy thing for
someone to come along later and "optimize" -- i.e., break again.

Signed-off-by: Ronald G. Minnich <rminnich@gmail.com>
Ronald G. Minnich 7 years ago
parent
commit
bd9f11d216
1 changed files with 16 additions and 0 deletions
  1. 16 0
      sys/src/libc/port/reallocarray.c

+ 16 - 0
sys/src/libc/port/reallocarray.c

@@ -28,6 +28,22 @@
 void *
 reallocarray(void *optr, size_t nmemb, size_t size)
 {
+	/* If both size or nmemb are 0, and realloc is called with
+	 * 0, it's equivalent to freeing the memory. realloc is even
+	 * allowed to return nil. Hence this function needs to distinguish
+	 * between 'pointer to 0 length array' and 'nil pointer'. In the
+	 * event that nmemb * size is zero: free optr, and malloc(0),
+	 * i.e. return a pointer to a zero-length array.
+	 * It is entirely possible that it will return optr, since the allocators
+	 * tend to like to optimize that case, but the bookkeeping
+	 * will be correct: the allocator will know that optr points to a
+	 * 0-length allocation.
+	 */
+	if (nmemb == 0 || size == 0) {
+		free(optr);
+		return malloc(0);
+	}
+
 	if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
 	    nmemb > 0 && SIZE_MAX / nmemb < size) {
 		return nil;