Skip to content

Fix ARAL double-free race - #22294

Merged
stelfrag merged 2 commits into
netdata:masterfrom
stelfrag:fix_aral
Apr 27, 2026
Merged

Fix ARAL double-free race#22294
stelfrag merged 2 commits into
netdata:masterfrom
stelfrag:fix_aral

Conversation

@stelfrag

@stelfrag stelfrag commented Apr 27, 2026

Copy link
Copy Markdown
Collaborator
Summary

aral_freez_internal() used a non-atomic load+store on the trailer. Two concurrent double-frees of the same pointer could both see the same page and enqueue the slot onto the free list twice, leading to later trailer corruption and crashes (e.g. in aral_unmark_allocation following a stale page pointer).

Replace with __atomic_exchange_n(.., 0, ACQ_REL); the loser sees NULL and fatal()s with a clear diagnostic before touching the free list.

aral_unmark_allocation() has the same load-then-modify shape but is not exercised concurrently by any current caller; left for follow-up.


Summary by cubic

Fixes a double-free race in ARAL by atomically claiming the slot trailer during free. Only one thread can re-enqueue; stale/double frees now fatal consistently (debug and release) with a clearer message that also covers corrupted pointers.

  • Bug Fixes
    • Switched aral_freez_internal to use __atomic_exchange_n(..., 0, __ATOMIC_ACQ_REL) to claim and clear the trailer atomically.
    • Added aral_decode_page_pointer_after_element... and aral_claim_page_pointer_after_element...; claim short-circuits on NULL so the free-path fatal runs in all builds, with a broadened message.
    • Prevents double-enqueue, trailer corruption, and crashes; aral_unmark_allocation remains for follow-up.

Written for commit d580d34. Summary will update on new commits. Review in cubic

aral_freez_internal() used a non-atomic load+store on the trailer.
Two concurrent double-frees of the same pointer could both see the
same page and enqueue the slot onto the free list twice, leading to
later trailer corruption and crashes (e.g. in aral_unmark_allocation
following a stale page pointer).

Replace with __atomic_exchange_n(.., 0, ACQ_REL); the loser sees NULL
and fatal()s with a clear diagnostic before touching the free list.

aral_unmark_allocation() has the same load-then-modify shape but is
not exercised concurrently by any current caller; left for follow-up.

@cubic-dev-ai cubic-dev-ai Bot 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.

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 T1 as Thread 1 (First Free)
    participant T2 as Thread 2 (Concurrent Free)
    participant Slot as Memory Slot Trailer
    participant Stats as ARAL Global Stats
    participant FL as Free List

    Note over T1, T2: Concurrent aral_freez_internal(ptr) calls

    T1->>Slot: NEW: aral_claim_page_pointer_after_element()
    Note right of Slot: Atomic Exchange (swap with 0)
    Slot-->>T1: Returns valid ARAL_PAGE pointer
    
    T2->>Slot: NEW: aral_claim_page_pointer_after_element()
    Note right of Slot: Atomic Exchange (swap with 0)
    Slot-->>T2: Returns NULL (Already claimed)

    rect rgb(200, 255, 200)
        Note over T1, FL: Winner Path
        T1->>Stats: Increment deallocators count
        T1->>FL: Enqueue slot to free list
    end

    rect rgb(255, 200, 200)
        Note over T2: Loser Path (Double Free)
        alt page is NULL
            T2->>T2: fatal() "ARAL: double free or stale free"
        end
    end
Loading

@stelfrag
stelfrag marked this pull request as ready for review April 27, 2026 13:53
@stelfrag
stelfrag requested a review from thiagoftsm as a code owner April 27, 2026 13:53
Copilot AI review requested due to automatic review settings April 27, 2026 13:53
@stelfrag
stelfrag requested a review from vkalintiris as a code owner April 27, 2026 13:53
@stelfrag
stelfrag marked this pull request as draft April 27, 2026 13:53

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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@stelfrag
stelfrag requested a review from Copilot April 27, 2026 14:21

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

Copilot reviewed 1 out of 1 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/libnetdata/aral/aral.c
Comment thread src/libnetdata/aral/aral.c Outdated
Have aral_claim_page_pointer_after_element_*() return NULL directly
when the atomic exchange yields 0, so the freez path's explicit fatal()
runs in both debug and release builds (was previously preempted by
aral_decode_*'s internal_fatal in NETDATA_INTERNAL_CHECKS builds). The
load path keeps its debug-only NULL assertion unchanged.

Reword the fatal to also cover memory corruption / invalid pointers
that happen to have 0 in the trailer.
@sonarqubecloud

Copy link
Copy Markdown

@stelfrag
stelfrag requested a review from Copilot April 27, 2026 14:38

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

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


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/libnetdata/aral/aral.c
@stelfrag
stelfrag marked this pull request as ready for review April 27, 2026 14:46

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

PR is working as expected and no issue was found during runtime. LGTM!

@stelfrag
stelfrag merged commit 82fcb4f into netdata:master Apr 27, 2026
162 checks passed
@stelfrag
stelfrag deleted the fix_aral branch April 27, 2026 18:41
@stelfrag stelfrag mentioned this pull request Jun 22, 2026
Ferroin pushed a commit that referenced this pull request Jul 15, 2026
* ARAL: close double-free race by atomically claiming the slot trailer

aral_freez_internal() used a non-atomic load+store on the trailer.
Two concurrent double-frees of the same pointer could both see the
same page and enqueue the slot onto the free list twice, leading to
later trailer corruption and crashes (e.g. in aral_unmark_allocation
following a stale page pointer).

Replace with __atomic_exchange_n(.., 0, ACQ_REL); the loser sees NULL
and fatal()s with a clear diagnostic before touching the free list.

aral_unmark_allocation() has the same load-then-modify shape but is
not exercised concurrently by any current caller; left for follow-up.

* ARAL: short-circuit claim on NULL trailer; broaden free fatal message

Have aral_claim_page_pointer_after_element_*() return NULL directly
when the atomic exchange yields 0, so the freez path's explicit fatal()
runs in both debug and release builds (was previously preempted by
aral_decode_*'s internal_fatal in NETDATA_INTERNAL_CHECKS builds). The
load path keeps its debug-only NULL assertion unchanged.

Reword the fatal to also cover memory corruption / invalid pointers
that happen to have 0 in the trailer.

(cherry picked from commit 82fcb4f)
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