Skip to content

[RF] Fix dangling stack normSet after xRooNode::BuildHistogram - #22932

Open
guitargeek wants to merge 2 commits into
root-project:masterfrom
guitargeek:xroofit_leak
Open

[RF] Fix dangling stack normSet after xRooNode::BuildHistogram#22932
guitargeek wants to merge 2 commits into
root-project:masterfrom
guitargeek:xroofit_leak

Conversation

@guitargeek

@guitargeek guitargeek commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

BuildHistogram evaluates the model with a stack-local normSet. RooFit proxies only cache a bare pointer to the last normalization set and re-use it later (e.g. RooProduct::evaluate reads back _compRSet.nset()), so once normSet goes out of scope those proxies dangle into freed stack memory. A subsequent evaluation then reads it, reported by valgrind as "use of uninitialised value" in RooAbsReal::getValV (nset->uniqueId()).

sterilize() was meant to forget the passed normSet, but in >=6.27 that code is disabled (the uniqueId invalidity check only guards RooAbsPdf::_normSet, not the proxy pointers) and it only walks clients, whereas getVal() propagates the normSet down to the servers of the evaluated function. Reset the proxy normSet on the whole server tree of every function we called getVal() on, before normSet is destroyed.

Thanks @TomasDado for reporting the problem!

FYI, @will-cern. If this works, I'll also submit it upstream to your xRooFit repo.

Expose setProxyNormSet so that external code can make an object forget a
normalization set previously passed to `getVal()`. This is needed when that
set may not outlive the object (e.g. it lived on the caller's stack), since
the proxies only keep a bare pointer to it. Symmetric to the already-public
`RooAbsPdf::setActiveNormSet`.

🤖 Done with the help of AI.
BuildHistogram evaluates the model with a stack-local `normSet`. RooFit
proxies only cache a bare pointer to the last normalization set and re-use
it later (e.g. RooProduct::evaluate reads back _compRSet.nset()), so once
`normSet` goes out of scope those proxies dangle into freed stack memory. A
subsequent evaluation then reads it, reported by valgrind as "use of
uninitialised value" in RooAbsReal::getValV (nset->uniqueId()).

`sterilize()` was meant to forget the passed normSet, but in >=6.27 that
code is disabled (the uniqueId invalidity check only guards
`RooAbsPdf::_normSet`, not the proxy pointers) and it only walks
clients, whereas getVal() propagates the normSet down to the servers of
the evaluated function. Reset the proxy normSet on the whole server tree
of every function we called getVal() on, before `normSet` is destroyed.

🤖 Done with the help of AI.
@guitargeek
guitargeek requested a review from hageboeck as a code owner July 27, 2026 10:30
@guitargeek guitargeek self-assigned this Jul 27, 2026
@guitargeek guitargeek added the bug label Jul 27, 2026
@github-actions

Copy link
Copy Markdown

Test Results

    23 files      23 suites   3d 14h 29m 43s ⏱️
 3 877 tests  3 876 ✅ 0 💤 1 ❌
79 862 runs  79 861 ✅ 0 💤 1 ❌

For more details on these failures, see this check.

Results for commit 79ad705.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant