Skip to content

Fix Buf::Resize leaving active pointer and capacity stale after growth - #72

Open
filacocta wants to merge 1 commit into
cwida:devfrom
filacocta:fix/buf-resize-growth-state
Open

filacocta wants to merge 1 commit into
cwida:devfrom
filacocta:fix/buf-resize-growth-state

Conversation

@filacocta

@filacocta filacocta commented Oct 18, 2026 •

Copy link
Copy Markdown

Why

Buf::Resize grows an owned buffer by allocating a larger block, copying, and freeing the old one, but it only updates m_owned_p. m_active_p keeps pointing at the freed block, and m_capacity keeps the old size.

For example, after Buf buf(2); buf.Resize(4);:

  • buf.Capacity() still returns 2, and buf.data() still returns the freed block.
  • A second buf.Resize(8) throws "Buffer is already swapped", because m_owned_p != m_active_p.
  • Append still checks the old capacity. With assertions disabled, it proceeds through the stale active pointer.

Relevance and scope

Buf::Resize is part of the exported Buf API. Our search of the upstream source at the tested revision found no production callers of this method. This PR corrects its behavior when called directly; we have not identified an affected application or established external usage. The regression tests demonstrate the API defect, rather than an observed failure in FastLanes' current internal workflows.

What changed

  • src/cor/lyt/buf.cpp: after replacing the owned allocation, also set m_active_p and m_capacity.
  • rust/vendor/fastlanes/src/cor/lyt/buf.cpp: the same two assignments, so the Rust crate does not keep the bug. Following Add MSVC and GCC support & add+improve CI Coverage (incl DEBUG runs) #69, only the affected vendored file is patched rather than running a full update-fastlanes-src refresh.
  • test/src/unit_tests/buf_resize_test.cpp (new, registered in unit_test): five tests covering growth, repeated growth, append after growth, a non-growth request, and resizing a swapped buffer.
  • test/src/quick_fuzz_tests/fuzz_config.json: base_seed 14 → 15 (make bump).

Unchanged: the used-byte count, the copy length (m_capacity bytes), the early return for non-growth, and the exception when resizing a swapped buffer.

Testing

On Linux:

  • unit_test built from scratch with GCC 15.2.0, Release. BufResize.* 5/5 passed, and the full binary passed 102/102 with no skips.
  • With only the buf.cpp change reverted, the three growth tests fail and the two controls pass (Clang 21.1.8).
  • An AddressSanitizer build that instruments buf.cpp reports a use-after-free when reading data() after growth on the original code, and is clean with the fix.
  • Rust: cargo build --release of fls-rs compiles the patched vendored source with Clang 21.1.8. It finished with no errors (10 minutes, serial jobs).
  • make verify (seed), scripts/header_check.py --check, and clang-format 14 on the changed C++ files all pass.

Not run locally: the other C++ suites (dataset, expression, reader, primitive, quick-fuzz), Python, and the Rust example, which needs libc++. The full Clang build of the test target stopped inside GoogleTest on -Wcharacter-conversion with Clang 21, before reaching this change. I'm relying on CI for those.

This change was developed with AI coding agents, which wrote the tests, ran the checks above, and drafted this description.

Resize replaced the owned allocation but left m_active_p on the freed
block and m_capacity at the old size. Update both, in the main source
and the Rust vendored copy, and add BufResize unit tests. Bump the fuzz
seed 14 -> 15.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@filacocta filacocta closed this Oct 19, 2026
@filacocta filacocta reopened this Oct 19, 2026
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.

1 participant