From 0f21d6b61cac7ba56b18d78aea6c2c27907f7d5e Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Sun, 1 Feb 2026 18:32:16 +0100 Subject: [PATCH] Introduce xfree_sized and xrealloc_sized This will allow prism to pass buffer sizes to the Ruby GC. It also helps avoid buffer overflow as it confirms the size was correctly tracked all the way until the buffer is freed. --- Doxyfile | 1 + ext/prism/extension.c | 4 +- include/prism/debug_allocator.h | 99 ++++++++++++++++++++++++++++++ include/prism/defines.h | 32 ++++++++-- prism.gemspec | 1 + src/options.c | 4 +- src/prism.c | 53 ++++++++-------- src/static_literals.c | 4 +- src/util/pm_buffer.c | 5 +- src/util/pm_constant_pool.c | 19 +++--- src/util/pm_integer.c | 17 ++--- src/util/pm_line_offset_list.c | 4 +- src/util/pm_list.c | 2 +- src/util/pm_string.c | 15 ++--- templates/ext/prism/api_node.c.erb | 2 +- templates/src/diagnostic.c.erb | 4 +- templates/src/node.c.erb | 10 ++- 17 files changed, 207 insertions(+), 69 deletions(-) create mode 100644 include/prism/debug_allocator.h diff --git a/Doxyfile b/Doxyfile index 8827dea28d..4ea648ed7a 100644 --- a/Doxyfile +++ b/Doxyfile @@ -24,6 +24,7 @@ OUTPUT_DIRECTORY = doc JAVADOC_AUTOBRIEF = YES OPTIMIZE_OUTPUT_FOR_C = YES INPUT = src src/util include include/prism include/prism/util +EXCLUDE = include/prism/debug_allocator.h HTML_OUTPUT = c SORT_MEMBER_DOCS = NO GENERATE_LATEX = NO diff --git a/ext/prism/extension.c b/ext/prism/extension.c index 03182c1a57..fcbc1e6c24 100644 --- a/ext/prism/extension.c +++ b/ext/prism/extension.c @@ -413,7 +413,7 @@ dump(int argc, VALUE *argv, VALUE self) { if (options.freeze) rb_obj_freeze(value); #ifdef PRISM_BUILD_DEBUG - xfree(dup); + xfree_sized(dup, length); #endif pm_string_free(&input); @@ -929,7 +929,7 @@ parse(int argc, VALUE *argv, VALUE self) { VALUE value = parse_input(&input, &options); #ifdef PRISM_BUILD_DEBUG - xfree(dup); + xfree_sized(dup, length); #endif pm_string_free(&input); diff --git a/include/prism/debug_allocator.h b/include/prism/debug_allocator.h new file mode 100644 index 0000000000..3e28a95efb --- /dev/null +++ b/include/prism/debug_allocator.h @@ -0,0 +1,99 @@ +/** + * @file debug_allocator.h + * + * Decorate allocation function to ensure sizes are correct. + */ +#ifndef PRISM_DEBUG_ALLOCATOR_H +#define PRISM_DEBUG_ALLOCATOR_H + +#include +#include +#include + +static inline void * +pm_debug_malloc(size_t size) +{ + size_t *memory = xmalloc(size + sizeof(size_t)); + memory[0] = size; + return memory + 1; +} + +static inline void * +pm_debug_calloc(size_t nmemb, size_t size) +{ + size_t total_size = nmemb * size; + void *ptr = pm_debug_malloc(total_size); + memset(ptr, 0, total_size); + return ptr; +} + +static inline void * +pm_debug_realloc(void *ptr, size_t size) +{ + if (ptr == NULL) { + return pm_debug_malloc(size); + } + + size_t *memory = (size_t *)ptr; + void *raw_memory = memory - 1; + memory = (size_t *)xrealloc(raw_memory, size + sizeof(size_t)); + memory[0] = size; + return memory + 1; +} + +static inline void +pm_debug_free(void *ptr) +{ + if (ptr != NULL) { + size_t *memory = (size_t *)ptr; + xfree(memory - 1); + } +} + +static inline void +pm_debug_free_sized(void *ptr, size_t old_size) +{ + if (ptr != NULL) { + size_t *memory = (size_t *)ptr; + if (old_size != memory[-1]) { + fprintf(stderr, "[BUG] buffer %p was allocated with size %lu but freed with size %lu\n", ptr, memory[-1], old_size); + abort(); + } + xfree_sized(memory - 1, old_size + sizeof(size_t)); + } +} + +static inline void * +pm_debug_realloc_sized(void *ptr, size_t size, size_t old_size) +{ + if (ptr == NULL) { + if (old_size != 0) { + fprintf(stderr, "[BUG] realloc_sized called with NULL pointer and old size %lu\n", old_size); + abort(); + } + return pm_debug_malloc(size); + } + + size_t *memory = (size_t *)ptr; + if (old_size != memory[-1]) { + fprintf(stderr, "[BUG] buffer %p was allocated with size %lu but realloced with size %lu\n", ptr, memory[-1], old_size); + abort(); + } + return pm_debug_realloc(ptr, size); +} + +#undef xmalloc +#undef xrealloc +#undef xcalloc +#undef xfree +#undef xrealloc_sized +#undef xfree_sized + +#define xmalloc pm_debug_malloc +#define xrealloc pm_debug_realloc +#define xcalloc pm_debug_calloc +#define xfree pm_debug_free +#define xrealloc_sized pm_debug_realloc_sized +#define xfree_sized pm_debug_free_sized + +#endif diff --git a/include/prism/defines.h b/include/prism/defines.h index f2e814fa1d..1c4e5fa053 100644 --- a/include/prism/defines.h +++ b/include/prism/defines.h @@ -161,10 +161,12 @@ * ``` * #ifndef PRISM_XALLOCATOR_H * #define PRISM_XALLOCATOR_H - * #define xmalloc my_malloc - * #define xrealloc my_realloc - * #define xcalloc my_calloc - * #define xfree my_free + * #define xmalloc my_malloc + * #define xrealloc my_realloc + * #define xcalloc my_calloc + * #define xfree my_free + * #define xrealloc_sized my_realloc_sized // (optional) + * #define xfree_sized my_free_sized // (optional) * #endif * ``` */ @@ -204,6 +206,28 @@ #endif #endif +#ifndef xfree_sized +/** + * The free_sized function that should be used. This can be overridden with the + * PRISM_XALLOCATOR define. + * If not defined, defaults to calling xfree. + */ + #define xfree_sized(p, s) xfree(((void)(s), (p))) +#endif + +#ifndef xrealloc_sized +/** + * The xrealloc_sized function that should be used. This can be overridden with the + * PRISM_XALLOCATOR define. + * If not defined, defaults to calling xrealloc. + */ + #define xrealloc_sized(p, ns, os) xrealloc((p), ((void)(os), (ns))) +#endif + +#ifdef PRISM_BUILD_DEBUG + #include "prism/debug_allocator.h" +#endif + /** * If PRISM_BUILD_MINIMAL is defined, then we're going to define every possible * switch that will turn off certain features of prism. diff --git a/prism.gemspec b/prism.gemspec index a155dc3da4..dde7e711e2 100644 --- a/prism.gemspec +++ b/prism.gemspec @@ -48,6 +48,7 @@ Gem::Specification.new do |spec| "ext/prism/extension.h", "include/prism.h", "include/prism/ast.h", + "include/prism/debug_allocator.h", "include/prism/defines.h", "include/prism/diagnostic.h", "include/prism/encoding.h", diff --git a/src/options.c b/src/options.c index 09d2a65a6c..961d52330f 100644 --- a/src/options.c +++ b/src/options.c @@ -226,10 +226,10 @@ pm_options_free(pm_options_t *options) { pm_string_free(&scope->locals[local_index]); } - xfree(scope->locals); + xfree_sized(scope->locals, scope->locals_count * sizeof(pm_string_t)); } - xfree(options->scopes); + xfree_sized(options->scopes, options->scopes_count * sizeof(pm_options_scope_t)); } /** diff --git a/src/prism.c b/src/prism.c index 4ab1ca4594..bfa91f4296 100644 --- a/src/prism.c +++ b/src/prism.c @@ -286,7 +286,7 @@ lex_mode_pop(pm_parser_t *parser) { } else { parser->lex_modes.index--; pm_lex_mode_t *prev = parser->lex_modes.current->prev; - xfree(parser->lex_modes.current); + xfree_sized(parser->lex_modes.current, sizeof(pm_lex_mode_t)); parser->lex_modes.current = prev; } } @@ -777,7 +777,7 @@ pm_parser_scope_shareable_constant_set(pm_parser_t *parser, pm_shareable_constan static void pm_locals_free(pm_locals_t *locals) { if (locals->capacity > 0) { - xfree(locals->locals); + xfree_sized(locals->locals, locals->capacity * sizeof(pm_local_t)); } } @@ -2956,7 +2956,7 @@ pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const // Here we're going to free the target, since it is no longer necessary. // However, we don't want to call `pm_node_destroy` because we want to keep // around all of its children since we just reused them. - xfree(target); + xfree_sized(target, sizeof(pm_call_node_t)); return node; } @@ -3010,7 +3010,7 @@ pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, cons // Here we're going to free the target, since it is no longer necessary. // However, we don't want to call `pm_node_destroy` because we want to keep // around all of its children since we just reused them. - xfree(target); + xfree_sized(target, sizeof(pm_call_node_t)); return node; } @@ -3040,7 +3040,7 @@ pm_call_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, // Here we're going to free the target, since it is no longer necessary. // However, we don't want to call `pm_node_destroy` because we want to keep // around all of its children since we just reused them. - xfree(target); + xfree_sized(target, sizeof(pm_call_node_t)); return node; } @@ -3071,7 +3071,7 @@ pm_index_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, // Here we're going to free the target, since it is no longer necessary. // However, we don't want to call `pm_node_destroy` because we want to keep // around all of its children since we just reused them. - xfree(target); + xfree_sized(target, sizeof(pm_call_node_t)); return node; } @@ -3101,7 +3101,7 @@ pm_call_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const // Here we're going to free the target, since it is no longer necessary. // However, we don't want to call `pm_node_destroy` because we want to keep // around all of its children since we just reused them. - xfree(target); + xfree_sized(target, sizeof(pm_call_node_t)); return node; } @@ -3132,7 +3132,7 @@ pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const // Here we're going to free the target, since it is no longer necessary. // However, we don't want to call `pm_node_destroy` because we want to keep // around all of its children since we just reused them. - xfree(target); + xfree_sized(target, sizeof(pm_call_node_t)); return node; } @@ -3164,7 +3164,7 @@ pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { // Here we're going to free the target, since it is no longer necessary. // However, we don't want to call `pm_node_destroy` because we want to keep // around all of its children since we just reused them. - xfree(target); + xfree_sized(target, sizeof(pm_call_node_t)); return node; } @@ -3192,7 +3192,7 @@ pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { // Here we're going to free the target, since it is no longer necessary. // However, we don't want to call `pm_node_destroy` because we want to keep // around all of its children since we just reused them. - xfree(target); + xfree_sized(target, sizeof(pm_call_node_t)); return node; } @@ -3873,7 +3873,8 @@ pm_double_parse(pm_parser_t *parser, const pm_token_t *token) { // First, get a buffer of the content. size_t length = (size_t) diff; - char *buffer = xmalloc(sizeof(char) * (length + 1)); + const size_t buffer_size = sizeof(char) * (length + 1); + char *buffer = xmalloc(buffer_size); memcpy((void *) buffer, token->start, length); // Next, determine if we need to replace the decimal point because of @@ -3908,7 +3909,7 @@ pm_double_parse(pm_parser_t *parser, const pm_token_t *token) { // is in a valid format. However it's good to be safe. if ((eptr != buffer + length) || (errno != 0 && errno != ERANGE)) { PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, token, PM_ERR_FLOAT_PARSE); - xfree((void *) buffer); + xfree_sized(buffer, buffer_size); return 0.0; } @@ -3931,7 +3932,7 @@ pm_double_parse(pm_parser_t *parser, const pm_token_t *token) { } // Finally we can free the buffer and return the value. - xfree((void *) buffer); + xfree_sized(buffer, buffer_size); return value; } @@ -4017,7 +4018,7 @@ pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) { digits[0] = '1'; if (fract_length > 1) memset(digits + 1, '0', fract_length - 1); pm_integer_parse(&node->denominator, PM_INTEGER_BASE_DEFAULT, digits, digits + fract_length); - xfree(digits); + xfree_sized(digits, length); pm_integers_reduce(&node->numerator, &node->denominator); return node; @@ -5521,7 +5522,7 @@ pm_multi_write_node_create(pm_parser_t *parser, pm_multi_target_node_t *target, // Explicitly do not call pm_node_destroy here because we want to keep // around all of the information within the MultiWriteNode node. - xfree(target); + xfree_sized(target, sizeof(pm_multi_target_node_t)); return node; } @@ -5646,7 +5647,7 @@ pm_numbered_reference_read_node_number(pm_parser_t *parser, const pm_token_t *to value = 0; } - xfree(digits); + xfree_sized(digits, sizeof(char) * (length + 1)); if ((errno == ERANGE) || (value > NTH_REF_MAX)) { PM_PARSER_WARN_FORMAT(parser, U32(start - parser->start), U32(length), PM_WARN_INVALID_NUMBERED_REFERENCE, (int) (length + 1), (const char *) token->start); @@ -6751,7 +6752,7 @@ pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const // We are explicitly _not_ using pm_node_destroy here because we don't want // to trash the unescaped string. We could instead copy the string if we // know that it is owned, but we're taking the fast path for now. - xfree(node); + xfree_sized(node, sizeof(pm_string_node_t)); return new_node; } @@ -6784,7 +6785,7 @@ pm_symbol_node_to_string_node(pm_parser_t *parser, pm_symbol_node_t *node) { // We are explicitly _not_ using pm_node_destroy here because we don't want // to trash the unescaped string. We could instead copy the string if we // know that it is owned, but we're taking the fast path for now. - xfree(node); + xfree_sized(node, sizeof(pm_symbol_node_t)); return new_node; } @@ -7247,7 +7248,7 @@ pm_parser_scope_pop(pm_parser_t *parser) { parser->current_scope = scope->previous; pm_locals_free(&scope->locals); pm_node_list_free(&scope->implicit_parameters); - xfree(scope); + xfree_sized(scope, sizeof(pm_scope_t)); } /******************************************************************************/ @@ -7847,7 +7848,7 @@ context_push(pm_parser_t *parser, pm_context_t context) { static void context_pop(pm_parser_t *parser) { pm_context_node_t *prev = parser->current_context->prev; - xfree(parser->current_context); + xfree_sized(parser->current_context, sizeof(pm_context_node_t)); parser->current_context = prev; } @@ -16639,7 +16640,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node } pm_hash_pattern_node_t *node = pm_hash_pattern_node_node_list_create(parser, &assocs, rest); - xfree(assocs.nodes); + xfree_sized(assocs.nodes, assocs.capacity * sizeof(pm_node_t *)); pm_static_literals_free(&keys); return node; @@ -17166,7 +17167,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag } } - xfree(nodes.nodes); + xfree_sized(nodes.nodes, nodes.capacity * sizeof(pm_node_t *)); } else if (leading_rest) { // Otherwise, if we parsed a single splat pattern, then we know we have // an array pattern, so we can go ahead and create that node. @@ -19453,7 +19454,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_interpolated_symbol_node_append(interpolated, first_string); pm_interpolated_symbol_node_append(interpolated, second_string); - xfree(current); + xfree_sized(current, sizeof(pm_symbol_node_t)); current = UP(interpolated); } else { assert(false && "unreachable"); @@ -19538,7 +19539,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_interpolated_symbol_node_append(interpolated, first_string); pm_interpolated_symbol_node_append(interpolated, second_string); - xfree(current); + xfree_sized(current, sizeof(pm_symbol_node_t)); current = UP(interpolated); } else { assert(false && "unreachable"); @@ -22252,7 +22253,7 @@ pm_comment_list_free(pm_list_t *list) { next = node->next; pm_comment_t *comment = (pm_comment_t *) node; - xfree(comment); + xfree_sized(comment, sizeof(pm_comment_t)); } } @@ -22267,7 +22268,7 @@ pm_magic_comment_list_free(pm_list_t *list) { next = node->next; pm_magic_comment_t *magic_comment = (pm_magic_comment_t *) node; - xfree(magic_comment); + xfree_sized(magic_comment, sizeof(pm_magic_comment_t)); } } diff --git a/src/static_literals.c b/src/static_literals.c index 58a08a8c03..f3a5650d31 100644 --- a/src/static_literals.c +++ b/src/static_literals.c @@ -183,7 +183,7 @@ pm_node_hash_insert(pm_node_hash_t *hash, const pm_static_literals_metadata_t *m } // Finally, free the old node list and update the hash. - xfree(hash->nodes); + xfree_sized(hash->nodes, hash->capacity * sizeof(pm_node_t *)); hash->nodes = new_nodes; hash->capacity = new_capacity; } @@ -221,7 +221,7 @@ pm_node_hash_insert(pm_node_hash_t *hash, const pm_static_literals_metadata_t *m */ static void pm_node_hash_free(pm_node_hash_t *hash) { - if (hash->capacity > 0) xfree(hash->nodes); + if (hash->capacity > 0) xfree_sized(hash->nodes, hash->capacity * sizeof(pm_node_t *)); } /** diff --git a/src/util/pm_buffer.c b/src/util/pm_buffer.c index 2136a7c43e..9e392427c6 100644 --- a/src/util/pm_buffer.c +++ b/src/util/pm_buffer.c @@ -50,6 +50,7 @@ pm_buffer_length(const pm_buffer_t *buffer) { static inline bool pm_buffer_append_length(pm_buffer_t *buffer, size_t length) { size_t next_length = buffer->length + length; + const size_t original_capacity = buffer->capacity; if (next_length > buffer->capacity) { if (buffer->capacity == 0) { @@ -60,7 +61,7 @@ pm_buffer_append_length(pm_buffer_t *buffer, size_t length) { buffer->capacity *= 2; } - buffer->value = xrealloc(buffer->value, buffer->capacity); + buffer->value = xrealloc_sized(buffer->value, buffer->capacity, original_capacity); if (buffer->value == NULL) return false; } @@ -353,5 +354,5 @@ pm_buffer_insert(pm_buffer_t *buffer, size_t index, const char *value, size_t le */ void pm_buffer_free(pm_buffer_t *buffer) { - xfree(buffer->value); + xfree_sized(buffer->value, buffer->capacity); } diff --git a/src/util/pm_constant_pool.c b/src/util/pm_constant_pool.c index 922ce6a18c..bde7f959ea 100644 --- a/src/util/pm_constant_pool.c +++ b/src/util/pm_constant_pool.c @@ -33,8 +33,13 @@ pm_constant_id_list_init_capacity(pm_constant_id_list_t *list, size_t capacity) bool pm_constant_id_list_append(pm_constant_id_list_t *list, pm_constant_id_t id) { if (list->size >= list->capacity) { + const size_t original_capacity = list->capacity; list->capacity = list->capacity == 0 ? 8 : list->capacity * 2; - list->ids = (pm_constant_id_t *) xrealloc(list->ids, sizeof(pm_constant_id_t) * list->capacity); + list->ids = (pm_constant_id_t *) xrealloc_sized( + list->ids, + sizeof(pm_constant_id_t) * list->capacity, + sizeof(pm_constant_id_t) * original_capacity + ); if (list->ids == NULL) return false; } @@ -71,7 +76,7 @@ pm_constant_id_list_includes(pm_constant_id_list_t *list, pm_constant_id_t id) { void pm_constant_id_list_free(pm_constant_id_list_t *list) { if (list->ids != NULL) { - xfree(list->ids); + xfree_sized(list->ids, list->capacity * sizeof(pm_constant_id_t)); } } @@ -165,7 +170,7 @@ pm_constant_pool_resize(pm_constant_pool_t *pool) { // pool->constants and pool->buckets are allocated out of the same chunk // of memory, with the buckets coming first. - xfree(pool->buckets); + xfree_sized(pool->buckets, pool->capacity * element_size); pool->constants = next_constants; pool->buckets = next_buckets; pool->capacity = next_capacity; @@ -257,12 +262,12 @@ pm_constant_pool_insert(pm_constant_pool_t *pool, const uint8_t *start, size_t l // an existing constant, then either way we don't want the given // memory. Either it's duplicated with the existing constant or // it's not necessary because we have a shared version. - xfree((void *) start); + xfree_sized((void *) start, length); } else if (bucket->type == PM_CONSTANT_POOL_BUCKET_OWNED) { // If we're attempting to insert a shared constant and the // existing constant is owned, then we can free the owned // constant and replace it with the shared constant. - xfree((void *) constant->start); + xfree_sized((void *) constant->start, constant->length); constant->start = start; bucket->type = (unsigned int) (type & 0x3); } @@ -334,9 +339,9 @@ pm_constant_pool_free(pm_constant_pool_t *pool) { // If an id is set on this constant, then we know we have content here. if (bucket->id != PM_CONSTANT_ID_UNSET && bucket->type == PM_CONSTANT_POOL_BUCKET_OWNED) { pm_constant_t *constant = &pool->constants[bucket->id - 1]; - xfree((void *) constant->start); + xfree_sized((void *) constant->start, constant->length); } } - xfree(pool->buckets); + xfree_sized(pool->buckets, pool->capacity * (sizeof(pm_constant_pool_bucket_t) + sizeof(pm_constant_t))); } diff --git a/src/util/pm_integer.c b/src/util/pm_integer.c index 4170ecc58d..2b77a4b5d2 100644 --- a/src/util/pm_integer.c +++ b/src/util/pm_integer.c @@ -374,7 +374,7 @@ pm_integer_convert_base(pm_integer_t *destination, const pm_integer_t *source, u } } - xfree(bigints); + xfree_sized(bigints, bigints_length * sizeof(pm_integer_t)); bigints = next_bigints; bigints_length = next_length; } @@ -383,7 +383,7 @@ pm_integer_convert_base(pm_integer_t *destination, const pm_integer_t *source, u destination->negative = source->negative; pm_integer_normalize(destination); - xfree(bigints); + xfree_sized(bigints, bigints_length * sizeof(pm_integer_t)); pm_integer_free(&base); } @@ -422,7 +422,7 @@ pm_integer_parse_powof2(pm_integer_t *integer, uint32_t base, const uint8_t *dig static void pm_integer_parse_decimal(pm_integer_t *integer, const uint8_t *digits, size_t digits_length) { const size_t batch = 9; - size_t length = (digits_length + batch - 1) / batch; + const size_t length = (digits_length + batch - 1) / batch; uint32_t *values = (uint32_t *) xcalloc(length, sizeof(uint32_t)); uint32_t value = 0; @@ -439,7 +439,7 @@ pm_integer_parse_decimal(pm_integer_t *integer, const uint8_t *digits, size_t di // Convert base from 10**9 to 1<<32. pm_integer_convert_base(integer, &((pm_integer_t) { .length = length, .values = values, .value = 0, .negative = false }), 1000000000, ((uint64_t) 1 << 32)); - xfree(values); + xfree_sized(values, length * sizeof(uint32_t)); } /** @@ -448,7 +448,8 @@ pm_integer_parse_decimal(pm_integer_t *integer, const uint8_t *digits, size_t di static void pm_integer_parse_big(pm_integer_t *integer, uint32_t multiplier, const uint8_t *start, const uint8_t *end) { // Allocate an array to store digits. - uint8_t *digits = xmalloc(sizeof(uint8_t) * (size_t) (end - start)); + const size_t digits_capa = sizeof(uint8_t) * (size_t) (end - start); + uint8_t *digits = xmalloc(digits_capa); size_t digits_length = 0; for (; start < end; start++) { @@ -463,7 +464,7 @@ pm_integer_parse_big(pm_integer_t *integer, uint32_t multiplier, const uint8_t * pm_integer_parse_powof2(integer, multiplier, digits, digits_length); } - xfree(digits); + xfree_sized(digits, digits_capa); } /** @@ -635,7 +636,7 @@ pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer) { } // Allocate a buffer that we'll copy the decimal digits into. - size_t digits_length = converted.length * 9; + const size_t digits_length = converted.length * 9; char *digits = xcalloc(digits_length, sizeof(char)); if (digits == NULL) return; @@ -654,7 +655,7 @@ pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer) { // Finally, append the string to the buffer and free the digits. pm_buffer_append_string(buffer, digits + start_offset, digits_length - start_offset); - xfree(digits); + xfree_sized(digits, sizeof(char) * digits_length); pm_integer_free(&converted); } diff --git a/src/util/pm_line_offset_list.c b/src/util/pm_line_offset_list.c index 710fa4b788..d55b2f6874 100644 --- a/src/util/pm_line_offset_list.c +++ b/src/util/pm_line_offset_list.c @@ -39,7 +39,7 @@ pm_line_offset_list_append(pm_line_offset_list_t *list, uint32_t cursor) { if (list->offsets == NULL) return false; memcpy(list->offsets, original_offsets, list->size * sizeof(uint32_t)); - xfree(original_offsets); + xfree_sized(original_offsets, list->size * sizeof(uint32_t)); } assert(list->size == 0 || cursor > list->offsets[list->size - 1]); @@ -109,5 +109,5 @@ pm_line_offset_list_line_column(const pm_line_offset_list_t *list, uint32_t curs */ void pm_line_offset_list_free(pm_line_offset_list_t *list) { - xfree(list->offsets); + xfree_sized(list->offsets, list->capacity * sizeof(uint32_t)); } diff --git a/src/util/pm_list.c b/src/util/pm_list.c index ad2294cd60..940baffb64 100644 --- a/src/util/pm_list.c +++ b/src/util/pm_list.c @@ -41,7 +41,7 @@ pm_list_free(pm_list_t *list) { while (node != NULL) { next = node->next; - xfree(node); + xfree_sized(node, sizeof(pm_list_node_t)); node = next; } diff --git a/src/util/pm_string.c b/src/util/pm_string.c index a7493c468b..5ba8c78ec1 100644 --- a/src/util/pm_string.c +++ b/src/util/pm_string.c @@ -71,9 +71,10 @@ pm_string_file_handle_open(pm_string_file_handle_t *handle, const char *filepath int length = MultiByteToWideChar(CP_UTF8, 0, filepath, -1, NULL, 0); if (length == 0) return PM_STRING_INIT_ERROR_GENERIC; - handle->path = xmalloc(sizeof(WCHAR) * ((size_t) length)); + const size_t path_size = sizeof(WCHAR) * ((size_t) length); + handle->path = xmalloc(path_size); if ((handle->path == NULL) || (MultiByteToWideChar(CP_UTF8, 0, filepath, -1, handle->path, length) == 0)) { - xfree(handle->path); + xfree_sized(handle->path, path_size); return PM_STRING_INIT_ERROR_GENERIC; } @@ -88,7 +89,7 @@ pm_string_file_handle_open(pm_string_file_handle_t *handle, const char *filepath } } - xfree(handle->path); + xfree_sized(handle->path, path_size); return result; } @@ -215,7 +216,7 @@ pm_string_file_init(pm_string_t *string, const char *filepath) { if (result != PM_STRING_INIT_SUCCESS) return result; // Get the file size. - DWORD file_size = GetFileSize(handle.file, NULL); + const DWORD file_size = GetFileSize(handle.file, NULL); if (file_size == INVALID_FILE_SIZE) { pm_string_file_handle_close(&handle); return PM_STRING_INIT_ERROR_GENERIC; @@ -245,7 +246,7 @@ pm_string_file_init(pm_string_t *string, const char *filepath) { // Check the number of bytes read if (bytes_read != file_size) { - xfree(source); + xfree_sized(source, file_size); pm_string_file_handle_close(&handle); return PM_STRING_INIT_ERROR_GENERIC; } @@ -281,7 +282,7 @@ pm_string_file_init(pm_string_t *string, const char *filepath) { return PM_STRING_INIT_SUCCESS; } - size_t length = (size_t) size; + const size_t length = (size_t) size; uint8_t *source = xmalloc(length); if (source == NULL) { close(fd); @@ -292,7 +293,7 @@ pm_string_file_init(pm_string_t *string, const char *filepath) { close(fd); if (bytes_read == -1) { - xfree(source); + xfree_sized(source, length); return PM_STRING_INIT_ERROR_GENERIC; } diff --git a/templates/ext/prism/api_node.c.erb b/templates/ext/prism/api_node.c.erb index 69f70240f8..e3bcf116cc 100644 --- a/templates/ext/prism/api_node.c.erb +++ b/templates/ext/prism/api_node.c.erb @@ -113,7 +113,7 @@ pm_node_stack_pop(pm_node_stack_node_t **stack) { const pm_node_t *visit = current->visit; *stack = current->prev; - xfree(current); + xfree_sized(current, sizeof(pm_node_stack_node_t)); return visit; } diff --git a/templates/src/diagnostic.c.erb b/templates/src/diagnostic.c.erb index 88f8525f80..a11c7893d3 100644 --- a/templates/src/diagnostic.c.erb +++ b/templates/src/diagnostic.c.erb @@ -488,7 +488,7 @@ pm_diagnostic_list_append_format(pm_list_t *list, uint32_t start, uint32_t lengt size_t message_length = (size_t) (result + 1); char *message = (char *) xmalloc(message_length); if (message == NULL) { - xfree(diagnostic); + xfree_sized(diagnostic, sizeof(pm_diagnostic_t)); return false; } @@ -519,7 +519,7 @@ pm_diagnostic_list_free(pm_list_t *list) { pm_diagnostic_t *next = (pm_diagnostic_t *) node->node.next; if (node->owned) xfree((void *) node->message); - xfree(node); + xfree_sized(node, sizeof(pm_diagnostic_t)); node = next; } diff --git a/templates/src/node.c.erb b/templates/src/node.c.erb index f1709a0249..e42a8e5b70 100644 --- a/templates/src/node.c.erb +++ b/templates/src/node.c.erb @@ -32,7 +32,11 @@ pm_node_list_grow(pm_node_list_t *list, size_t size) { next_capacity = double_capacity; } - pm_node_t **nodes = (pm_node_t **) xrealloc(list->nodes, sizeof(pm_node_t *) * next_capacity); + pm_node_t **nodes = (pm_node_t **) xrealloc_sized( + list->nodes, + sizeof(pm_node_t *) * next_capacity, + sizeof(pm_node_t *) * list->capacity + ); if (nodes == NULL) return false; list->nodes = nodes; @@ -79,7 +83,7 @@ pm_node_list_concat(pm_node_list_t *list, pm_node_list_t *other) { void pm_node_list_free(pm_node_list_t *list) { if (list->capacity > 0) { - xfree(list->nodes); + xfree_sized(list->nodes, sizeof(pm_node_t *) * list->capacity); *list = (pm_node_list_t) { 0 }; } } @@ -132,6 +136,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { <%- raise -%> <%- end -%> <%- end -%> + xfree_sized(node, sizeof(pm_<%= node.human %>_t)); break; } <%- end -%> @@ -140,7 +145,6 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { assert(false && "unreachable"); break; } - xfree(node); } /**