Skip to content

Harden against invalid cache peer digests - #2485

Open
kinkie wants to merge 1 commit into
squid-cache:masterfrom
kinkie:fix-cachedigest-calcmasksize
Open

Harden against invalid cache peer digests#2485
kinkie wants to merge 1 commit into
squid-cache:masterfrom
kinkie:fix-cachedigest-calcmasksize

Conversation

@kinkie

@kinkie kinkie commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

An assert() call was improperly used to validate
a received cache digest from a cache_peer.
Switch to returning an invalid value, so that
the improper digest is rejected instead.

An assert() call was improperly used to validate
a received cache digest from a cache_peer.
Switch to using an invalid value, so that
the improper digest is rejected instead.
@squid-anubis squid-anubis added the M-failed-description https://github.com/measurement-factory/anubis#pull-request-labels label Aug 27, 2026
@squid-anubis

This comment was marked as resolved.

@kinkie

kinkie commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

The relevant call is in peerDigestSetCBlock():783, which calls CalcMaskSize() with a peer-supplied cblock.capacity, triggering the assert.

The new code will cause mismatch between cblock.mask_size and the calculated capacity (we may also add an explicit non-zero check as an additional precaution), causing the rejection of the digest.

No other call to CalcMaskSize( ) uses user input

@kinkie kinkie added the backport-to-v7 maintainer has approved these changes for v7 backporting label Aug 27, 2026
@squid-anubis squid-anubis removed the M-failed-description https://github.com/measurement-factory/anubis#pull-request-labels label Aug 27, 2026

@rousskov rousskov 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.

The proposed solution has several conceptual and implementation problems. I will find the time to disclose and fix them. The ball is in my court.

@rousskov rousskov added the S-waiting-for-reviewer ready for review: Set this when requesting a (re)review using GitHub PR Reviewers box label Aug 27, 2026

@rousskov rousskov 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.

I will fix the problems identified in this review. The ball is in my court.

Comment thread src/CacheDigest.cc
uint64_t bitCount = (cap * bpe) + 7;
assert(bitCount < INT_MAX); // do not 31-bit overflow later
const uint64_t bitCount = (cap * bpe) + 7;
if (bitCount >= std::numeric_limits<int>::max())

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.

The proposed code is buggy. For example, CalcMaskSize(2^57 + 1, 128) overflows cap * bpe, resulting in incorrect 135 return value:

cap * bpe = (2^57 + 1) * 128 = (2^57 + 1) * 2^7 = 2^64 + 2^7 = 2^64 + 128;

The above multiplication wraps modulo 2^64, producing just 128, and leading to an incorrect "positive" 135 result rather than overflow detection:

(cap * bpe) + 7 = 128 + 7 = 135;

N.B. Unlike this PR code, the corresponding official code did not even try to overcome 64-bit overflows, but it is also buggy, for the same reason.

Comment thread src/CacheDigest.cc
assert(bitCount < INT_MAX); // do not 31-bit overflow later
const uint64_t bitCount = (cap * bpe) + 7;
if (bitCount >= std::numeric_limits<int>::max())
return 0; // overflow; caller must treat 0 as invalid

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.

Callers may forget to obey this "must". Depending on one's definition of "treat as invalid", it can be argued that at least one and possibly even two existing callers do not really "treat 0 as invalid" already.

In general, please avoid using special valid (from the compiler point of view) values like zeros or empty strings to flag invalid input.

Comment thread src/CacheDigest.h

/// calculate the size of mask required to digest up to
/// a specified capacity and bitsize.
/// \returns 0 when inputs would overflow (invalid).

@rousskov rousskov Sep 1, 2026

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.

It also returns 0 in other cases. For example, CalcMaskSize(0, 8) AFAICT. This is one of the dangers with using valid (from the compiler point of view) values like zeros or empty strings to flag invalid input.

P.S. Current code may not have any CalcMaskSize(0, 8) callers, but that assertion does not address this concern because code will change (and because changing such code increases associated caller risks).

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

Labels

backport-to-v7 maintainer has approved these changes for v7 backporting S-waiting-for-reviewer ready for review: Set this when requesting a (re)review using GitHub PR Reviewers box

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants