Re-apply lost fix 4f33f5b: collection_unpack value_len underflow regression#3560
Re-apply lost fix 4f33f5b: collection_unpack value_len underflow regression#3560g4mm4-VCF wants to merge 4 commits into
Conversation
cf1f9c4 to
17f3445
Compare
|
Hi @g4mm4-VCF, thanks for this patch. Please take a look at the SonarCLoud report and fix those issues. |
The fix from owasp-modsecurity#3082 was lost in merge 649aea7 (2024-04-04) and has been missing from every release v2.9.10 .. v2.9.13. This re-applies the same one-line guard as the original commit by @twouters. Refs: owasp-modsecurity#3082
Standalone harness that replicates the unpack arithmetic and asserts that the patched bound check rejects a value_len=0 blob while the buggy version would have produced a 4 GiB copy length. Runs without APR / Apache build dependencies so it integrates into `make check` as a self-contained TAP test.
Split combined declarations and remove redundant inner casts as flagged by SonarCloud's static analyser. No functional change — the regression test still produces buggy_copy = UINT_MAX on the buggy path and patched_rc = 0 on the patched path.
b47c646 to
d7759ab
Compare
|
Hi there,
I have fixed all the issues, and everything is now working properly.
~g4mm4
…On Sun, May 10, 2026 at 5:43 PM Ervin Hegedus ***@***.***> wrote:
*airween* left a comment (owasp-modsecurity/ModSecurity#3560)
<#3560 (comment)>
Hi @g4mm4-VCF <https://github.com/g4mm4-VCF>,
thanks for this patch.
Please take a look at the SonarCLoud report and fix those issues.
—
Reply to this email directly, view it on GitHub
<#3560 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/CC66HTG37WTJBERASUVT43L42BMNXAVCNFSM6AAAAACYX36OD6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DIMJVGA4DIOJRG4>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
There was a problem hiding this comment.
Pull request overview
Re-applies a previously lost defensive check in collection_unpack() to prevent value_len - 1 underflow (and resulting huge memcpy/apr_pstrmemdup), and adds a standalone regression test intended to demonstrate the buggy vs fixed arithmetic.
Changes:
- Reintroduce
name_len < 1/value_len < 1guards inapache2/persist_dbm.cbefore subtracting 1 from those lengths. - Add a new standalone C regression test for the
value_len == 0underflow scenario. - Add an Automake snippet intended to build/run the new regression test.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
apache2/persist_dbm.c |
Restores the missing < 1 guards to prevent *_len - 1 unsigned underflow leading to oversized copies. |
tests/regression/persist_dbm/test_persist_dbm_value_len.c |
Adds a standalone test program modeling the buggy vs patched arithmetic around value_len - 1. |
tests/regression/persist_dbm/Makefile.am |
Attempts to hook the new test into an Automake-based make check flow for that subdirectory. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Thanks - now please take a look at Copilot suggestions. I think all of them make sense. |
1. Add missing bound check before reading value_len (Copilot owasp-modsecurity#1 — real bug). After consuming the name field, blob_offset can advance to exactly blob_size; the subsequent 16-bit read of value_len from blob[blob_offset] / blob[blob_offset+1] would then OOB-read on a truncated blob. Fixed with the standard 2-byte check. 2. Drop tests/regression/persist_dbm/ (Copilot owasp-modsecurity#2-owasp-modsecurity#5). The directory was not wired into the Autotools build (no AC_CONFIG_FILES nor parent SUBDIRS entry), and the existing tests/regression/ is a Perl-based HTTP integration harness that doesn't fit a unit test of a static function. Wiring it into tests/Makefile.am where msc_test lives would require non-trivial restructuring; keeping the standalone harness outside the upstream tree (in the security advisory's PoC archive) is the cleaner path for now. Refs: PR owasp-modsecurity#3560 review comments by github-actions[bot] / Copilot.
|
Thanks for running Copilot. Pushed f239b02 to address all 5 suggestions. Summary: #1 ( #2–#5 ( The cleanest move was to drop the in-tree test directory entirely. The standalone test harness is preserved outside the upstream tree (in the disclosure PoC archive) for anyone who wants to verify the fix offline: // snippet from the standalone test
unsigned char blob[] = {
0x49, 0x52, 0x01, // SDBM header
0x00, 0x01, // name_len = 1
0x00, // name = ""
0x00, 0x00, // value_len = 0 ← BUG TRIGGER
};
// vulnerable build → apr_pstrmemdup(..., 0xFFFFFFFF) → SIGSEGV
// patched build → second bound check returns NULL earlyHappy to revisit the in-tree test if you'd prefer a Perl-level integration test that drops a malformed SDBM file into Diff vs
CI / SonarCloud should pass on the new commit. Ping me if anything else surfaces. |
1. Add missing bound check before reading value_len (Copilot owasp-modsecurity#1 — real bug). After consuming the name field, blob_offset can advance to exactly blob_size; the subsequent 16-bit read of value_len from blob[blob_offset] / blob[blob_offset+1] would then OOB-read on a truncated blob. Fixed with the standard 2-byte check. 2. Drop tests/regression/persist_dbm/ (Copilot owasp-modsecurity#2-owasp-modsecurity#5). The directory was not wired into the Autotools build (no AC_CONFIG_FILES nor parent SUBDIRS entry), and the existing tests/regression/ is a Perl-based HTTP integration harness that doesn't fit a unit test of a static function. Wiring it into tests/Makefile.am where msc_test lives would require non-trivial restructuring; keeping the standalone harness outside the upstream tree (in the security advisory's PoC archive) is the cleaner path for now. Refs: PR owasp-modsecurity#3560 review comments by github-actions[bot] / Copilot.
f239b02 to
fbdd35a
Compare
|
SonarCloud caught a follow-up: cognitive complexity tipped to 26/25 because of the new bound check. Force-pushed - if (blob_offset + var->name_len > blob_size) return NULL;
+ /* Need name_len bytes for the name body plus 2 more for the value_len header. */
+ if (var->name_len < 1 || blob_offset + var->name_len + 2 > blob_size) return NULL;The strengthened predicate now requires room for both the name body and the 2-byte Final diff vs
CI/SonarCloud should be green again on |
| blob_offset += 2; | ||
|
|
||
| if (blob_offset + var->value_len > blob_size) return NULL; | ||
| if (var->value_len < 1 || blob_offset + var->value_len > blob_size) return NULL; |
|
Good catch — the PR description was stale after I dropped
The branch itself is unchanged; only the description was out of sync. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
apache2/persist_dbm.c:73
- The restored
value_len == 0guard is not covered by an in-tree regression test. Because this PR is re-applying a security fix that was previously dropped, an automated test for the malformed collection blob path would materially reduce the chance of losing or breaking this bound check again; the existing regression suite already has SecDataDir coverage for persistent collections.
if (var->value_len < 1 || blob_offset + var->value_len > blob_size) return NULL;
| blob_offset += 2; | ||
| if (blob_offset + var->name_len > blob_size) return NULL; | ||
| /* Need name_len bytes for the name body plus 2 more for the value_len header. */ | ||
| if (var->name_len < 1 || blob_offset + var->name_len + 2 > blob_size) return NULL; |
| blob_offset += 2; | ||
| if (blob_offset + var->name_len > blob_size) return NULL; | ||
| /* Need name_len bytes for the name body plus 2 more for the value_len header. */ | ||
| if (var->name_len < 1 || blob_offset + var->name_len + 2 > blob_size) return NULL; |
1. Add missing bound check before reading value_len (Copilot owasp-modsecurity#1 — real bug). After consuming the name field, blob_offset can advance to exactly blob_size; the subsequent 16-bit read of value_len from blob[blob_offset] / blob[blob_offset+1] would then OOB-read on a truncated blob. Fixed with the standard 2-byte check. 2. Drop tests/regression/persist_dbm/ (Copilot owasp-modsecurity#2-owasp-modsecurity#5). The directory was not wired into the Autotools build (no AC_CONFIG_FILES nor parent SUBDIRS entry), and the existing tests/regression/ is a Perl-based HTTP integration harness that doesn't fit a unit test of a static function. Wiring it into tests/Makefile.am where msc_test lives would require non-trivial restructuring; keeping the standalone harness outside the upstream tree (in the security advisory's PoC archive) is the cleaner path for now. Refs: PR owasp-modsecurity#3560 review comments by github-actions[bot] / Copilot.
fbdd35a to
2d2c0b0
Compare
|
Two follow-ups from the latest Copilot pass, both on Copilot — " Verified by walking the control flow:
So blob_offset += 2;
/* Need name_len bytes for the name body plus 2 more for the value_len header.
* name_len == 0 is already handled by the early break above, so no zero-check
* is required at this point. */
if (blob_offset + var->name_len + 2 > blob_size) return NULL;The The original Copilot — "the new bound check is not covered by an in-tree regression test" — push-back, requesting maintainer guidance. I want to flag the framework limitation explicitly so we can decide together rather than play whack-a-mole with Copilot. The fix lives inside
Three options I see, in increasing scope:
Default position is (A), which is what the current branch reflects. If you'd prefer (B), I can put together the Perl fixture in a follow-up commit on this branch — happy to do the work, just want to confirm scope before adding test machinery that the harness doesn't currently have. (C) feels disproportionate for a 2-line bound-check fix. Let me know which way you'd like to go. |
|



Summary
Re-applies commit 4f33f5b ("Fix possible segfault in collection_unpack",
@twouters, 2024-03-01) which was silently dropped during merge 649aea7
on 2024-04-04, and adds one further bound check that the review
process surfaced. The drop has been confirmed by:
git show 4f33f5b:apache2/persist_dbm.c | sed -n '60,75p'— has the guardsgit show 649aea7:apache2/persist_dbm.c | sed -n '60,75p'— guards gonegit show v2.9.13:apache2/persist_dbm.c | sed -n '60,75p'— still goneAffected releases: v2.9.10, v2.9.11, v2.9.12, v2.9.13.
What this PR does
collection_unpack()inapache2/persist_dbm.chad two missingbound checks that this PR restores / adds:
Re-introduce the
var->name_len < 1 ||andvar->value_len < 1 ||guards from 4f33f5b. Without them, ablob containing
0x00 0x00in thevalue_lenfield underflowsvalue_len - 1to0xFFFFFFFFandapr_pstrmemdup()is invokedwith size = 4 GiB.
Strengthen the name-bound check from
blob_offset + name_len > blob_sizetoblob_offset + name_len + 2 > blob_sizeto also cover the2-byte
value_lenheader read site (caught during Copilotreview on this PR). The previous shape allowed an OOB read on a
truncated blob whose tail landed exactly at the end of the name
body.
Both changes apply to the same function and are exercised by the same
crash primitive (a malformed SDBM entry under SecDataDir), so they
are landed together.
The final diff against
v2/masteris four lines inapache2/persist_dbm.cplus one explanatory comment — no additionalfiles, no new dependencies, no behaviour change for well-formed
blobs.
Test plan
collection_unpackarithmetic byte-for-byte) confirms
copy_len = UINT_MAXonthe buggy code path and
rc = 0(NULL return) on the patchedcode path for a
value_len = 0blobname_len = 1+
name = ""+value_len = 0) into aSecDataDir-backedcollection; observe the previously-crashing worker now ignore
the malformed entry
whose tail lands exactly at
blob_offset + name_len;observe the patched code returns
NULLinstead of readingvalue_lenpast the bufferThe standalone unit-test harness is not included in-tree — the
existing
tests/regression/is a Perl HTTP integration harness thatdoesn't fit a unit test of a
staticfunction, and wiring a newcheck_PROGRAMSintotests/Makefile.am(wheremsc_testlives)would require restructuring out of scope for this fix. The harness
is preserved in the disclosure PoC archive for anyone who wants to
verify offline.
References
Coordinated with @airween via private security channel.