fix: harden query path — hdr_mean overflow + count accessor OOB reads - #147
filipecosta90 wants to merge 3 commits into
Conversation
Two more issues found by adversarial review during the ClusterFuzzLite effort: - hdr_mean summed iter.count * hdr_median_equivalent_value in an int64 running total. For a histogram holding values near 2^62 this overflows INT64_MAX (signed-overflow UB) and yields a garbage/negative mean. Reachable via the fuzz surface (log_reader_fuzzer calls hdr_mean on every decoded histogram). Accumulate in double, as hdr_stddev already does. - hdr_count_at_value indexed counts[] via counts_index_for() with no range check, so a value beyond highest_trackable_value read out of bounds. Mirror hdr_record_values' guard and return 0 for out-of-range values. Verified under ASan+UBSan: main aborts (UBSan signed overflow in hdr_mean; ASan OOB read in hdr_count_at_value); both return clean results after the fix. Added regression tests. Found-by: adversarial review during the ClusterFuzzLite fuzzing effort Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adversarial review of the hdr_count_at_value fix flagged its index-based twin hdr_count_at_index as having the identical unchecked out-of-bounds read (raw caller index -> counts_get_normalised -> h->counts[index]). Guard it the same way (return 0 for an out-of-range index; the unsigned compare also catches negatives). All in-tree callers pass in-range indices. Also strengthen the regression tests per review: pin hdr_mean to a plausible magnitude band (not just sign/finiteness), assert in-range and the value==highest boundary for hdr_count_at_value (catchable without a sanitizer), and add an out-of-range test for hdr_count_at_index. (Note: hdr_value_at_index's shift UB at an extreme index is left as a documented precondition -- it performs no counts[] access and guarding it would change the internal peek at index==counts_len; tracked separately.) Found-by: adversarial review during the ClusterFuzzLite fuzzing effort Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6e627e5 to
b6343fe
Compare
|
🤖 Automated first-pass review — a human maintainer's review is still required before merge. Both fixes read as correct to me, and the diff is well-scoped: no change to
While in that function:
One calibration on the CI claim in the description: the per-PR ClusterFuzzLite job is ASan-only and 200s, and it only reaches these accessors if a target actually calls them; the I can't verify the Java behaviour or run the sanitizers from here, so the author's stated local ASan+UBSan verification is what the mean change ultimately rests on. The statistical side may be worth a second look from whoever is comfortable comparing against the upstream Java implementation. |
Two more fuzzing-surfaced bugs in the query path (found by adversarial review during the ClusterFuzzLite effort).
1.
hdr_mean— signed int64 overflow (UBSan), fuzz-reachabletotal += iter.count * hdr_median_equivalent_value(...)accumulated inint64. For a histogram holding values near2^62, a few samples overflowINT64_MAX→ signed-overflow UB and a wrapped/negative mean.log_reader_fuzzercallshdr_meanon every decoded histogram, so a crafted large-range log reaches it. Fix: accumulate indouble(exactly whathdr_stddevalready does).2.
hdr_count_at_value— out-of-bounds read (ASan)It indexed
counts[]viacounts_index_for()with no range check, so querying a value beyondhighest_trackable_valueread past the array. Fix: mirrorhdr_record_values' guard (valuerange +(uint32_t)index >= (uint32_t)counts_len) and return0— an untracked value has count 0.Verification (local, ASan+UBSan)
hdr_mean→runtime error: signed integer overflow ... at hdr_histogram.c:832;hdr_count_at_value(h, INT64_MAX)→ ASan heap-buffer-overflow READ.hdr_meanreturns a positive finite value;hdr_count_at_valuereturns 0. Added regression tests; full suite passes under ASan+UBSan.🤖 Generated with Claude Code