fix double-free in CZString copy-assignment operator - #1706
Conversation
|
any update? |
|
| Filename | Overview |
|---|---|
| src/lib_json/json_value.cpp | Replaces shallow assignment with copy-and-swap, but the newly invoked swap accesses an inactive union member for string keys. |
| src/test_lib_json/main.cpp | Adds focused owning-string copy-assignment coverage that exercises source validity and destruction-time ownership behavior. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
A[Copy-assign string CZString] --> B[Construct deep-copy temporary]
B --> C[Call CZString::swap]
C --> D[Swap cstr pointers]
C --> E[Access inactive index union members]
E --> F[Undefined behavior while transferring policy and length]
D --> G[Temporary destroys old destination buffer]
Reviews (1): Last reviewed commit: "Merge branch 'master' into czstring-copy..." | Re-trigger Greptile
| // released once. The prior shallow copy aliased other.cstr_, double-freeing | ||
| // an owned string and leaking the overwritten one. | ||
| CZString temp(other); | ||
| swap(temp); |
There was a problem hiding this comment.
When a string-backed CZString is copy-assigned, the new call to swap(temp) unconditionally swaps index_ even though storage_ is the active union member, causing undefined behavior while transferring length and ownership metadata and enabling invalid comparisons, leaks, or invalid frees.
Coverage Report for CI Build 32423622865Coverage increased (+0.1%) to 90.022%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats💛 - Coveralls |
CZString is the key type behind Json::Value's object and array maps, and a key that owns its buffer (the duplicate policy) frees that buffer in its destructor. The copy-assignment operator only shallow-copied cstr_ and the packed index_/policy word, so after
a = bboth keys pointed at b's allocation while a's old buffer was never released. On scope exit both destructors free the same pointer, which AddressSanitizer reports as a double free, and the overwritten buffer leaks. I ran into it while filling out the CZString assignment coverage from #1654, which exercised the copy and move constructors and the move-assignment but not this operator. The move-assignment already releases then transfers correctly, so I made copy-assignment do the safe thing with copy-and-swap: the temporary deep-copies the source and takes our old buffer to be freed exactly once. Added the missing case to runCZStringTests, which double-frees under ASan before the change and passes after.