fix: bounds-check module lists and drop <sstream> from JsonSettings - #18
Merged
kinonn merged 1 commit intoSep 3, 2026
Merged
Conversation
SplitFlapDisplay::init()/reloadOffsets() sized their loops from numModules but indexed the address/offset vectors without checking their length: a short or malformed stored list read past the end of the vector. init() now falls back to the stock address chain (0x20+i) and zero offsets; reload keeps the module's current offset instead of zeroing it, which would physically re-home calibrated modules. getIntVector/getIntMatrix/putIntVector/putIntMatrix no longer use <sstream>, which dragged the C++ iostreams and locale machinery (and newlib's wide-char/float printf/scanf families) into the image: strtol and Arduino String do the same job. esp32_c3 flash drops from 58.9% to 49.3% of the no_ota partition (-196 KB); RAM -4.6 KB. The new parser is deliberately lenient where the old std::stoi threw: junk between numbers is skipped and out-of-range values clamp to 0 in place, so a malformed value can never throw while settings load. The bounds checks above replace the throw as the length-safety guarantee. Matrix row splitting on ';' preserves the old std::getline row semantics. Host tests: test/jsonsettings_parser_test.cpp covers round-trips, whitespace, empty entries, junk, overflow clamping and the never-throw contract against the real JsonSettings.cpp.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ports the two real fixes from jhoff#47 to this fork, rebuilt from first principles rather than cherry-picked — the PR's
JsonSettings.cpphunks do not apply here: this fork hasgetIntMatrix/putIntMatrix(absent upstream), which also used<sstream>, so the PR as written would not compile on this tree.1. Bounds-check the module address/offset lists (from the PR)
SplitFlapDisplay::init()andreloadOffsets()sized their loops fromnumModulesbut indexedsettingAddresses/settingOffsetswithout checking their length: a short or malformed stored list read past the end of the vector. Now:init(): missing entries fall back to the stock address chain (0x20 + i, the schema default formoduleAddresses) and zero offsets.reloadOffsets(): missing entries keep the module's current offset instead of zeroing it — zeroing would physically re-home modules whose calibration never changed. (The PR's approach would cause that re-home here.)charOffsetswas already guarded in both functions; the web/ESP-NOW call sites (SplitFlapEspNow.cpp,SplitFlapWebServer.cpp) were audited and are already fully bounds-checked.2. Drop
<sstream>fromJsonSettings(from the PR, extended)All four functions —
getIntVector,getIntMatrix,putIntVector,putIntMatrix— no longer usestd::istringstream/std::ostringstream/std::stoi, which dragged the C++ iostreams and locale machinery (and newlib's wide-char/floating-point printf/scanf families) into the image. Parsing isstrtol-based; serialization isStringconcatenation.Measured on
esp32_c3(no_ota.csv partition, clean A/B againstmain@394f331):-196 KB flash, −4.6 KB RAM.
Behavior changes (deliberate)
The old code threw
std::runtime_erroron junk/out-of-range values; the new parser is lenient so a malformed stored value can never throw while settings load:"1,x,3"→{1, 3}), never spins.0in place (position preserved) — keeps the old code's guarantee that absurd input can't produce a bogus large offset.;preserves the oldstd::getlinesemantics exactly: a separator emits the row before it even when empty; an empty tail after the last;emits nothing — row indexing stays positionally stable.The bounds checks in (1) replace the exception as the length-safety guarantee, matching the PR's design.
Testing
test/jsonsettings_parser_test.cpp(40 checks, all passing) compiles the realsrc/JsonSettings.cppagainst the stubs and drives the parser through the public API: round-trips, whitespace tolerance, empty/trailing separators, junk skipping, overflow clamping, adversarial never-throw inputs, and matrix row semantics.jsonsettings_test(37 checks),jsonsettings_threads_test(hammer PASS),splitflap_module_test(28 checks),motor_scheduler_test,step_math_test.pio run -e esp32_c3builds clean after the change.