Skip to content

fix double-free in CZString copy-assignment operator - #1706

Open
SABITHSAHEB wants to merge 2 commits into
open-source-parsers:masterfrom
SABITHSAHEB:czstring-copy-assign-double-free
Open

fix double-free in CZString copy-assignment operator#1706
SABITHSAHEB wants to merge 2 commits into
open-source-parsers:masterfrom
SABITHSAHEB:czstring-copy-assign-double-free

Conversation

@SABITHSAHEB

Copy link
Copy Markdown
Contributor

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 = b both 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.

@SABITHSAHEB

Copy link
Copy Markdown
Contributor Author

any update?

@greptile-apps

greptile-apps Bot commented Aug 20, 2026

Copy link
Copy Markdown

Greptile Summary

The PR replaces CZString’s shallow copy assignment with copy-and-swap and adds an owning-string regression test.

  • Deep-copies the source through the existing copy constructor.
  • Transfers the destination’s old allocation to a temporary for cleanup.
  • Adds copy-assignment coverage to runCZStringTests.
  • The new path exposes undefined behavior in the existing union-based swap helper.

Confidence Score: 4/5

The PR should not merge until string-backed copy assignment stops accessing the inactive index_ union member through CZString::swap.

The ownership strategy is sound, but every string copy assignment now reaches undefined behavior while transferring the packed policy and length metadata.

Files Needing Attention: src/lib_json/json_value.cpp

Important Files Changed

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]
Loading

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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Inactive union member access

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.

@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 32423622865

Coverage increased (+0.1%) to 90.022%

Details

  • Coverage increased (+0.1%) from the base build.
  • Patch coverage: 2 of 2 lines across 1 file are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 2741
Covered Lines: 2622
Line Coverage: 95.66%
Relevant Branches: 2641
Covered Branches: 2223
Branch Coverage: 84.17%
Branches in Coverage %: Yes
Coverage Strength: 23818.38 hits per line

💛 - Coveralls

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants