Netdata fixes part 33 - #22973
Merged
Merged
Conversation
cbuffer_realloc_unsafe() doubled the current buffer size to compute the next allocation. When a buffer was created with initial size 0 and a positive maximum size, zero doubled to zero, the helper returned success without increasing buf->size, and the add/reserve loops could repeat forever. Seed the growth calculation with one byte when the current size is zero, then keep the existing max-size cap. All non-zero buffers keep the same doubling behavior, and max_size 0 still fails before the growth calculation. (cherry picked from commit 82704a2)
Circular-buffer growth and write paths performed size_t addition before capacity checks. Doubling buf->size could wrap before max_size clamping, and d_len + len, size + len, and buf->write + size could wrap before deciding whether a write or reserve fits. Saturate growth before multiplying, reject impossible requests before the growth loop, and rewrite capacity and write-position updates in subtraction form. Valid buffer behavior, return values, and growth semantics are unchanged; only wrapped arithmetic paths now fail or saturate safely. (cherry picked from commit 34158ed)
Issue: cbuffer_commit_reserved_unsafe() advanced the write cursor with a single wrap adjustment. Current WebSocket callers reserve and commit matching bounded sizes, so the issue is potential in the reviewed call graph, but a contract-violating helper caller could commit more than one ring length and leave buf->write outside the backing allocation. Current vs potential: valid production callers are not known to exercise an over-sized commit today. The helper itself currently accepts arbitrary size_t input, so misuse inside future callers or error paths could preserve an out-of-range cursor after one invalid call. Solution: return before any arithmetic when the buffer pointer, data pointer, commit length, or ring size is unusable, reduce the committed size modulo that ring size, and then apply the existing wrap logic. The null and zero-size checks are written as explicit control-flow guards before loading buf->size and before the modulo. Functional impact: no change for valid callers; invalid over-commit sizes now keep the cursor in range instead of corrupting the ring invariant. Null buffers, missing data, zero-size buffers, and zero-size commits remain no-ops. Performance impact: one size_t modulo per reserved commit, negligible beside current WebSocket socket I/O and frame processing paths. Splitting the exceptional guards into explicit branches has no meaningful cost on the valid path. Security impact: prevents a reusable circular-buffer helper from preserving an out-of-range write cursor after invalid commit sizes. Validation: git diff --check passed for src/libnetdata/circular_buffer/circular_buffer.c. A debugfs-enabled CMake configure passed after initializing submodules. cmake --build for debugfs.plugin passed and recompiled src/libnetdata/circular_buffer/circular_buffer.c into libnetdata. Reviewer gate: required local semantic review gate passed for the original finding, and follow-up static-analysis feedback was verified against the source before this fixup. (cherry picked from commit 8d6abfe)
cbuffer_commit_reserved_unsafe() documents that callers should commit no more than the size returned by cbuffer_reserve_unsafe(), but the helper did not enforce the circular-buffer capacity invariant. The previous modulo hardening kept the write index inside the allocation, but a contract-violating overlarge commit could still be reduced to a smaller commit and make the buffer state represent bytes that were never reserved. Current direct WebSocket callers reserve and commit bounded sizes, so no reviewed production caller triggers this today. The reusable helper still accepted arbitrary size_t input, so future or error-path misuse could corrupt the logical ring state. Compute the current used size and return without advancing the cursor when the buffer is already in an impossible full/corrupt state or when the requested commit would fill or overrun the representable ring capacity. Remove the modulo because accepted commits are already smaller than the ring size. Functional impact: valid reserve/commit callers are unchanged. Invalid overlarge commits now leave the buffer unchanged instead of silently committing a modulo-reduced byte count. Performance impact: one used-size calculation and one unlikely branch per reserved commit, negligible beside current WebSocket socket I/O and frame processing. Security impact: hardens a reusable circular-buffer helper against state corruption from internal misuse and preserves the one-free-byte invariant used to distinguish empty from full. Validation: git diff --check; built the circular-buffer object, WebSocket receive/send caller objects, libnetdata, and debugfs.plugin. Review: required independent source review approved the exact diff; one non-blocking adjacent maintainability observation was recorded separately. (cherry picked from commit ec46047)
|
Contributor
There was a problem hiding this comment.
No issues found across 1 file
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Architecture diagram
sequenceDiagram
participant Client as Caller Component
participant CB as Circular Buffer
participant Alloc as Memory Allocator
Client->>CB: cbuffer_add_unsafe(buf, d, d_len)
CB->>CB: Compute used size len
alt impossible: len ≥ max_size or d_len > max_size - len
CB-->>Client: return 1 (error, no change)
else possible
loop while space insufficient (len ≥ size or d_len ≥ size - len)
CB->>CB: cbuffer_realloc_unsafe(buf)
alt growth succeeds
CB->>Alloc: realloc (new_size = max_size or capped doubling, seed 1 if size 0)
Alloc-->>CB: new buffer
CB->>CB: move data, update size
else growth fails
CB-->>Client: return 1
end
end
alt d_len < size - write
CB->>CB: memcpy contiguous write
CB->>CB: write += d_len
else
CB->>CB: memcpy wrap‑aware (two parts)
CB->>CB: write updated with wrap
end
CB-->>Client: return 0
end
Note over Client,CB: Similar guards apply to cbuffer_reserve_unsafe and cbuffer_commit_reserved_unsafe (validation, safe wrap arithmetic)
Contributor
There was a problem hiding this comment.
Pull request overview
Hardens libnetdata’s adaptive circular buffer to avoid infinite growth loops, integer overflow during resizing, and out-of-range write/commit arithmetic, while preserving behavior for valid callers.
Changes:
- Make buffer growth overflow-safe and able to recover from a zero-sized buffer by seeding growth at 1 byte and capping before doubling.
- Replace addition-based capacity checks (
len + size) with subtraction-based bounds checks to avoid wraparound and impossible requests. - Make reserve/add/commit pointer arithmetic wrap-safe and add defensive validation in
cbuffer_commit_reserved_unsafe().
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
thiagoftsm
approved these changes
Jul 7, 2026
thiagoftsm
left a comment
Contributor
There was a problem hiding this comment.
No issues found during runtime. LGTM!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Summary by cubic
Hardens the circular buffer to prevent infinite growth loops, integer overflow, and out-of-range write positions, with no change for valid callers.
Written for commit 0a4f672. Summary will update on new commits.