Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 3, 2026

main PR

Description

SerialStream.EventLoopRunner checks event handlers once but invokes them multiple times per callback. If a handler unsubscribes during the first invocation, subsequent invocations throw NullReferenceException.

Before:

if (stream.DataReceived != null)
{
    if ((nativeEvents & (int)SerialData.Chars) != 0)
        stream.DataReceived(stream, ...);  // Handler unsubscribes here
    if ((nativeEvents & (int)SerialData.Eof) != 0)
        stream.DataReceived(stream, ...);  // NRE: handler is now null
}

After:

if ((nativeEvents & (int)SerialData.Chars) != 0)
    stream.DataReceived?.Invoke(stream, ...);
if ((nativeEvents & (int)SerialData.Eof) != 0)
    stream.DataReceived?.Invoke(stream, ...);  // Safe

Applied to all three affected methods:

  • CallReceiveEvents
  • CallErrorEvents
  • CallPinEvents

Customer Impact

Application crash when unsubscribing from SerialPort.DataReceived, ErrorReceived, or PinChanged events within the event handler when multiple event types fire simultaneously.

Regression

No, this has existed in the codebase.

Testing

  • Library builds successfully
  • All 954 tests pass (832 skipped due to no serial hardware in CI)

Risk

Low. Uses standard null-conditional invocation pattern. No behavioral change for correctly functioning code.

Package authoring no longer needed in .NET 9

IMPORTANT: Starting with .NET 9, you no longer need to edit a NuGet package's csproj to enable building and bump the version.
Keep in mind that we still need package authoring in .NET 8 and older versions.

Original prompt

This section details on the original issue you should resolve

<issue_title>SerialPort: NullReferenceException</issue_title>
<issue_description>### Description

.NET may raise NullReferenceException if the handler of the event SerialPort.DataReceived unsubscribes from this event.

The problem is in System.IO.Ports.SerialStream. EventLoopRunner

The function CallReceiveEvents(object state) may raise the event DataReceived twice, but it checks for the presence of the handler only once.
When the first event-raise happens, the application may decide to unsubscribe from this event, or even to dispose the SerialPort instance, then, when the control returns to CallReceiveEvents, it attempts to raise the same event again (without checking again against null), but, at this time, the event may be null if the application unsubscribed from it during the first event-raise, and ==> NullReferenceException; the application crushes. See the screenshot:

Imageack to

Reproduction Steps

  • subscribe to the event SerialPort.DataReceived
  • in the event handler, unsubscribe from SerialPort.DataReceived
  • the issue happens only if the serial port has the Eof in its internal buffer.

The problem should be clear enough from the Description.

Expected behavior

SerialPort should check for the event against null before attempting to raise it.

Actual behavior

.NET raises NullReferenceException and the application crushes.

Looking at the source code, I noticed that all Call*Events functions have the same problem: CallErrorEvents, CallPinEvents, and CallReceiveEvents.

Regression?

No response

Known Workarounds

No response

Configuration

No response

Other information

No response</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Use null-conditional operator (?.) before each event invocation to prevent
NullReferenceException when handlers are unsubscribed during event processing.

This fixes CallReceiveEvents, CallErrorEvents, and CallPinEvents methods
in EventLoopRunner where the event delegate was checked only once but
invoked multiple times.

Fixes #XXXXX

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix NullReferenceException in SerialPort event handling Fix NullReferenceException in SerialPort event callbacks when handler unsubscribes during invocation Jan 3, 2026
Copilot AI requested a review from stephentoub January 3, 2026 03:13
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-io-ports
See info in area-owners.md if you want to be subscribed.

@stephentoub stephentoub marked this pull request as ready for review January 3, 2026 03:35
Copilot AI review requested due to automatic review settings January 3, 2026 03:35
@stephentoub stephentoub enabled auto-merge (squash) January 3, 2026 03:37
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a critical NullReferenceException bug in SerialPort event callbacks when handlers unsubscribe during invocation. The issue occurred when multiple event types fired simultaneously (e.g., SerialData.Chars and SerialData.Eof), causing the code to check for null handlers once but invoke them multiple times, leading to crashes if a handler unsubscribed during the first invocation.

Key changes:

  • Replaced single null check with individual null-conditional invocations for each event type across three callback methods
  • Updated null comparison pattern from == null to is null for consistency with coding standards

@stephentoub
Copy link
Member

/ba-g deadletter

@stephentoub stephentoub merged commit 7b65de3 into main Jan 3, 2026
93 of 96 checks passed
@stephentoub stephentoub deleted the copilot/fix-nullreferenceexception-serialport branch January 3, 2026 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SerialPort: NullReferenceException

3 participants