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
This commit is contained in:
2026-04-17 08:23:22 -06:00
parent 413965828f
commit bf5e15b909
3 changed files with 116 additions and 35 deletions

View File

@@ -9,30 +9,49 @@
#define JSON_MAX_SIZE (4 * 1024 * 1024) #define JSON_MAX_SIZE (4 * 1024 * 1024)
int solver_read_json_stdin(char **buffer_out, size_t *length_out) { 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; size_t len = 0;
char *buf = (char *)malloc(cap); char *buf = (char *)malloc(cap);
if (!buf) { if (!buf) {
return -1; return -1;
} }
for (;;) { for (;;) {
size_t n = fread(buf + len, 1, JSON_READ_CHUNK, stdin); size_t remaining = (cap > len) ? (cap - len - 1) : 0;
len += n; if (remaining == 0) {
if (n < JSON_READ_CHUNK) { if (cap > JSON_MAX_SIZE) {
break;
}
if (len + JSON_READ_CHUNK > JSON_MAX_SIZE) {
free(buf); free(buf);
return -2; return -2;
} }
if (len + JSON_READ_CHUNK > cap) { size_t next_cap = cap * 2;
cap *= 2; if (next_cap > JSON_MAX_SIZE + 1) {
char *nb = (char *)realloc(buf, cap); next_cap = JSON_MAX_SIZE + 1;
}
if (next_cap <= cap) {
free(buf);
return -2;
}
char *nb = (char *)realloc(buf, next_cap);
if (!nb) { if (!nb) {
free(buf); free(buf);
return -3; return -3;
} }
buf = nb; 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'; buf[len] = '\0';

View File

@@ -4,6 +4,40 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
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) { static void print_json_output(const SolverInputs *inputs, const SolverOutputs *outputs) {
printf("{\n"); printf("{\n");
printf(" \"pointCount\": %d,\n", outputs->point_count); printf(" \"pointCount\": %d,\n", outputs->point_count);
@@ -33,8 +67,12 @@ static void print_json_output(const SolverInputs *inputs, const SolverOutputs *o
} }
printf(" ],\n"); printf(" ],\n");
double pmin = outputs->pump_position_m[0]; double pmin = 0.0;
double pmax = outputs->pump_position_m[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++) { 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] < pmin) pmin = outputs->pump_position_m[i];
if (outputs->pump_position_m[i] > pmax) pmax = 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) { } else if (argc == 2) {
/* allow file path for tests */ /* allow file path for tests */
FILE *fp = fopen(argv[1], "rb"); int rr = read_json_file(argv[1], &buf);
if (!fp) { if (rr != 0) {
fprintf(stderr, "usage: solver_main --stdin OR solver_main <path.json>\n"); fprintf(stderr, "usage: solver_main --stdin OR solver_main <path.json>\n");
return 1; 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 { } else {
fprintf(stderr, "usage: solver_main --stdin OR solver_main <path.json>\n"); fprintf(stderr, "usage: solver_main --stdin OR solver_main <path.json>\n");
return 1; return 1;

View File

@@ -4,6 +4,40 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
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) { static void print_json_output(const SolverInputs *inputs, const SolverOutputs *outputs) {
printf("{\n"); printf("{\n");
printf(" \"pointCount\": %d,\n", outputs->point_count); printf(" \"pointCount\": %d,\n", outputs->point_count);
@@ -33,8 +67,12 @@ static void print_json_output(const SolverInputs *inputs, const SolverOutputs *o
} }
printf(" ],\n"); printf(" ],\n");
double pmin = outputs->pump_position_m[0]; double pmin = 0.0;
double pmax = outputs->pump_position_m[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++) { 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] < pmin) pmin = outputs->pump_position_m[i];
if (outputs->pump_position_m[i] > pmax) pmax = 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; return 1;
} }
} else if (argc == 2) { } else if (argc == 2) {
FILE *fp = fopen(argv[1], "rb"); int rr = read_json_file(argv[1], &buf);
if (!fp) { if (rr != 0) {
fprintf(stderr, "usage: solver_fea_main --stdin OR solver_fea_main <path.json>\n"); fprintf(stderr, "usage: solver_fea_main --stdin OR solver_fea_main <path.json>\n");
return 1; 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 { } else {
fprintf(stderr, "usage: solver_fea_main --stdin OR solver_fea_main <path.json>\n"); fprintf(stderr, "usage: solver_fea_main --stdin OR solver_fea_main <path.json>\n");
return 1; return 1;