From 1d4c2ab15e1b31c52ada35685b56e8f940669b44 Mon Sep 17 00:00:00 2001 From: Aaran McGuire Date: Tue, 5 May 2026 19:53:09 +0100 Subject: [PATCH] Fix null pointer dereferences when allocation fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Six locations immediately dereference the result of calloc/malloc without checking whether the allocation succeeded. On memory-constrained systems or with crafted files, these are reliable crash bugs. - stata/readstat_dta_read.c: dta_init_variable() — calloc result used unconditionally; adds out_retval parameter and null guard - sas/readstat_sas7bdat_read.c: sas7bdat_parse_subheader_rdc() — malloc for RDC decompression buffer unchecked - sas/readstat_sas7bdat_read.c: readstat_parse_sas7bdat() — both ctx and hinfo calloc results used before any null check - readstat_writer.c: readstat_begin_writing_data() — writer->row malloc unchecked; every subsequent insert call would crash - readstat_writer.c: readstat_string_ref_init() — calloc for strL ref unchecked before dereferencing - sas/readstat_xport_read.c: xport_construct_format() — malloc for format conversion buffer unchecked --- src/readstat_writer.c | 2 ++ src/sas/readstat_sas7bdat_read.c | 6 ++++++ src/sas/readstat_xport_read.c | 1 + src/stata/readstat_dta_read.c | 11 +++++++++-- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/readstat_writer.c b/src/readstat_writer.c index c1e98907..3040e184 100644 --- a/src/readstat_writer.c +++ b/src/readstat_writer.c @@ -28,6 +28,7 @@ static int readstat_compare_string_refs(const void *elem1, const void *elem2) { readstat_string_ref_t *readstat_string_ref_init(const char *string) { size_t len = strlen(string) + 1; readstat_string_ref_t *ref = calloc(1, sizeof(readstat_string_ref_t) + len); + if (ref == NULL) return NULL; ref->first_o = -1; ref->first_v = -1; ref->len = len; @@ -140,6 +141,7 @@ static readstat_error_t readstat_begin_writing_data(readstat_writer_t *writer) { } writer->row_len = row_len; writer->row = malloc(writer->row_len); + if (writer->row == NULL) { retval = READSTAT_ERROR_MALLOC; goto cleanup; } if (writer->callbacks.begin_data) { retval = writer->callbacks.begin_data(writer); } diff --git a/src/sas/readstat_sas7bdat_read.c b/src/sas/readstat_sas7bdat_read.c index 5fc8444b..9d8107d2 100644 --- a/src/sas/readstat_sas7bdat_read.c +++ b/src/sas/readstat_sas7bdat_read.c @@ -513,6 +513,7 @@ static readstat_error_t sas7bdat_parse_subheader_rdc(const char *subheader, size readstat_error_t retval = READSTAT_OK; const unsigned char *input = (const unsigned char *)subheader; char *buffer = malloc(ctx->row_length); + if (buffer == NULL) return READSTAT_ERROR_MALLOC; char *output = buffer; while (input + 2 <= (const unsigned char *)subheader + len) { int i; @@ -1211,6 +1212,11 @@ readstat_error_t readstat_parse_sas7bdat(readstat_parser_t *parser, const char * sas7bdat_ctx_t *ctx = calloc(1, sizeof(sas7bdat_ctx_t)); sas_header_info_t *hinfo = calloc(1, sizeof(sas_header_info_t)); + if (ctx == NULL || hinfo == NULL) { + retval = READSTAT_ERROR_MALLOC; + goto cleanup; + } + ctx->handle = parser->handlers; ctx->input_encoding = parser->input_encoding; ctx->output_encoding = parser->output_encoding; diff --git a/src/sas/readstat_xport_read.c b/src/sas/readstat_xport_read.c index 6bd9ddbf..730b3346 100644 --- a/src/sas/readstat_xport_read.c +++ b/src/sas/readstat_xport_read.c @@ -281,6 +281,7 @@ static readstat_error_t xport_read_obs_header_record(xport_ctx_t *ctx) { static readstat_error_t xport_construct_format(char *dst, size_t dst_len, const char *src, size_t src_len, int width, int decimals) { char *format = malloc(4 * src_len + 1); + if (format == NULL) return READSTAT_ERROR_MALLOC; readstat_error_t retval = readstat_convert(format, 4 * src_len + 1, src, src_len, NULL); if (retval != READSTAT_OK) { diff --git a/src/stata/readstat_dta_read.c b/src/stata/readstat_dta_read.c index ce07c333..286e744a 100644 --- a/src/stata/readstat_dta_read.c +++ b/src/stata/readstat_dta_read.c @@ -36,9 +36,14 @@ static readstat_error_t dta_update_progress(dta_ctx_t *ctx) { } static readstat_variable_t *dta_init_variable(dta_ctx_t *ctx, int i, int index_after_skipping, - readstat_type_t type, size_t max_len) { + readstat_type_t type, size_t max_len, readstat_error_t *out_retval) { readstat_variable_t *variable = calloc(1, sizeof(readstat_variable_t)); + if (variable == NULL) { + if (out_retval) *out_retval = READSTAT_ERROR_MALLOC; + return NULL; + } + variable->type = type; variable->index = i; variable->index_after_skipping = index_after_skipping; @@ -952,7 +957,9 @@ static readstat_error_t dta_handle_variables(dta_ctx_t *ctx) { max_len = 0; } - ctx->variables[i] = dta_init_variable(ctx, i, index_after_skipping, type, max_len); + ctx->variables[i] = dta_init_variable(ctx, i, index_after_skipping, type, max_len, &retval); + if (ctx->variables[i] == NULL) + goto cleanup; const char *value_labels = NULL;