Skip to content

signal: fix data race in logger during shutdown#10866

Open
Euler-B wants to merge 1 commit into
lightningnetwork:masterfrom
Euler-B:fix/6137-signal-data-race
Open

signal: fix data race in logger during shutdown#10866
Euler-B wants to merge 1 commit into
lightningnetwork:masterfrom
Euler-B:fix/6137-signal-data-race

Conversation

@Euler-B
Copy link
Copy Markdown
Contributor

@Euler-B Euler-B commented May 31, 2026

Fix data race in the signal package logger during shutdown (issue #6137).

The mainInterruptHandler goroutine reads the package-level log variable while UseLogger() may be called from the main goroutine during startup to replace the logger with a properly configured one. This concurrent access without synchronization causes a data race when lnd is built with the -race flag.

The Problem

When lnd starts:

  1. signal.Intercept() spawns mainInterruptHandler goroutine which reads log to handle shutdown signals
  2. Main goroutine calls SetupLoggers()signal.UseLogger() which writes to log
  3. Both goroutines access the same log variable without synchronization

This race was reported in issue #6137 and can be reproduced by:

  • Building lnd with -race flag
  • Running lnd in simnet with btcd backend
  • Sending SIGINT during early startup

The Solution

Add a sync.RWMutex to protect concurrent access to the log variable:

  • UseLogger() acquires write lock when replacing the logger
  • New getLogger() helper acquires read lock when accessing the logger
  • All direct log accesses in signal.go replaced with getLogger() calls

This ensures thread-safe access while maintaining good performance (read-heavy workload benefits from RWMutex).

Fixes #6137

Steps to Test

  1. Build lnd with race detector:

    go build -race -o lnd-race ./cmd/lnd
  2. Start btcd in simnet:

    btcd --simnet --rpcuser=devuser --rpcpass=devpass \
      --rpclisten=127.0.0.1:18556 --listen=127.0.0.1:18555 \
      --rpccert=/tmp/btcd/rpc.cert --rpckey=/tmp/btcd/rpc.key \
      --txindex
  3. Start lnd with race detector:

    ./lnd-race --bitcoin.simnet --bitcoin.node=btcd \
      --btcd.rpcuser=devuser --btcd.rpcpass=devpass \
      --btcd.rpchost=127.0.0.1:18556 \
      --btcd.rpccert=/tmp/btcd/rpc.cert \
      --noseedbackup
  4. Send SIGINT during startup:

    kill -SIGINT <lnd-pid>
  5. Verify no data race warnings appear in logs related to signal/signal.go or signal/log.go

Before the fix: Data race detected between mainInterruptHandler (read) and UseLogger (write)

After the fix: No data race warnings

Pull Request Checklist

Testing

  • Your PR passes all CI checks.
  • Tests covering the positive and negative (error paths) are included.
  • Bug fixes contain tests triggering the bug to prevent regressions.

Code Style and Documentation

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a data race condition in the signal package that occurs during lnd startup. By implementing a read-write mutex, the changes ensure that the logger variable is accessed safely when it is being replaced by the main goroutine while simultaneously being read by the mainInterruptHandler goroutine, effectively resolving issue #6137.

Highlights

  • Thread-safe Logging: Introduced a sync.RWMutex to the signal package to protect the package-level logger variable from concurrent access.
  • New Accessor: Added a getLogger() helper function that utilizes an RLock to ensure thread-safe retrieval of the logger instance.
  • Refactored Access: Updated all direct references to the log variable in signal.go to use the new getLogger() helper.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request addresses a data race in the signal package logger by introducing a read-write mutex (logMtx) to protect the global log variable and replacing direct accesses with a thread-safe getLogger() helper. The reviewer suggested adding a nil check in getLogger() to return btclog.Disabled as a safe default and prevent potential nil-pointer dereferences.

Comment thread signal/log.go
@github-actions github-actions Bot added the severity-medium Focused review required label May 31, 2026
@github-actions
Copy link
Copy Markdown

PR Severity: MEDIUM

Automated classification | 3 files | 51 lines changed

Medium files (2):

  • signal/log.go - signal package logging (uncategorized Go package)
  • signal/signal.go - signal handling changes (uncategorized Go package)

Low files (1):

  • docs/release-notes/release-notes-0.22.0.md - release notes

Analysis: The PR modifies the signal package (OS signal processing). Not in critical/high categories, so MEDIUM. No bump conditions triggered (3 files, 51 lines). Release notes are LOW but do not raise overall severity.

To override, add a severity-override-{critical,high,medium,low} label.
<!-- pr-severity-bot -->

Add sync.RWMutex to protect concurrent access to the package-level
logger variable. The mainInterruptHandler goroutine reads the logger
while UseLogger() writes it during startup, causing a data race.

Changes:
- Add logMtx (sync.RWMutex) to protect log variable
- Wrap UseLogger() writes with Lock/Unlock
- Add getLogger() helper that uses RLock/RUnlock for thread-safe reads
- Replace all direct log access in signal.go with getLogger() calls

Fixes lightningnetwork#6137
@Euler-B Euler-B force-pushed the fix/6137-signal-data-race branch from c4f197e to 29b9c8b Compare May 31, 2026 19:08
@lightninglabs-deploy
Copy link
Copy Markdown
Collaborator

@Euler-B, remember to re-request review from reviewers when ready

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

severity-medium Focused review required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

signal: data-race during shutdown

2 participants