mem.c (6983B)
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20#include "guacamole/error.h" 21#include "guacamole/mem.h" 22#include "guacamole/private/mem.h" 23 24#include <stddef.h> 25#include <stdint.h> 26#include <stdlib.h> 27 28/* 29 * ============================================================================ 30 * 31 * IMPORTANT: For compatibility with past usages of libguac, all allocation 32 * functions implemented here need to remain compatible with the standard 33 * free() function, as there are past usages of libguac functions that expect 34 * allocated memory to have been allocated with malloc() or similar. Some good 35 * examples of this would be guac_strdup() or guac_user_parse_args_string(). 36 * 37 * It is OK if these allocation functions add new functionality beyond what 38 * malloc() provides, but care must be taken to ensure free() can still be used 39 * safely and without leaks, even if guac_mem_free() will always be preferred. 40 * 41 * It is further OK for guac_mem_free() to be incompatible with free() and only 42 * usable on memory blocks allocated through guac_mem_alloc() and similar. 43 * 44 * ============================================================================ 45 */ 46 47int PRIV_guac_mem_ckd_mul(size_t* result, size_t factor_count, const size_t* factors) { 48 49 /* Consider calculation invalid if no factors are provided at all */ 50 if (factor_count == 0) 51 return 1; 52 53 /* Multiply all provided factors together */ 54 size_t size = *(factors++); 55 while (--factor_count && size) { 56 57 size_t factor = *(factors++); 58 59 /* Fail if including this additional factor would exceed SIZE_MAX */ 60 size_t max_factor = SIZE_MAX / size; 61 if (factor > max_factor) 62 return 1; 63 64 size *= factor; 65 66 } 67 68 *result = size; 69 return 0; 70 71} 72 73int PRIV_guac_mem_ckd_add(size_t* result, size_t term_count, const size_t* terms) { 74 75 /* Consider calculation invalid if no terms are provided at all */ 76 if (term_count == 0) 77 return 1; 78 79 /* Multiply all provided terms together */ 80 size_t size = *(terms++); 81 while (--term_count) { 82 83 size_t term = *(terms++); 84 85 /* Fail if including this additional term would exceed SIZE_MAX */ 86 size_t max_term = SIZE_MAX - size; 87 if (term > max_term) 88 return 1; 89 90 size += term; 91 92 } 93 94 *result = size; 95 return 0; 96 97} 98 99int PRIV_guac_mem_ckd_sub(size_t* result, size_t term_count, const size_t* terms) { 100 101 /* Consider calculation invalid if no terms are provided at all */ 102 if (term_count == 0) 103 return 1; 104 105 /* Multiply all provided terms together */ 106 size_t size = *(terms++); 107 while (--term_count) { 108 109 size_t term = *(terms++); 110 111 /* Fail if including this additional term would wrap past zero */ 112 if (term > size) 113 return 1; 114 115 size -= term; 116 117 } 118 119 *result = size; 120 return 0; 121 122} 123 124size_t PRIV_guac_mem_ckd_mul_or_die(size_t factor_count, const size_t* factors) { 125 126 /* Perform request multiplication, aborting the entire process if the 127 * calculation overflows */ 128 size_t result = 0; 129 if (PRIV_guac_mem_ckd_mul(&result, factor_count, factors)) 130 abort(); 131 132 return result; 133 134} 135 136size_t PRIV_guac_mem_ckd_add_or_die(size_t term_count, const size_t* terms) { 137 138 /* Perform request addition, aborting the entire process if the calculation 139 * overflows */ 140 size_t result = 0; 141 if (PRIV_guac_mem_ckd_add(&result, term_count, terms)) 142 abort(); 143 144 return result; 145 146} 147 148size_t PRIV_guac_mem_ckd_sub_or_die(size_t term_count, const size_t* terms) { 149 150 /* Perform request subtraction, aborting the entire process if the 151 * calculation overflows */ 152 size_t result = 0; 153 if (PRIV_guac_mem_ckd_sub(&result, term_count, terms)) 154 abort(); 155 156 return result; 157 158} 159 160void* PRIV_guac_mem_alloc(size_t factor_count, const size_t* factors) { 161 162 size_t size = 0; 163 164 if (PRIV_guac_mem_ckd_mul(&size, factor_count, factors)) { 165 guac_error = GUAC_STATUS_NO_MEMORY; 166 return NULL; 167 } 168 else if (size == 0) 169 return NULL; 170 171 void* mem = malloc(size); 172 if (mem == NULL) { 173 /* C does not require that malloc() set errno (though POSIX does). For 174 * portability, we set guac_error here regardless of the underlying 175 * behavior of malloc(). */ 176 guac_error = GUAC_STATUS_NO_MEMORY; 177 } 178 179 return mem; 180 181} 182 183void* PRIV_guac_mem_zalloc(size_t factor_count, const size_t* factors) { 184 185 size_t size = 0; 186 187 if (PRIV_guac_mem_ckd_mul(&size, factor_count, factors)) { 188 guac_error = GUAC_STATUS_NO_MEMORY; 189 return NULL; 190 } 191 else if (size == 0) 192 return NULL; 193 194 void* mem = calloc(1, size); 195 if (mem == NULL) { 196 /* C does not require that calloc() set errno (though POSIX does). For 197 * portability, we set guac_error here regardless of the underlying 198 * behavior of calloc(). */ 199 guac_error = GUAC_STATUS_NO_MEMORY; 200 } 201 202 return mem; 203 204} 205 206void* PRIV_guac_mem_realloc(void* mem, size_t factor_count, const size_t* factors) { 207 208 size_t size = 0; 209 210 if (PRIV_guac_mem_ckd_mul(&size, factor_count, factors)) { 211 guac_error = GUAC_STATUS_NO_MEMORY; 212 return NULL; 213 } 214 215 /* Resize to 0 is equivalent to free() */ 216 if (size == 0) { 217 guac_mem_free(mem); 218 return NULL; 219 } 220 221 void* resized_mem = realloc(mem, size); 222 if (resized_mem == NULL) { 223 /* C does not require that realloc() set errno (though POSIX does). For 224 * portability, we set guac_error here regardless of the underlying 225 * behavior of realloc(). */ 226 guac_error = GUAC_STATUS_NO_MEMORY; 227 } 228 229 return resized_mem; 230 231} 232 233void* PRIV_guac_mem_realloc_or_die(void* mem, size_t factor_count, const size_t* factors) { 234 235 /* Reset any past errors for upcoming error check */ 236 guac_error = GUAC_STATUS_SUCCESS; 237 238 /* Perform requested resize, aborting the entire process if this cannot be 239 * done */ 240 void* resized_mem = PRIV_guac_mem_realloc(mem, factor_count, factors); 241 if (resized_mem == NULL && guac_error != GUAC_STATUS_SUCCESS) 242 abort(); 243 244 return resized_mem; 245 246} 247 248void PRIV_guac_mem_free(void* mem) { 249 free(mem); 250} 251