Skip to content

fix request resources deallocation race - #6938

Open
yegorskii wants to merge 5 commits into
mainfrom
users/yegorskii/fix-buffer-deallocation-race
Open

fix request resources deallocation race#6938
yegorskii wants to merge 5 commits into
mainfrom
users/yegorskii/fix-buffer-deallocation-race

Conversation

@yegorskii

@yegorskii yegorskii commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Notes

Fix a use-after-free in the RDMA client that can occur when an endpoint
reconnects while a request is still holding buffers from the connection that's
being torn down.

The problem. On every reconnect TClientEndpoint::CreateQP() reinitializes
SendBuffers/RecvBuffers. TBufferPool::Init() replaces the whole internal
Impl, which destroys every TChunk of the previous generation — and
~TChunk() both deregisters the memory region and free()s the chunk memory.
A TPooledBuffer handed out earlier keeps a raw TChunk* and an address inside
that chunk, so after a reconnect both are dangling. Two places didn't know about
this:

  1. FreeRequest() (called from ~TRequest(), whenever the caller drops its
    TClientRequestPtr) unconditionally called
    SendBuffers.ReleaseBuffer()/RecvBuffers.ReleaseBuffer(). Request lifetime
    is controlled by the external caller, not by the endpoint, so a request that
    outlived a reconnect would dereference a freed TChunk.
  2. SendRequest() only checked CheckState(EEndpointState::Connected), not
    which pool generation the request's buffers came from. If the endpoint
    reconnected between AllocateRequest() and SendRequest() — a window
    invisible to the endpoint's own tracking, since the request isn't enqueued
    anywhere yet — a stale request would still be accepted, and its buffers used
    for IBV_WR_BIND_MW/PostSend or written to by AbortRequest().

The fix. Added BufferPoolGeneration, an atomic counter bumped in
CreateQP(). The bump, both Init() calls and the SendBuffer/RecvBuffer
acquisition now happen under AllocationLock, so "generation + pool contents"
is a single atomic unit for every thread; AllocateRequest() stamps the request
with the current generation inside the same lock, together with its buffer
acquisition. Three call sites check the stamp:

  • SendRequest() rejects requests from a stale generation, reporting
    RDMA_PROTO_FAIL through the new RejectStaleGenerationRequest() helper
    instead of AbortRequest() (which serializes the error into OutBuffer
    unsafe for a stale request). ResponseBuffer is pointed at a preallocated,
    pre-serialized E_RDMA_UNAVAILABLE error with static storage duration.
  • HandleQueuedRequests() repeats the check before StartRequest(). The
    caller's thread can be preempted between the check in SendRequest() and
    InputRequests.Enqueue(), so a request can land in the queue after
    AbortRequests() has already drained it and before the pools are replaced.
  • FreeRequest() skips releasing buffers for a stale generation and just drops
    them: the chunk they came from no longer exists, so there is nothing to return
    and nothing to leak in the current pool.

Also in this change: AbortRequest() now eagerly destroys both memory windows
(destroying a window invalidates it, which guarantees no remote write can
succeed afterwards), and SerializeError() gained a TString-returning
overload for the preallocated error above.

Issue

#5456

@yegorskii yegorskii added blockstore Add this label to run only cloud/blockstore build and tests on PR asan Launch builds with address sanitizer along with regular build tsan Launch builds with thread sanitizer along with regular build storage Add this label to run tests only from cloud/storage/ directory labels Aug 27, 2026
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Note

This is an automated comment that will be appended during run.

Note

All workloads for linux-x86_64-release-tsan have completed.

Tip

Planned checks for linux-x86_64-release-tsan.

  • 🚫 all (cancelled or timed out before reporting completion)

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Note

This is an automated comment that will be appended during run.

Note

All workloads for linux-x86_64-release-asan have completed.

Tip

Planned checks for linux-x86_64-release-asan.

  • 🚫 all (cancelled or timed out before reporting completion)

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Note

This is an automated comment that will be appended during run.

Note

All workloads for linux-x86_64-relwithdebinfo have completed.

Tip

