Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions sys/include/ape/xalloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,24 @@ extern void xalloc_die(void);
extern void *xmalloc(size_t);
extern void *xcalloc(size_t, size_t);
extern void *xrealloc(void *, size_t);
extern void *xreallocarray(void *, size_t, size_t);
extern void *xnmalloc(size_t, size_t);
extern void *xnrealloc(void *, size_t, size_t); /* fixed resize to n*s */
extern void *xmemdup(const void *, size_t);
extern char *xcharalloc(size_t);
extern void *xzalloc(size_t);
extern char *xstrdup(const char *);
extern char *xstrndup(const char *, size_t);
extern void *xnrealloc(void *, size_t *, size_t);
extern void *x2nrealloc(void *, size_t *, size_t); /* growing: ~1.5x */
extern void *x2realloc(void *, size_t *);
extern void *xpalloc(void *, ptrdiff_t *, ptrdiff_t, ptrdiff_t, ptrdiff_t);

/* Convenience: typed versions */
#define XMALLOC(t, n) ((t *)xmalloc((n) * sizeof(t)))
#define XCALLOC(t, n) ((t *)xcalloc((n), sizeof(t)))
#define XREALLOC(t, p, n) ((t *)xrealloc((p), (n) * sizeof(t)))
#define XMALLOC(t, n) ((t *)xmalloc((n) * sizeof(t)))
#define XCALLOC(t, n) ((t *)xcalloc((n), sizeof(t)))
#define XREALLOC(t, p, n) ((t *)xrealloc((p), (n) * sizeof(t)))
#define XNMALLOC(n, t) ((t *)xnmalloc((n), sizeof(t)))
#define XNREALLOC(p, n, t) ((t *)xnrealloc((p), (n), sizeof(t)))

#ifdef __cplusplus
}
Expand Down
112 changes: 100 additions & 12 deletions sys/src/ape/lib/ap/malloc/xmalloc.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include <xalloc.h>

/* Called on allocation failure. Programs may override this. */
Expand Down Expand Up @@ -38,6 +39,31 @@ xrealloc(void *p, size_t n)
return p;
}

/* Reallocate P as an array of N objects of S bytes each, with error checking. */
void *
xreallocarray(void *p, size_t n, size_t s)
{
void *r = reallocarray(p, n, s);
if(!r && n != 0 && s != 0)
xalloc_die();
return r;
}
Comment on lines +44 to +50

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The function reallocarray is an OpenBSD extension and is not part of the standard Plan 9 APE library. Using it here will result in a link error. Since this is libap, you should provide a manual implementation that checks for overflow before calling realloc.

void *
xreallocarray(void *p, size_t n, size_t s)
{
	if(n != 0 && s != 0 && n > (size_t)-1 / s)
		xalloc_die();
	void *r = realloc(p, n * s);
	if(!r && n != 0 && s != 0)
		xalloc_die();
	return r;
}


/* Allocate an array of N objects of S bytes each, with error checking. */
void *
xnmalloc(size_t n, size_t s)
{
return xreallocarray(NULL, n, s);
}

/* Resize P to an array of N objects of S bytes each, with error checking.
Unlike x2nrealloc, this resizes to exactly N — it does not grow. */
void *
xnrealloc(void *p, size_t n, size_t s)
{
return xreallocarray(p, n, s);
}

void *
xmemdup(const void *src, size_t n)
{
Expand All @@ -46,6 +72,18 @@ xmemdup(const void *src, size_t n)
return p;
}

char *
xcharalloc(size_t n)
{
return xmalloc(n);
}

void *
xzalloc(size_t s)
{
return xcalloc(s, 1);
}

char *
xstrdup(const char *s)
{
Expand All @@ -64,20 +102,70 @@ xstrndup(const char *s, size_t n)
return p;
}

/*
* xnrealloc - realloc *pp to hold at least *pn elements of size s,
* doubling *pn each time. Useful for growing arrays.
*/
/* Grow P (array of *PN objects of S bytes) by ~1.5x, with error checking.
If P is null, allocate an initial block sized for the default fast path. */
void *
xnrealloc(void *p, size_t *pn, size_t s)
x2nrealloc(void *p, size_t *pn, size_t s)
{
size_t n = *pn;
if(n == 0)
n = 1;
else if(n > (size_t)-1 / 2 / s)
xalloc_die();
else
n *= 2;

if(!p) {
enum { DEFAULT_MXFAST = 64 * sizeof(size_t) / 4 };
if(!n) {
n = DEFAULT_MXFAST / s;
n += !n;
}
Comment on lines +114 to +117

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If s is 0, the division DEFAULT_MXFAST / s will cause a division-by-zero exception (SIGFPE). While s is typically a sizeof expression, a robust library implementation should guard against this.

		if(!n) {
			n = s ? DEFAULT_MXFAST / s : 0;
			n += !n;
		}

} else {
/* n = floor(1.5 * n) + 1, checking for overflow */
size_t incr = (n >> 1) + 1;
if(n > (size_t)-1 - incr)
xalloc_die();
n += incr;
}
p = xreallocarray(p, n, s);
*pn = n;
return p;
}

void *
x2realloc(void *p, size_t *ps)
{
return x2nrealloc(p, ps, 1);
}

/* Grow array PA of *PN items (each S bytes) by at least N_INCR_MIN items,
not exceeding N_MAX total (-1 = unlimited). */
void *
xpalloc(void *pa, ptrdiff_t *pn, ptrdiff_t n_incr_min, ptrdiff_t n_max, ptrdiff_t s)
{
enum { DEFAULT_MXFAST = 64 * sizeof(size_t) / 4 };
ptrdiff_t n0 = *pn;
ptrdiff_t n;
size_t nbytes;

/* Grow by ~50%; cap at n_max if set. */
n = n0 + (n0 >> 1) + 1;
if(n0 > (ptrdiff_t)((size_t)-1 / 2))
n = (ptrdiff_t)((size_t)-1 / 2); /* overflow guard */
if(0 <= n_max && n_max < n)
n = n_max;

nbytes = (size_t)n * (size_t)s;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The multiplication (size_t)n * (size_t)s can overflow size_t before the result is passed to xrealloc. This is a critical security risk as it could lead to a heap buffer overflow if a smaller-than-requested buffer is allocated. An explicit overflow check is required.

	if(s > 0 && (size_t)n > (size_t)-1 / (size_t)s)
		xalloc_die();
	nbytes = (size_t)n * (size_t)s;

/* If tiny, round up to DEFAULT_MXFAST */
if(nbytes < DEFAULT_MXFAST && s > 0) {
n = DEFAULT_MXFAST / s;
nbytes = (size_t)n * (size_t)s;
}

if(!pa)
*pn = 0;
if(n - n0 < n_incr_min) {
n = n0 + n_incr_min;
if(n < 0 || (0 <= n_max && n_max < n))
xalloc_die();
nbytes = (size_t)n * (size_t)s;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

Similar to the previous calculation, this multiplication must be guarded against overflow to ensure the integrity of the heap allocation.

		if(s > 0 && (size_t)n > (size_t)-1 / (size_t)s)
			xalloc_die();
		nbytes = (size_t)n * (size_t)s;

}
pa = xrealloc(pa, nbytes);
*pn = n;
return xrealloc(p, n * s);
return pa;
}
Loading