From bf5e15b9096a7a1fdc5c6b01251e5b1cdeae44c3 Mon Sep 17 00:00:00 2001 From: Conner Majic Date: Fri, 17 Apr 2026 08:23:22 -0600 Subject: [PATCH] solver-c: harden parser/io and numerical output entrypoints Improve stdin/file JSON ingestion safety and guard output paths against edge-case assumptions so solver binaries fail predictably instead of risking malformed reads. Made-with: Cursor --- solver-c/src/json_stdin.c | 45 +++++++++++++++++++++++---------- solver-c/src/main.c | 53 +++++++++++++++++++++++++++++++-------- solver-c/src/main_fea.c | 53 +++++++++++++++++++++++++++++++-------- 3 files changed, 116 insertions(+), 35 deletions(-) diff --git a/solver-c/src/json_stdin.c b/solver-c/src/json_stdin.c index 0d0f15f..52f34ed 100644 --- a/solver-c/src/json_stdin.c +++ b/solver-c/src/json_stdin.c @@ -9,30 +9,49 @@ #define JSON_MAX_SIZE (4 * 1024 * 1024) int solver_read_json_stdin(char **buffer_out, size_t *length_out) { - size_t cap = JSON_READ_CHUNK; + size_t cap = JSON_READ_CHUNK + 1; size_t len = 0; char *buf = (char *)malloc(cap); if (!buf) { return -1; } for (;;) { - size_t n = fread(buf + len, 1, JSON_READ_CHUNK, stdin); - len += n; - if (n < JSON_READ_CHUNK) { - break; - } - if (len + JSON_READ_CHUNK > JSON_MAX_SIZE) { - free(buf); - return -2; - } - if (len + JSON_READ_CHUNK > cap) { - cap *= 2; - char *nb = (char *)realloc(buf, cap); + size_t remaining = (cap > len) ? (cap - len - 1) : 0; + if (remaining == 0) { + if (cap > JSON_MAX_SIZE) { + free(buf); + return -2; + } + size_t next_cap = cap * 2; + if (next_cap > JSON_MAX_SIZE + 1) { + next_cap = JSON_MAX_SIZE + 1; + } + if (next_cap <= cap) { + free(buf); + return -2; + } + char *nb = (char *)realloc(buf, next_cap); if (!nb) { free(buf); return -3; } buf = nb; + cap = next_cap; + remaining = cap - len - 1; + } + size_t to_read = remaining < JSON_READ_CHUNK ? remaining : JSON_READ_CHUNK; + size_t n = fread(buf + len, 1, to_read, stdin); + len += n; + if (ferror(stdin)) { + free(buf); + return -4; + } + if (n < to_read) { + break; + } + if (len >= JSON_MAX_SIZE) { + free(buf); + return -2; } } buf[len] = '\0'; diff --git a/solver-c/src/main.c b/solver-c/src/main.c index 51e9e80..de15814 100644 --- a/solver-c/src/main.c +++ b/solver-c/src/main.c @@ -4,6 +4,40 @@ #include #include +static int read_json_file(const char *path, char **buf_out) { + FILE *fp = fopen(path, "rb"); + if (!fp) { + return -1; + } + if (fseek(fp, 0, SEEK_END) != 0) { + fclose(fp); + return -2; + } + long sz = ftell(fp); + if (sz < 0) { + fclose(fp); + return -3; + } + if (fseek(fp, 0, SEEK_SET) != 0) { + fclose(fp); + return -4; + } + char *buf = (char *)malloc((size_t)sz + 1); + if (!buf) { + fclose(fp); + return -5; + } + size_t n = fread(buf, 1, (size_t)sz, fp); + fclose(fp); + if (n != (size_t)sz) { + free(buf); + return -6; + } + buf[sz] = '\0'; + *buf_out = buf; + return 0; +} + static void print_json_output(const SolverInputs *inputs, const SolverOutputs *outputs) { printf("{\n"); printf(" \"pointCount\": %d,\n", outputs->point_count); @@ -33,8 +67,12 @@ static void print_json_output(const SolverInputs *inputs, const SolverOutputs *o } printf(" ],\n"); - double pmin = outputs->pump_position_m[0]; - double pmax = outputs->pump_position_m[0]; + double pmin = 0.0; + double pmax = 0.0; + if (outputs->point_count > 0) { + pmin = outputs->pump_position_m[0]; + pmax = outputs->pump_position_m[0]; + } for (int i = 1; i < outputs->point_count; i++) { if (outputs->pump_position_m[i] < pmin) pmin = outputs->pump_position_m[i]; if (outputs->pump_position_m[i] > pmax) pmax = outputs->pump_position_m[i]; @@ -143,18 +181,11 @@ int main(int argc, char **argv) { } } else if (argc == 2) { /* allow file path for tests */ - FILE *fp = fopen(argv[1], "rb"); - if (!fp) { + int rr = read_json_file(argv[1], &buf); + if (rr != 0) { fprintf(stderr, "usage: solver_main --stdin OR solver_main \n"); return 1; } - fseek(fp, 0, SEEK_END); - long sz = ftell(fp); - fseek(fp, 0, SEEK_SET); - buf = (char *)malloc((size_t)sz + 1); - fread(buf, 1, (size_t)sz, fp); - fclose(fp); - buf[sz] = '\0'; } else { fprintf(stderr, "usage: solver_main --stdin OR solver_main \n"); return 1; diff --git a/solver-c/src/main_fea.c b/solver-c/src/main_fea.c index d8eb4ce..539ff6f 100644 --- a/solver-c/src/main_fea.c +++ b/solver-c/src/main_fea.c @@ -4,6 +4,40 @@ #include #include +static int read_json_file(const char *path, char **buf_out) { + FILE *fp = fopen(path, "rb"); + if (!fp) { + return -1; + } + if (fseek(fp, 0, SEEK_END) != 0) { + fclose(fp); + return -2; + } + long sz = ftell(fp); + if (sz < 0) { + fclose(fp); + return -3; + } + if (fseek(fp, 0, SEEK_SET) != 0) { + fclose(fp); + return -4; + } + char *buf = (char *)malloc((size_t)sz + 1); + if (!buf) { + fclose(fp); + return -5; + } + size_t n = fread(buf, 1, (size_t)sz, fp); + fclose(fp); + if (n != (size_t)sz) { + free(buf); + return -6; + } + buf[sz] = '\0'; + *buf_out = buf; + return 0; +} + static void print_json_output(const SolverInputs *inputs, const SolverOutputs *outputs) { printf("{\n"); printf(" \"pointCount\": %d,\n", outputs->point_count); @@ -33,8 +67,12 @@ static void print_json_output(const SolverInputs *inputs, const SolverOutputs *o } printf(" ],\n"); - double pmin = outputs->pump_position_m[0]; - double pmax = outputs->pump_position_m[0]; + double pmin = 0.0; + double pmax = 0.0; + if (outputs->point_count > 0) { + pmin = outputs->pump_position_m[0]; + pmax = outputs->pump_position_m[0]; + } for (int i = 1; i < outputs->point_count; i++) { if (outputs->pump_position_m[i] < pmin) pmin = outputs->pump_position_m[i]; if (outputs->pump_position_m[i] > pmax) pmax = outputs->pump_position_m[i]; @@ -142,18 +180,11 @@ int main(int argc, char **argv) { return 1; } } else if (argc == 2) { - FILE *fp = fopen(argv[1], "rb"); - if (!fp) { + int rr = read_json_file(argv[1], &buf); + if (rr != 0) { fprintf(stderr, "usage: solver_fea_main --stdin OR solver_fea_main \n"); return 1; } - fseek(fp, 0, SEEK_END); - long sz = ftell(fp); - fseek(fp, 0, SEEK_SET); - buf = (char *)malloc((size_t)sz + 1); - fread(buf, 1, (size_t)sz, fp); - fclose(fp); - buf[sz] = '\0'; } else { fprintf(stderr, "usage: solver_fea_main --stdin OR solver_fea_main \n"); return 1;