Skip to content

Prevent panic during TCP EOF (#521) #522

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 19, 2025
Merged

Prevent panic during TCP EOF (#521) #522

merged 1 commit into from
Jul 19, 2025

Conversation

stepga
Copy link
Contributor

@stepga stepga commented Jul 18, 2025

When performing SNMP over TCP, a panic could have been triggered due to a nil pointer dereference in marshal.go:send().

The following example could have led to a panic:

  • Perform a GET SNMP request over TCP
  • Wait for the response in the waitingResponse loop
  • (*GoSNMP).receive() returns io.EOF
  • A new tcp connection will be established successfully via (*GoSNMP).netConnect and err will be set to nil
  • Break out of the waitingResponse loop
  • err == nil and result == nil, because latter would be initialized only after a successful (*GoSNMP).receive()
  • Reach the "Success!" point and erroneously return nil, nil

This will lead to a nil pointer dereference in the calling function send.

This patch fixes the bug, by actually performing the retry and continuing the outer retry loop.

The patch also removes the decrementation of the retries counter, as this:

// EOF on TCP: reconnect and retry. Do not count
// as retry as socket was broken

is seen a legitimate failed request attempt.

Also, remove the decrementation of retries here: it could potentially lead to an infinite loop, if reconnecting to the server always succeeds but also always results in an immediate EOF upon receiving the response.

Also, ensure that err is always set if the retries counter exceeds `(*GoSNMP).Retries.

When performing SNMP over TCP, a panic could have been triggered
due to a nil pointer dereference in marshal.go:send().

The following example could have led to a panic:
- Perform a GET SNMP request over TCP
- Wait for the response in the `waitingResponse` loop
- `(*GoSNMP).receive()` returns io.EOF
- A new tcp connection will be established successfully via
  `(*GoSNMP).netConnect` and `err` will be set to `nil`
- Break out of the `waitingResponse` loop
- `err == nil` and `result == nil`, because latter would
  be initialized only after a successful `(*GoSNMP).receive()`
- Reach the "Success!" point and erroneously `return nil, nil`

This will lead to a nil pointer dereference in the calling
function `send`.

This patch fixes the bug, by actually performing the retry and
continuing the outer retry loop.

The patch also removes the decrementation of the `retries` counter,
as this:

> // EOF on TCP: reconnect and retry. Do not count
> // as retry as socket was broken

is seen a legitimate failed request attempt.

Also, remove the decrementation of `retries` here:
it could potentially lead to an infinite loop, if reconnecting to
the server always succeeds but also always results in an immediate
EOF upon receiving the response.

Also, ensure that `err` is always set if the `retries` counter
exceeds `(*GoSNMP).Retries.

The added test is the work of @TimRots.

Signed-off-by: Stephan Gabert <stepga@nirgendwo.eu>
@TimRots TimRots requested a review from SuperQ July 19, 2025 12:31
Copy link
Contributor

@SuperQ SuperQ left a comment

Choose a reason for hiding this comment

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

I'm not a fan of loop goto jumps like this. But I'm guessing we would need some serious refactoring in order to eliminate it.

@@ -202,6 +203,9 @@ func (x *GoSNMP) sendOneRequest(packetOut *SnmpPacket,
break
}
if retries > x.Retries {
if err == nil {
err = fmt.Errorf("max retries (%d) exceeded", x.Retries)
Copy link
Contributor

Choose a reason for hiding this comment

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

Future improvement idea: Create ErrMaxRetriesExceeded package var so that errors.Is() can be used.

For now, this seems fine.

Copy link
Member

Choose a reason for hiding this comment

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

Future improvement idea: Create ErrMaxRetriesExceeded package var so that errors.Is() can be used.

For now, this seems fine.

Created issue #523 with your enhancement

@TimRots TimRots merged commit 2a929d6 into gosnmp:master Jul 19, 2025
13 checks passed
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.

Wrong handling of TCP connection closing/EOF leads to nil pointer dereference
3 participants