Skip to content

Fix svector use-after-free when the argument aliases an element - #2947

Open
gouarin wants to merge 1 commit into
xtensor-stack:masterfrom
gouarin:fix-svector-aliasing
Open

gouarin wants to merge 1 commit into
xtensor-stack:masterfrom
gouarin:fix-svector-aliasing

Conversation

@gouarin

@gouarin gouarin commented Oct 29, 2026

Copy link
Copy Markdown
Member

Checklist

  • The title and commit message(s) are descriptive.
  • Small commits made to fix your PR have been squashed to avoid history pollution.
  • Tests have been added for new features or bug fixes.
  • API of new functions and classes are documented (no public API changes).

Description

svector::push_back, insert(pos, value) and assign(n, value) read their argument after grow(). When the call reallocates, grow() frees the old heap buffer, so an argument that refers to an element of the same svector dangles:

xt::svector<std::size_t, 1> v = {1, 2, 3};  // heap buffer, size == capacity
v.push_back(v[0]);                          // reads v[0] from the freed buffer
v.insert(v.begin(), v[0]);                  // same
v.assign(5, v[0]);                          // same

AddressSanitizer reports a heap-use-after-free in each case. Without it, the inserted values are garbage.

insert() already adjusted the argument pointer when it fell inside the shifted range, but that did not cover the reallocation. That adjustment is also the source of the -Warray-bounds warning GCC 13 and 15 emit at that line: when the argument is a separate object, GCC follows the branch where it lies inside the vector and sees a read past it.

The three functions now copy the value before anything can move or free it. push_back only copies when it reallocates. insert() treats pos == end() like any other position, and the pointer adjustment is gone, along with its warnings.

svector.insert_aliased_element, svector.push_back_aliased_element and svector.assign_aliased_element cover these cases. On master all three fail under AddressSanitizer, and 6 of their checks also fail in a plain -O2 build. They pass with the fix.

svector::push_back, insert(pos, value) and assign(n, value) read their
argument after calling grow(). grow() frees the old heap buffer, so when
the argument refers to an element of the same svector and the call has
to reallocate, the read is a heap-use-after-free. On a full heap-backed
svector, v.push_back(v[0]), v.insert(v.begin(), v[0]) and
v.assign(5, v[0]) store garbage, and AddressSanitizer reports the read.

insert() already adjusted the argument pointer when it fell inside the
shifted range. That covered the shift but not the reallocation. When the
argument is a separate object, GCC 13 and 15 also follow the branch where
it lies inside the vector and warn that the adjusted pointer reads past
it (-Warray-bounds).

The three functions now copy the value before anything can move or free
it. push_back only makes the copy when it reallocates. insert() treats
pos == end() like any other position, and the pointer adjustment is
gone, along with its -Warray-bounds warnings.
@codspeed

codspeed Bot commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will regress 1 benchmark

1 warning

⚠️ Unable to measure the execution of 1 benchmark

This benchmark measured no execution time, so its result is not comparable and counts as unchanged. This usually happens when the compiler removes the code under test.

Prevent compiler optimizations

See affected benchmarks

⚡ 1 improved benchmark
❌ 1 regressed benchmark
✅ 253 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
❌ xshape_initializer[std\:\:array<std\:\:size_t, 4>] 151.7 ns 180.8 ns -16.13%
⚡ xshape_access[std\:\:array<std\:\:size_t, 4>] 154.4 ns 125.3 ns +23.28%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing gouarin:fix-svector-aliasing (7fc7e24) with master (57f3d07)

Open in CodSpeed

@gouarin gouarin mentioned this pull request Oct 29, 2026
3 of 4 tasks

This branch has not been deployed

No deployments
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