You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Multi-threaded RDataFrame::Snapshot writes unreadable baskets (heap-use-after-free in TBasket::CopyTo) #23550
Recently, after updating to the 6.40.05 version of the ROOT from version 6.38, I started getting the following type of error while reading (of some) trees created by Snapshot. I am compiling root from source, using branch v6-40-00-patches (pulled and recompiled today).
Error in TBasket::Streamer: The value of fNbytes is incorrect (-235009601) ; trying to recover by setting it to zero
Error in TBranch::GetBasket: File: filter_kpiee_tree_standard.root at byte:3214918, branch:acc_em_chod, entry:2, badread=1, nerrors=1, basketnumber=2
Error in TTreeFormula::TTreeFormula::TT_EVAL_INIT: Could not read entry (2) of leaf (acc_em_chod), r=(-1).
I was not able to find the source of the problem, but I tried today to give the problem to Claude, and it seems to be a ROOT bug. I attach here the issue report generated by Claude and a short reproducer. The issue appears only with ROOT::EnableImplicitMT (number of threads does not matter). The point is that the number of entries needs to be small and the tree name needs to be long enough to trigger the bug.
// Multi-threaded RDataFrame::Snapshot writes baskets that cannot be read back.
// Seen with ROOT 6.40.05 on Linux x86_64 (Debian 13, GCC 14.2).
//
// Run with: root -l -b -q snapshot_mt_basket_corruption.C
//
// Expected: all 10 entries of the snapshot can be read back.
// Observed: several entries fail with "TBasket::Streamer: The value of fNbytes is incorrect".
// - same result with ROOT::EnableImplicitMT(1)
// - no errors without implicit multi-threading
// - no errors with tree names of up to ~50 characters
void snapshot_mt_basket_corruption()
{
ROOT::EnableImplicitMT(4);
const std::string treeName(60, 'n');
const char *fileName = "snapshot_mt_basket_corruption.root";
ROOT::RDataFrame(1000000)
.Define("b", "rdfentry_ % 2 == 0")
.Filter("rdfentry_ % 100000 == 7") // 10 entries, each processed by a different task
.Snapshot(treeName, fileName, {"b"});
TFile f(fileName);
auto tree = f.Get<TTree>(treeName.c_str());
int unreadable = 0;
for (Long64_t i = 0; i < tree->GetEntries(); i++)
if (tree->GetEntry(i) < 0)
unreadable++;
std::cout << unreadable << " of " << tree->GetEntries() << " entries cannot be read back\n";
}
ROOT version
| Welcome to ROOT 6.40.05 https://root.cern |
| (c) 1995-2025, The ROOT Team; conception: R. Brun, F. Rademakers |
| Built for linuxx8664gcc on Sep 30 2026, 11:31:44 |
| From heads/v6-40-00-patches@v6-40-04-58-g05353987903 |
| With c++ (Debian 14.2.0-19) 14.2.0 std202302 |
| Try '.help'/'.?', '.demo', '.license', '.credits', '.quit'/'.q' |
Hi @vepadulano, I had a look at this and I think the cause is in the TTree I/O layer rather than in RDataFrame. When the merger fast-copies a small basket, TBasket::LoadBasketBuffers gives it a buffer sized for reading, and CopyTo then writes the key header into it. If the header doesn't fit, the buffer is reallocated and the old memory is what ends up in the file. It became visible with 619d396, which is also on the 6.40 patch branch and was backported to 6.38 as 62c0ddf.
I have a small fix with a regression test (tested on Linux, not with RDataFrame itself) and I would like to open a PR if possible. Thanks.
Activity
Elvand-Lie commentedon Oct 12, 2026
Hi @vepadulano, I had a look at this and I think the cause is in the TTree I/O layer rather than in RDataFrame. When the merger fast-copies a small basket,
TBasket::LoadBasketBuffersgives it a buffer sized for reading, andCopyTothen writes the key header into it. If the header doesn't fit, the buffer is reallocated and the old memory is what ends up in the file. It became visible with 619d396, which is also on the 6.40 patch branch and was backported to 6.38 as 62c0ddf.I have a small fix with a regression test (tested on Linux, not with RDataFrame itself) and I would like to open a PR if possible. Thanks.