* E-252: XSPICE xfer/file_source — fix two heap out-of-bounds writes in the file parsers
Found while sweeping the file-parser code models (d_state/d_source/xfer/ file_source); confirmed with AddressSanitizer. Unlike the earlier code-model finds (E-246/247/250, reads or bounded errors), these are out-of-bounds WRITES -- silent heap corruption. 1. xfer (analog/xfer/cfunc.mod, read_file): reads a transfer-function file (a Touchstone-style `#` option line then data). It sscanf's up to 9 values per line and a state machine stores every value (freq/real/imag triples), so a line with more than one record stores more than 3. The allocation check reserved only 3 (`if (i + 3 > size)`, ALLOC=1024), so a multi-record line wrote past the buffer at the 1024-double boundary (ASan: heap-buffer-overflow WRITE, cm_xfer). Fix: reserve the sscanf maximum of 9 (`if (i + 9 > size)`). 2. file_source (analog/file_source/cfunc.mod): stores one record per line -- a timepoint plus `size` channel values = stepsize (size+1) doubles -- but reserved only `size` (`vecallocated - size`), one short. At the reallocation boundary the final channel wrote one double past the end (ASan: heap-buffer-overflow WRITE, cm_filesource). Fix: reserve a full record (`- stepsize`). Both are heap OOB writes reachable from a valid-syntax netlist with a crafted data file; the release build corrupts adjacent heap silently rather than always crashing (UB either way). The sibling parsers were checked: d_source validates its per-line token count against the declared width, and d_state's fixed line buffer is fgets-bounded -- no analogous overrun. Code-model-only change: analog.cm regenerated via cmpp and redeployed under bin/*/codemodels/; the ngspice binary is unchanged. Verify (examples/filefix_examples, 4 checks x2 solvers): valid transfer-function and file_source files simulate; an xfer multi-record file and a boundary-crossing file_source file run without overrunning. Both reproduced under ASan and shown fixed. Full regression 208/208. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
6a9610efd4
commit
9ee0028132
|
|
@ -503,7 +503,11 @@ void cm_filesource(ARGS) /* structure holding parms, inputs, outputs, etc.
|
|||
tprev = t;
|
||||
|
||||
/* before storing, check if vector size is large enough.
|
||||
If not, add another 1000*size doubles */
|
||||
If not, add another 1000*size doubles. Each record appended below
|
||||
is a full stepsize (= size + 1: one timepoint plus `size` channel
|
||||
values), so reserve stepsize -- reserving only `size` left room for
|
||||
one fewer value than is written and overran the buffer by one
|
||||
double at the allocation boundary. */
|
||||
if (count > (int) loc->indata->vecallocated - stepsize) {
|
||||
loc->indata->vecallocated += (size_t) (size * 1000);
|
||||
void * const p = realloc(loc->indata->datavec,
|
||||
|
|
|
|||
|
|
@ -120,9 +120,13 @@ static double *read_file(const char *fn, int span, int offset,
|
|||
j = 0;
|
||||
}
|
||||
|
||||
/* Check allocation. */
|
||||
/* Check allocation. The store loop below can append up to one value
|
||||
per column on this line, and sscanf read up to 9 (count <= 9) -- a
|
||||
line with more than one data record stores more than the 3 of a
|
||||
single freq/real/imag triple. Reserve for the whole line (9) so a
|
||||
multi-record line cannot write past the buffer. */
|
||||
|
||||
if (i + 3 > size) {
|
||||
if (i + 9 > size) {
|
||||
size += ALLOC;
|
||||
file_data = realloc(file_data, size * sizeof(double));
|
||||
if (!file_data)
|
||||
|
|
|
|||
Loading…
Reference in New Issue