Bug 925100: ensure a size is <= half of the maximum PRUint32 value
before passing it to NSPR memory allocation functions. r=rrelyea.
Adam Langley wrote the first version of the patch.
--- a/lib/util/secport.c
+++ b/lib/util/secport.c
@@ -64,50 +64,63 @@ unsigned long port_allocFailures;
/* locations for registering Unicode conversion functions.
* XXX is this the appropriate location? or should they be
* moved to client/server specific locations?
*/
PORTCharConversionFunc ucs4Utf8ConvertFunc;
PORTCharConversionFunc ucs2Utf8ConvertFunc;
PORTCharConversionWSwapFunc ucs2AsciiConvertFunc;
+/* NSPR memory allocation functions (PR_Malloc, PR_Calloc, and PR_Realloc)
+ * use the PRUint32 type for the size parameter. Before we pass a size_t or
+ * unsigned long size to these functions, we need to ensure it is <= half of
+ * the maximum PRUint32 value to avoid truncation and catch a negative size.
+ */
+#define MAX_SIZE (PR_UINT32_MAX >> 1)
+
void *
PORT_Alloc(size_t bytes)
{
- void *rv;
+ void *rv = NULL;
- /* Always allocate a non-zero amount of bytes */
- rv = (void *)PR_Malloc(bytes ? bytes : 1);
+ if (bytes <= MAX_SIZE) {
+ /* Always allocate a non-zero amount of bytes */
+ rv = PR_Malloc(bytes ? bytes : 1);
+ }
if (!rv) {
++port_allocFailures;
PORT_SetError(SEC_ERROR_NO_MEMORY);
}
return rv;
}
void *
PORT_Realloc(void *oldptr, size_t bytes)
{
- void *rv;
+ void *rv = NULL;
- rv = (void *)PR_Realloc(oldptr, bytes);
+ if (bytes <= MAX_SIZE) {
+ rv = PR_Realloc(oldptr, bytes);
+ }
if (!rv) {
++port_allocFailures;
PORT_SetError(SEC_ERROR_NO_MEMORY);
}
return rv;
}
void *
PORT_ZAlloc(size_t bytes)
{
- void *rv;
+ void *rv = NULL;
- /* Always allocate a non-zero amount of bytes */
- rv = (void *)PR_Calloc(1, bytes ? bytes : 1);
+ if (bytes <= MAX_SIZE) {
+ /* Always allocate a non-zero amount of bytes */
+ rv = PR_Calloc(1, bytes ? bytes : 1);
+ }
if (!rv) {
++port_allocFailures;
PORT_SetError(SEC_ERROR_NO_MEMORY);
}
return rv;
}
void
@@ -204,33 +217,35 @@ PORT_GetError(void)
* until the arenapool is destroyed, at which point all the arenas are
* freed or returned to a "free arena list", depending on their sizes.
*/
PLArenaPool *
PORT_NewArena(unsigned long chunksize)
{
PORTArenaPool *pool;
+ if (chunksize > MAX_SIZE) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
pool = PORT_ZNew(PORTArenaPool);
if (!pool) {
return NULL;
}
pool->magic = ARENAPOOL_MAGIC;
pool->lock = PZ_NewLock(nssILockArena);
if (!pool->lock) {
++port_allocFailures;
PORT_Free(pool);
return NULL;
}
PL_InitArenaPool(&pool->arena, "security", chunksize, sizeof(double));
return(&pool->arena);
}
-#define MAX_SIZE 0x7fffffffUL
-
void *
PORT_ArenaAlloc(PLArenaPool *arena, size_t size)
{
void *p = NULL;
PORTArenaPool *pool = (PORTArenaPool *)arena;
if (size <= 0) {
@@ -325,16 +340,21 @@ PORT_FreeArena(PLArenaPool *arena, PRBoo
}
void *
PORT_ArenaGrow(PLArenaPool *arena, void *ptr, size_t oldsize, size_t newsize)
{
PORTArenaPool *pool = (PORTArenaPool *)arena;
PORT_Assert(newsize >= oldsize);
+ if (newsize > MAX_SIZE) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
if (ARENAPOOL_MAGIC == pool->magic ) {
PZ_Lock(pool->lock);
/* Do we do a THREADMARK check here? */
PL_ARENA_GROW(ptr, arena, oldsize, ( newsize - oldsize ) );
PZ_Unlock(pool->lock);
} else {
PL_ARENA_GROW(ptr, arena, oldsize, ( newsize - oldsize ) );
}