Skip to content

[io] Fix crash reading emulated collections of SSO strings - #22925

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

[io] Fix crash reading emulated collections of SSO strings#22925
guitargeek wants to merge 2 commits into
root-project:masterfrom
guitargeek:issue-20882

Conversation

@guitargeek

Copy link
Copy Markdown
Contributor

TEmulatedCollectionProxy stores the collection in a std::vector of raw aligned bytes. When that buffer grows past its capacity, Expand() lets it relocate the existing elements with a raw memory copy. That is correct for trivially relocatable content, but corrupts emulated objects that keep a pointer into themselves -- most notably a libstdc++ std::string (and TString) using the small-string optimization, whose internal data pointer keeps pointing into the old, now freed, buffer. The corrupted object then crashes when it is destroyed, freeing an invalid pointer.

TClass::Move(), invoked by Expand(), does not actually move the data for emulated classes, so it cannot fix up the relocated objects. Expand() is however only ever reached while preparing the collection to be entirely overwritten by a member-wise read, so when the buffer would reallocate and the value/key is a non-pointer class or string, destroy the (still valid) elements first and reconstruct all of them at the new location instead of relocating them.

This fixes the abort observed when reading e.g. a std::vector<std::pair<std::string,double>> without its dictionary.

Closes #20882

TEmulatedCollectionProxy stores the collection in a std::vector of raw
aligned bytes. When that buffer grows past its capacity, Expand() lets it
relocate the existing elements with a raw memory copy. That is correct for
trivially relocatable content, but corrupts emulated objects that keep a
pointer into themselves -- most notably a libstdc++ std::string (and TString)
using the small-string optimization, whose internal data pointer keeps
pointing into the old, now freed, buffer. The corrupted object then crashes
when it is destroyed, freeing an invalid pointer.

TClass::Move(), invoked by Expand(), does not actually move the data for
emulated classes, so it cannot fix up the relocated objects. Expand() is
however only ever reached while preparing the collection to be entirely
overwritten by a member-wise read, so when the buffer would reallocate and
the value/key is a non-pointer class or string, destroy the (still valid)
elements first and reconstruct all of them at the new location instead of
relocating them.

This fixes the abort observed when reading e.g. a
std::vector<std::pair<std::string,double>> without its dictionary.

Closes root-project#20882

🤖 Done with the help of AI.
The nolib test reads the STL containers back without the dictionary library,
exercising the emulated collection proxy. It was flagged WILLFAIL because it
crashed ROOT (and, before that, silently failed to compile) and its reference
file dated back to the CINT/Makefile era, so it never matched.

Now that the underlying crash is fixed (see previous commit), regenerate the
reference from the current, deterministic output and drop the WILLFAIL flag,
turning nolib into a real regression test that guards against the crash
reappearing.
@github-actions

Copy link
Copy Markdown

Test Results

    23 files      23 suites   3d 17h 22m 1s ⏱️
 3 876 tests  3 874 ✅ 0 💤 2 ❌
78 940 runs  78 937 ✅ 1 💤 2 ❌

For more details on these failures, see this check.

Results for commit 6226f3a.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[roottest] The stlNoLib test crashes when reading a tree without a dictionary

1 participant