Planned checks for linux-x86_64-relwithdebinfo.

  • 🚫 blockstore (cancelled or timed out before reporting completion)
  • 🚫 storage (cancelled or timed out before reporting completion)

@yegorskii
yegorskii force-pushed the users/yegorskii/fix-buffer-deallocation-race branch from bc9e08b to 7115fc9 Compare August 27, 2026 18:31
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Note

This is an automated comment that will be appended during run.

Note

All workloads for linux-x86_64-release-tsan have completed.

Tip

Planned checks for linux-x86_64-release-tsan.

🟢 linux-x86_64-release-tsan target: cloud/blockstore/,cloud/storage/ (test time: 1196s): all tests PASSED for commit 7115fc9.

TESTS PASSED ERRORS FAILED FAILED BUILD SKIPPED MUTED?
6394 6390 0 0 0 4 0

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Note

This is an automated comment that will be appended during run.

Note

All workloads for linux-x86_64-relwithdebinfo have completed.

Tip

Planned checks for linux-x86_64-relwithdebinfo.

🟢 linux-x86_64-relwithdebinfo target: cloud/storage/ (test time: 87s): all tests PASSED for commit 7115fc9.

TESTS PASSED ERRORS FAILED FAILED BUILD SKIPPED MUTED?
969 969 0 0 0 0 0

🟢 linux-x86_64-relwithdebinfo target: cloud/blockstore/ (test time: 1295s): all tests PASSED for commit 7115fc9.

TESTS PASSED ERRORS FAILED FAILED BUILD SKIPPED MUTED?
7085 7084 0 0 0 1 0

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Note

This is an automated comment that will be appended during run.

Note

All workloads for linux-x86_64-release-asan have completed.

Tip

Planned checks for linux-x86_64-release-asan.

🟢 linux-x86_64-release-asan target: cloud/blockstore/,cloud/storage/ (test time: 1290s): all tests PASSED for commit 7115fc9.

TESTS PASSED ERRORS FAILED FAILED BUILD SKIPPED MUTED?
6433 6433 0 0 0 0 0

// req->OutBuffer/OutMemoryWindow may belong to an already torn down
// pool generation/PD.
auto* handler = req->Handler.get();
handler->HandleResponse(std::move(req), RDMA_PROTO_FAIL, 0);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Did we check that all existing clients correctly handle responseBytes=0 case? E.g. not trying to parse the response?

// belong to an older pool generation and cannot be safely
// released via the current pool/PD, so just drop them without
// touching SendBuffers/RecvBuffers or calling ibv_dealloc_mw.
req->InMemoryWindow.release();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Wait, are we just leaking them? This feels wrong.

I'm not 100% sure that PD is ref-counted, but did we try to actually deallocate them? Even if calling ibv_dealloc_mw is unsafe, we need to at least free them

@tpashkin tpashkin Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

From the man

ibv_dereg_mr() fails if any memory window is still bound to this MR

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

And also

ibv_dealloc_pd() may fail if any other resource is still associated with the PD being freed

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Note

This is an automated comment that will be appended during run.

Note

All workloads for linux-x86_64-release-asan have completed.

Tip

Planned checks for linux-x86_64-release-asan.

  • 🔴 all (build failed, log)

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Note

This is an automated comment that will be appended during run.

Note

All workloads for linux-x86_64-relwithdebinfo have completed.

Tip

Planned checks for linux-x86_64-relwithdebinfo.

🔴 linux-x86_64-relwithdebinfo target: cloud/blockstore/ (test time: 1306s): some tests FAILED for commit b406ca2.

TESTS PASSED ERRORS FAILED FAILED BUILD SKIPPED MUTED?
7023 7021 0 0 1 1 0

🔴 linux-x86_64-relwithdebinfo target: cloud/blockstore/ (test time: 1079s): some tests FAILED for commit b406ca2.

TESTS PASSED ERRORS FAILED FAILED BUILD SKIPPED MUTED?
7023 7021 0 0 1 1 0

🔴 linux-x86_64-relwithdebinfo target: cloud/blockstore/ (test time: 886s): some tests FAILED for commit b406ca2.

TESTS PASSED ERRORS FAILED FAILED BUILD SKIPPED MUTED?
7023 7021 0 0 1 1 0

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Note

