Skip to content

guard underflowed value size in couchbase collection responses#3402

Open
ubeddulla wants to merge 1 commit into
apache:masterfrom
ubeddulla:couchbase-collection-value-size
Open

guard underflowed value size in couchbase collection responses#3402
ubeddulla wants to merge 1 commit into
apache:masterfrom
ubeddulla:couchbase-collection-value-size

Conversation

@ubeddulla

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: resolve

Problem Summary:

popCollectionId() and popManifest() in src/brpc/couchbase.cpp size the value section with unsigned arithmetic: header.total_body_length - header.extras_length - header.key_length. All three fields come straight from the server's response header and nothing cross-checks them, so a reply where extras_length + key_length exceeds total_body_length wraps the subtraction close to SIZE_MAX. IOBuf::cutn() clamps to what is available, so the whole remaining pipelined buffer gets swallowed: popManifest() hands those bytes back to the caller as the collection manifest, popCollectionId() folds them into the error string, and either way the response stream on that connection is out of sync from then on. The sibling parsers in the same file (popGet, popStore, popCounter, popVersion) already compute this as a signed int and bail out when it is negative; only these two collection parsers were written with size_t and no check.

Reproduced against a 24-byte header with total_body_length=0, extras_length=1 followed by 64 bytes of the next response: popManifest() returns true with a computed size of 18446744073709551615, copies 63 bytes of the following response into the manifest string and leaves the buffer empty. With this change it returns false and the buffer is untouched.

What is changed and the side effects?

Changed:

Both functions now use the same signed computation and < 0 rejection as their siblings, done before pop_front() so a malformed reply does not consume anything. Added a regression test to test/brpc_couchbase_unittest.cpp.

Side effects:

  • Performance effects: none.

  • Breaking backward compatibility: no, well-formed responses parse exactly as before.


Check List:

@chenBright
chenBright requested a review from Copilot July 22, 2026 09:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Guards against unsigned underflow when computing the value section size in Couchbase collection-related response parsers, preventing a malformed header from draining the pipelined buffer and desynchronizing the stream.

Changes:

  • Compute value_size using signed arithmetic and reject negative sizes before consuming any bytes in popCollectionId() and popManifest().
  • Add a regression test covering inconsistent total_body_length vs extras_length + key_length for both parsers.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
src/brpc/couchbase.cpp Adds negative-size guards to prevent underflow-driven buffer draining in collection response parsing.
test/brpc_couchbase_unittest.cpp Adds regression coverage ensuring malformed headers don’t consume buffered bytes or mutate outputs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/brpc/couchbase.cpp
Comment on lines +1510 to +1515
const int value_size = (int)header.total_body_length -
(int)header.extras_length - (int)header.key_length;
if (value_size < 0) {
butil::string_printf(&_err, "value_size=%d is negative", value_size);
return false;
}
Comment thread src/brpc/couchbase.cpp
const int value_size = (int)header.total_body_length -
(int)header.extras_length - (int)header.key_length;
if (value_size < 0) {
butil::string_printf(&_err, "value_size=%d is negative", value_size);
Comment on lines +107 to +111
brpc::policy::CouchbaseResponseHeader header = {};
header.magic = brpc::policy::CB_MAGIC_RESPONSE;
header.command = brpc::policy::CB_GET_COLLECTIONS_MANIFEST;
header.extras_length = 1;
header.total_body_length = 0;
Comment on lines +126 to +128
uint8_t collection_id = 0;
EXPECT_FALSE(res2.popCollectionId(&collection_id));
EXPECT_EQ(sizeof(header) + next_response.size(), res2.rawBuffer().size());

@chenBright chenBright left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

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