blob: 06735bf00f8c20742b61fdbe4c1b351e52e84d95 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "guacamole/assert.h"
#include "guacamole/error.h"
#include "guacamole/mem.h"
#include "guacamole/private/mem.h"
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
/*
* ============================================================================
*
* IMPORTANT: For compatibility with past usages of libguac, all allocation
* functions implemented here need to remain compatible with the standard
* free() function, as there are past usages of libguac functions that expect
* allocated memory to have been allocated with malloc() or similar. Some good
* examples of this would be guac_strdup() or guac_user_parse_args_string().
*
* It is OK if these allocation functions add new functionality beyond what
* malloc() provides, but care must be taken to ensure free() can still be used
* safely and without leaks, even if guac_mem_free() will always be preferred.
*
* It is further OK for guac_mem_free() to be incompatible with free() and only
* usable on memory blocks allocated through guac_mem_alloc() and similar.
*
* ============================================================================
*/
int PRIV_guac_mem_ckd_mul(size_t* result, size_t factor_count, const size_t* factors) {
/* Consider calculation invalid if no factors are provided at all */
if (factor_count == 0)
return 1;
/* Multiply all provided factors together */
size_t size = *(factors++);
while (--factor_count && size) {
size_t factor = *(factors++);
/* Fail if including this additional factor would exceed SIZE_MAX */
size_t max_factor = SIZE_MAX / size;
if (factor > max_factor)
return 1;
size *= factor;
}
*result = size;
return 0;
}
int PRIV_guac_mem_ckd_add(size_t* result, size_t term_count, const size_t* terms) {
/* Consider calculation invalid if no terms are provided at all */
if (term_count == 0)
return 1;
/* Multiply all provided terms together */
size_t size = *(terms++);
while (--term_count) {
size_t term = *(terms++);
/* Fail if including this additional term would exceed SIZE_MAX */
size_t max_term = SIZE_MAX - size;
if (term > max_term)
return 1;
size += term;
}
*result = size;
return 0;
}
int PRIV_guac_mem_ckd_sub(size_t* result, size_t term_count, const size_t* terms) {
/* Consider calculation invalid if no terms are provided at all */
if (term_count == 0)
return 1;
/* Multiply all provided terms together */
size_t size = *(terms++);
while (--term_count) {
size_t term = *(terms++);
/* Fail if including this additional term would wrap past zero */
if (term > size)
return 1;
size -= term;
}
*result = size;
return 0;
}
size_t PRIV_guac_mem_ckd_mul_or_die(size_t factor_count, const size_t* factors) {
/* Perform request multiplication, aborting the entire process if the
* calculation overflows */
size_t result = 0;
GUAC_ASSERT(!PRIV_guac_mem_ckd_mul(&result, factor_count, factors));
return result;
}
size_t PRIV_guac_mem_ckd_add_or_die(size_t term_count, const size_t* terms) {
/* Perform request addition, aborting the entire process if the calculation
* overflows */
size_t result = 0;
GUAC_ASSERT(!PRIV_guac_mem_ckd_add(&result, term_count, terms));
return result;
}
size_t PRIV_guac_mem_ckd_sub_or_die(size_t term_count, const size_t* terms) {
/* Perform request subtraction, aborting the entire process if the
* calculation overflows */
size_t result = 0;
GUAC_ASSERT(!PRIV_guac_mem_ckd_sub(&result, term_count, terms));
return result;
}
void* PRIV_guac_mem_alloc(size_t factor_count, const size_t* factors) {
size_t size = 0;
if (PRIV_guac_mem_ckd_mul(&size, factor_count, factors)) {
guac_error = GUAC_STATUS_NO_MEMORY;
return NULL;
}
else if (size == 0)
return NULL;
void* mem = malloc(size);
if (mem == NULL) {
/* C does not require that malloc() set errno (though POSIX does). For
* portability, we set guac_error here regardless of the underlying
* behavior of malloc(). */
guac_error = GUAC_STATUS_NO_MEMORY;
}
return mem;
}
void* PRIV_guac_mem_zalloc(size_t factor_count, const size_t* factors) {
size_t size = 0;
if (PRIV_guac_mem_ckd_mul(&size, factor_count, factors)) {
guac_error = GUAC_STATUS_NO_MEMORY;
return NULL;
}
else if (size == 0)
return NULL;
void* mem = calloc(1, size);
if (mem == NULL) {
/* C does not require that calloc() set errno (though POSIX does). For
* portability, we set guac_error here regardless of the underlying
* behavior of calloc(). */
guac_error = GUAC_STATUS_NO_MEMORY;
}
return mem;
}
void* PRIV_guac_mem_realloc(void* mem, size_t factor_count, const size_t* factors) {
size_t size = 0;
if (PRIV_guac_mem_ckd_mul(&size, factor_count, factors)) {
guac_error = GUAC_STATUS_NO_MEMORY;
return NULL;
}
/* Resize to 0 is equivalent to free() */
if (size == 0) {
guac_mem_free(mem);
return NULL;
}
void* resized_mem = realloc(mem, size);
if (resized_mem == NULL) {
/* C does not require that realloc() set errno (though POSIX does). For
* portability, we set guac_error here regardless of the underlying
* behavior of realloc(). */
guac_error = GUAC_STATUS_NO_MEMORY;
}
return resized_mem;
}
void* PRIV_guac_mem_realloc_or_die(void* mem, size_t factor_count, const size_t* factors) {
/* Reset any past errors for upcoming error check */
guac_error = GUAC_STATUS_SUCCESS;
/* Perform requested resize, aborting the entire process if this cannot be
* done */
void* resized_mem = PRIV_guac_mem_realloc(mem, factor_count, factors);
GUAC_ASSERT(resized_mem != NULL || guac_error == GUAC_STATUS_SUCCESS);
return resized_mem;
}
void PRIV_guac_mem_free(void* mem) {
free(mem);
}