Uh oh!
There was an error while loading. Please reload this page.
Stop freeing the model properties array twice - #157
Merged
antonio-leblanc merged 1 commit intoAug 12, 2026
Conversation
`properties` is allocated by each model's own constructor with `new double[numProperties]` and freed by ~ForeFireModel. Sixteen flux models and two propagation models freed it again in their own destructor, so destroying any model that registers at least one property was a double free. The flux ones also used scalar `delete` on an array allocated with `new[]`. Removing the derived deletes and leaving it to the base class is the whole fix; no ownership changes. This has never been hit in production because FireDomain keeps its models in propModelsTable and fluxModelsTable and frees neither, so nothing destroys a model today. It is reachable from anything that does, including a test -- which is why the test that covers it comes with it: `every model can be destroyed` now runs all 33 models rather than the six that register no properties, and `destroying a model does not disturb the next one` allocates across a destruction, since a double free often surfaces as the next allocation coming back wrong rather than as an immediate abort. Verified by reintroducing a single delete: the suite aborts with "double free or corruption (!prev)" and doctest reports the case as CRASHED - SIGABRT.
This was referenced Aug 12, 2026
This was referenced Aug 12, 2026
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 freeto 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.
Stop freeing the model
propertiesarray twiceStacked on #156 — targets
test/model-unit-suite, so the diff below is only this change. Merge #156 first and GitHub will retarget this todevautomatically.propertiesis allocated by each model's own constructor withnew double[numProperties]and freed by~ForeFireModel. Sixteen flux models and two propagation models freed it again in their own destructor, so destroying any model that registers at least one property was a double free. The flux ones compounded it by using scalardeleteon an array allocated withnew[].Removing the derived deletes and leaving it to the base class is the whole fix. No ownership changes, no signature changes — 18 files, 22 lines deleted.
/* destructor (shoudn't be modified) */ HeatFluxBasicModel::~HeatFluxBasicModel() { - if ( properties != 0 ) delete properties; }Why this has never crashed anyone
FireDomainkeeps its models inpropModelsTableandfluxModelsTableand frees neither, so nothing destroys a model today — every model a simulation instantiates is leaked instead. The bug is unreachable in production and becomes reachable the moment anything does destroy one, including a test.That leak is still open after this PR. It is a separate change, and fixing it before this one would have turned a silent leak into a crash on every run.
The test comes with the fix
#156 deliberately limited its destructor case to the six models that register no properties, precisely because destroying the others was a double free. That restriction lifts here:
every model can be destroyednow runs all 33 models — 17 propagation, 16 flux — instead of two.destroying a model does not disturb the next oneallocates a model across a destruction and checks it comes back intact, because a double free often surfaces as the next allocation being corrupted rather than as an immediate abort.Verified by reintroducing a single
deleteintoForeFireV1HeatFluxModeland re-running:So the test genuinely holds the fix in place rather than merely passing alongside it. Worth noting the first attempt at that control used
heatFluxBasicand passed — it registers no properties, so the!= 0guard made the reintroduced delete a no-op. The bug only bites models that actually allocate.Verification
ctest3/3.tests/runffunchanged — KML and NetCDF both within tolerance.AddressSanitizer would have been the natural tool here, but glibc's own heap checking catches this one just as decisively, and the suite needs no special build to do it.