Skip to content

CBG-3658: Prevent non-finite CPU stat from corrupting stats serialization#8430

Merged
bbrks merged 2 commits into
mainfrom
CBG-3658
Jul 7, 2026
Merged

CBG-3658: Prevent non-finite CPU stat from corrupting stats serialization#8430
bbrks merged 2 commits into
mainfrom
CBG-3658

Conversation

@bbrks

@bbrks bbrks commented Jul 6, 2026

Copy link
Copy Markdown
Member

CBG-3658

A divide-by-zero in the process CPU percentage calculation could produce +Inf/NaN when two CPU snapshots reported identical total system jiffies (very frequent stats sampling, or coarse platform CPU accounting). SgwFloatStat then emitted raw "+Inf"/"NaN" bytes, which the JSON encoder rejected, collapsing the entire SgwStats blob to "null" in sg_stats.log.

  • Guard the CPU percentage division: return 0 when no total system time has elapsed. The arithmetic is extracted into cpuStatsSnapshot.cpuPercentageSince so the degenerate case is unit-testable without reading live system state.
  • Harden SgwFloatStat rendering (shared formatValue used by MarshalJSON and String) to emit "0" for non-finite values, so no single stat can corrupt the whole serialized blob or the expvar output.

Integration Tests

…tion

A divide-by-zero in the process CPU percentage calculation could produce
+Inf/NaN when two CPU snapshots reported identical total system jiffies
(very frequent stats sampling, or coarse platform CPU accounting).
SgwFloatStat then emitted raw "+Inf"/"NaN" bytes, which the JSON encoder
rejected, collapsing the entire SgwStats blob to "null" in sg_stats.log.

- Guard the CPU percentage division: return 0 when no total system time has
  elapsed. The arithmetic is extracted into cpuStatsSnapshot.cpuPercentageSince
  so the degenerate case is unit-testable without reading live system state.
- Harden SgwFloatStat rendering (shared formatValue used by MarshalJSON and
  String) to emit "0" for non-finite values, so no single stat can corrupt
  the whole serialized blob or the expvar output.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 6, 2026 18:27

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

Prevents non-finite process CPU utilization stats (+Inf/NaN) from breaking Sync Gateway stats JSON/expvar serialization by guarding the CPU percentage calculation and hardening float stat rendering.

Changes:

  • Extracted CPU utilization arithmetic into cpuStatsSnapshot.cpuPercentageSince and guarded divide-by-zero/degenerate total-time deltas.
  • Hardened base.SgwFloatStat JSON/expvar rendering to emit 0 for non-finite float values.
  • Added regression tests covering both the CPU percentage degenerate case and non-finite stat marshaling.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
rest/stats_context.go Refactors CPU % calculation into a testable helper and guards zero/negative total-time deltas.
rest/stats_context_test.go Adds unit tests for the CPU % divide-by-zero regression and normal-case math.
base/stats.go Adds SgwFloatStat.formatValue() to sanitize NaN/Inf during JSON/expvar rendering.
base/stats_test.go Adds regression test ensuring non-finite float stats don’t break stats JSON/expvar output.

Comment thread base/stats_test.go Outdated
Comment on lines +190 to +197
// The output must be valid, parseable JSON.
var roundTripped map[string]float64
require.NoError(t, JSONUnmarshal(marshalled, &roundTripped))

// String() satisfies expvar.Var, which likewise requires a valid JSON value.
var viaString float64
require.NoError(t, JSONUnmarshal([]byte(stat.String()), &viaString))
})

@torcolvin torcolvin left a comment

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.

Nice catch, I think this is mostly fine.

Comment thread rest/stats_context.go
Comment thread rest/stats_context.go
Comment on lines +119 to +120
// Store the current values as the previous values for the next time this function is called
statsContext.cpuStatsSnapshot = currentSnapshot

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.

Should we drop cpuprecentagesince is 0? I think it is probably fine to keep it since it would probably be the same

torcolvin
torcolvin previously approved these changes Jul 6, 2026
- Assert non-finite float stats fall back to 0, not just valid JSON
- Trim cpuPercentageSince doc comment to describe the zero-delta case

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bbrks bbrks requested a review from torcolvin July 7, 2026 12:53
@bbrks bbrks assigned torcolvin and unassigned bbrks Jul 7, 2026
@bbrks bbrks merged commit b839cd2 into main Jul 7, 2026
48 checks passed
@bbrks bbrks deleted the CBG-3658 branch July 7, 2026 13:41
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