Skip to content

Fix dangling uintptr bugs#139

Merged
safchain merged 2 commits into
safchain:masterfrom
tsheinen:ethtool-ioctl-dangling-uintptr
Jun 11, 2026
Merged

Fix dangling uintptr bugs#139
safchain merged 2 commits into
safchain:masterfrom
tsheinen:ethtool-ioctl-dangling-uintptr

Conversation

@tsheinen

Copy link
Copy Markdown
Contributor
  • Add test calling FeatureNames at varying stack depths

Ethtool.getNames can fail when calling the ETHTOOL_GSSET_INFO ioctl if the morestack prologue of Ethtool.ioctl grows the stack. This is because the ssetInfo variable is stored on the stack, and uintptr is opaque and not updated when the stack is moved.

Add a test TestFeatureNamesStackDepth to assert the correctness at varying stack depths. It is expected to fail at this commit and will be fixed in the next.

 --- FAIL: TestFeatureNamesStackDepth (0.00s)
ethtool_test.go:224: FeatureNames("lo") incorrect at stack depth=10: got 0 features, want 60. 
  • Fix dangling uintptr bugs

A uintptr is opaque to the runtime and so any time an unsafe.Pointer is converted into a uintptr we must ensure that the underlying pointer cannot be invalidated.

The documentation for unsafe.Pointer lists the safe ways to use uintptr. The relevant subsection for this patch is 2), a uintptr has no pointer semantics and it is unsafe to treat it as a live reference except in a limited set of cases. Subsection 4) describes the exception we rely on, that converting unsafe.Pointer to uintptr in an argument is live until the call completes. We'll also store unsafe.Pointer instead of uintptr in the ifreq struct. Both types have the same underlying representation and so this is equivalent to the kernel.

I chose to introduce a failing test in the first commit so it can be checked out independently and tested. If you'd prefer each commit to be passing, let me know and I can squash or reorder :)

tsheinen added 2 commits June 10, 2026 13:55
Ethtool.getNames can fail when calling the ETHTOOL_GSSET_INFO ioctl
if the morestack prologue of Ethtool.ioctl grows the stack. This is
because the ssetInfo variable is stored on the stack, and uintptr is
opaque and not updated when the stack is moved.

Add a test `TestFeatureNamesStackDepth` to assert the correctness at varying
stack depths. It is expected to fail at this commit and will be fixed in
the next.

```
--- FAIL: TestFeatureNamesStackDepth (0.00s)
    ethtool_test.go:224: FeatureNames("lo") incorrect at stack depth=10: got 0 features, want 60.
```
A uintptr is opaque to the runtime and so any time an unsafe.Pointer is
converted into a uintptr we must ensure that the underlying pointer
cannot be invalidated.

The documentation for
[unsafe.Pointer](https://pkg.go.dev/unsafe#Pointer) lists the safe ways
to use uintptr. The relevant subsection for this patch is 2), a uintptr has no pointer semantics and it is unsafe to treat it as a live reference except in a limited set of cases. Subsection 4) describes the exception we rely on, that converting unsafe.Pointer to uintptr in an argument is live until the call completes. We'll also store unsafe.Pointer instead of uintptr in the ifreq struct. Both types have the same underlying representation and so this is equivalent to the kernel.
@safchain safchain merged commit ab247b0 into safchain:master Jun 11, 2026
3 checks passed
@safchain

Copy link
Copy Markdown
Owner

Thanks for the PR.

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.

2 participants