diff options
author | kp <kp@FreeBSD.org> | 2018-01-20 23:46:03 +0000 |
---|---|---|
committer | kp <kp@FreeBSD.org> | 2018-01-20 23:46:03 +0000 |
commit | 62408e87e823d2ddedd2b5c3859fecfa3e0530aa (patch) | |
tree | 1abd6a23b349e8f7fb99a77b977de77cf7c209d4 | |
parent | 0478c432b800de4237b5995a8e1a99ef45b48788 (diff) | |
download | FreeBSD-src-62408e87e823d2ddedd2b5c3859fecfa3e0530aa.zip FreeBSD-src-62408e87e823d2ddedd2b5c3859fecfa3e0530aa.tar.gz |
MFC r327674, r327796
Introduce mallocarray() in the kernel
Similar to calloc() the mallocarray() function checks for integer
overflows before allocating memory.
It does not zero memory, unless the M_ZERO flag is set.
Additionally, move the overflow check logic out to WOULD_OVERFLOW() for
consumers to have a common means of testing for overflowing allocations.
WOULD_OVERFLOW() should be a secondary check -- on 64-bit platforms, just
because an allocation won't overflow size_t does not mean it is a sane size
to request. Callers should be imposing reasonable allocation limits far,
far, below overflow.
Obtained from: OpenBSD
-rw-r--r-- | share/man/man9/malloc.9 | 20 | ||||
-rw-r--r-- | sys/kern/kern_malloc.c | 11 | ||||
-rw-r--r-- | sys/sys/malloc.h | 18 |
3 files changed, 48 insertions, 1 deletions
diff --git a/share/man/man9/malloc.9 b/share/man/man9/malloc.9 index 81d40dc..bd26600 100644 --- a/share/man/man9/malloc.9 +++ b/share/man/man9/malloc.9 @@ -29,7 +29,7 @@ .\" $NetBSD: malloc.9,v 1.3 1996/11/11 00:05:11 lukem Exp $ .\" $FreeBSD$ .\" -.Dd November 19, 2015 +.Dd January 10, 2018 .Dt MALLOC 9 .Os .Sh NAME @@ -45,6 +45,8 @@ .In sys/malloc.h .Ft void * .Fn malloc "unsigned long size" "struct malloc_type *type" "int flags" +.Ft void * +.Fn mallocarray "size_t nmemb" "size_t size" "struct malloc_type *type" "int flags" .Ft void .Fn free "void *addr" "struct malloc_type *type" .Ft void * @@ -64,6 +66,14 @@ object whose size is specified by .Fa size . .Pp The +.Fn mallocarray +function allocates uninitialized memory in kernel address space for an +array of +.Fa nmemb +entries whose size is specified by +.Fa size . +.Pp +The .Fn free function releases memory at address .Fa addr @@ -144,6 +154,7 @@ If the request cannot be immediately fulfilled, the current process is put to sleep to wait for resources to be released by other processes. The .Fn malloc , +.Fn mallocarray , .Fn realloc , and .Fn reallocf @@ -152,6 +163,13 @@ functions cannot return if .Dv M_WAITOK is specified. +if the multiplication of +.Fa nmemb +and +.Fa size +would cause an integer overflow, the +.Fn mallocarray +function induces a panic. .It Dv M_USE_RESERVE Indicates that the system can use its reserve of memory to satisfy the request. diff --git a/sys/kern/kern_malloc.c b/sys/kern/kern_malloc.c index e6bc60c..410a971 100644 --- a/sys/kern/kern_malloc.c +++ b/sys/kern/kern_malloc.c @@ -2,6 +2,7 @@ * Copyright (c) 1987, 1991, 1993 * The Regents of the University of California. * Copyright (c) 2005-2009 Robert N. M. Watson + * Copyright (c) 2008 Otto Moerbeek <otto@drijf.net> (mallocarray) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -526,6 +527,16 @@ malloc(unsigned long size, struct malloc_type *mtp, int flags) return ((void *) va); } +void * +mallocarray(size_t nmemb, size_t size, struct malloc_type *type, int flags) +{ + + if (WOULD_OVERFLOW(nmemb, size)) + panic("mallocarray: %zu * %zu overflowed", nmemb, size); + + return (malloc(size * nmemb, type, flags)); +} + /* * free: * diff --git a/sys/sys/malloc.h b/sys/sys/malloc.h index f94dd3b..70c33a8 100644 --- a/sys/sys/malloc.h +++ b/sys/sys/malloc.h @@ -39,6 +39,7 @@ #include <sys/queue.h> #include <sys/_lock.h> #include <sys/_mutex.h> +#include <machine/_limits.h> #define MINALLOCSIZE UMA_SMALLEST_UNIT @@ -175,6 +176,9 @@ void *contigmalloc(unsigned long size, struct malloc_type *type, int flags, void free(void *addr, struct malloc_type *type); void *malloc(unsigned long size, struct malloc_type *type, int flags) __malloc_like __result_use_check __alloc_size(1); +void *mallocarray(size_t nmemb, size_t size, struct malloc_type *type, + int flags) __malloc_like __result_use_check + __alloc_size(1) __alloc_size(2); void malloc_init(void *); int malloc_last_fail(void); void malloc_type_allocated(struct malloc_type *type, unsigned long size); @@ -187,6 +191,20 @@ void *reallocf(void *addr, unsigned long size, struct malloc_type *type, int flags) __result_use_check __alloc_size(2); struct malloc_type *malloc_desc2type(const char *desc); + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW (1UL << (sizeof(size_t) * 8 / 2)) +static inline bool +WOULD_OVERFLOW(size_t nmemb, size_t size) +{ + + return ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && __SIZE_T_MAX / nmemb < size); +} +#undef MUL_NO_OVERFLOW #endif /* _KERNEL */ #endif /* !_SYS_MALLOC_H_ */ |