This is an automated comment that will be appended during run.

Note

All workloads for linux-x86_64-release-tsan have completed.

Tip

Planned checks for linux-x86_64-release-tsan.

  • 🔴 all (build failed, log)

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Note

This is an automated comment that will be appended during run.

Note

All workloads for linux-x86_64-release-asan have completed.

Tip

Planned checks for linux-x86_64-release-asan.

🟢 linux-x86_64-release-asan target: cloud/blockstore/,cloud/storage/ (test time: 1102s): all tests PASSED for commit ddbc658.

TESTS PASSED ERRORS FAILED FAILED BUILD SKIPPED MUTED?
6433 6433 0 0 0 0 0

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Note

This is an automated comment that will be appended during run.

Note

All workloads for linux-x86_64-release-tsan have completed.

Tip

Planned checks for linux-x86_64-release-tsan.

  • 🚫 all (cancelled or timed out before reporting completion)

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Note

This is an automated comment that will be appended during run.

Note

All workloads for linux-x86_64-relwithdebinfo have completed.

Tip

Planned checks for linux-x86_64-relwithdebinfo.

🟢 linux-x86_64-relwithdebinfo target: cloud/storage/ (test time: 86s): all tests PASSED for commit ddbc658.

TESTS PASSED ERRORS FAILED FAILED BUILD SKIPPED MUTED?
969 969 0 0 0 0 0

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Note

This is an automated comment that will be appended during run.

Note

All workloads for linux-x86_64-release-tsan have completed.

Tip

Planned checks for linux-x86_64-release-tsan.

  • ⚠️ all (job finished but report update failed)

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Note

This is an automated comment that will be appended during run.

Note

All workloads for linux-x86_64-release-asan have completed.

Tip

Planned checks for linux-x86_64-release-asan.

  • ⚠️ all (job finished but report update failed)

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Note

This is an automated comment that will be appended during run.

Note

All workloads for linux-x86_64-relwithdebinfo have completed.

Tip

Planned checks for linux-x86_64-relwithdebinfo.

  • ⚠️ blockstore (job finished but report update failed)
  • ⚠️ storage (job finished but report update failed)

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Note

This is an automated comment that will be appended during run.

Note

All workloads for linux-x86_64-relwithdebinfo have completed.

Tip

Planned checks for linux-x86_64-relwithdebinfo.

🟢 linux-x86_64-relwithdebinfo target: cloud/storage/ (test time: 84s): all tests PASSED for commit 9f38bee.

TESTS PASSED ERRORS FAILED FAILED BUILD SKIPPED MUTED?
969 969 0 0 0 0 0

🟢 linux-x86_64-relwithdebinfo target: cloud/blockstore/ (test time: 1337s): all tests PASSED for commit 9f38bee.

TESTS PASSED ERRORS FAILED FAILED BUILD SKIPPED MUTED?
7085 7084 0 0 0 1 0

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Note

This is an automated comment that will be appended during run.

Note

All workloads for linux-x86_64-release-asan have completed.

Tip

Planned checks for linux-x86_64-release-asan.

🟢 linux-x86_64-release-asan target: cloud/blockstore/,cloud/storage/ (test time: 1239s): all tests PASSED for commit 9f38bee.

TESTS PASSED ERRORS FAILED FAILED BUILD SKIPPED MUTED?
6433 6433 0 0 0 0 0

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Note

This is an automated comment that will be appended during run.

Note

All workloads for linux-x86_64-release-tsan have completed.

Tip

Planned checks for linux-x86_64-release-tsan.

🟢 linux-x86_64-release-tsan target: cloud/blockstore/,cloud/storage/ (test time: 1173s): all tests PASSED for commit 9f38bee.

TESTS PASSED ERRORS FAILED FAILED BUILD SKIPPED MUTED?
6394 6390 0 0 0 4 0

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

Labels

asan Launch builds with address sanitizer along with regular build blockstore Add this label to run only cloud/blockstore build and tests on PR storage Add this label to run tests only from cloud/storage/ directory tsan Launch builds with thread sanitizer along with regular build

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants