Skip to content

netstack: return EADDRNOTAVAIL for SIOCGIFADDR/SIOCGIFNETMASK without IPv4#13433

Open
notandruu wants to merge 1 commit into
google:masterfrom
notandruu:fix/netstack-ioctl-13431
Open

netstack: return EADDRNOTAVAIL for SIOCGIFADDR/SIOCGIFNETMASK without IPv4#13433
notandruu wants to merge 1 commit into
google:masterfrom
notandruu:fix/netstack-ioctl-13431

Conversation

@notandruu

Copy link
Copy Markdown

Fixes #13431

Root cause

In interfaceIoctl (pkg/sentry/socket/netstack/netstack.go), the SIOCGIFADDR and SIOCGIFNETMASK cases loop over interface addresses looking for AF_INET. When the interface has no IPv4 address (for example an IPv6-only interface), the loop finds nothing and the function returns success without writing the ifreq. The caller then reads back whatever was previously in the ifreq union, which is how ifconfig ends up displaying a fabricated IPv4 address built from stale bytes (in the issue's strace, remnants of the preceding SIOCGIFMTU call).

Linux returns EADDRNOTAVAIL for both ioctls in this case (net/ipv4/devinet.c:devinet_ioctl, the ifa == NULL path shared by the SIOCGIF* address ioctls).

Changes

  • SIOCGIFADDR and SIOCGIFNETMASK now return EADDRNOTAVAIL when the interface has no IPv4 address.
  • The SIOCGIFADDR success path now also populates sa_family (AF_INET) and the port bytes; previously only the 4 address bytes were written, while Linux fills in the full sockaddr_in. The SIOCGIFNETMASK success path already did this.
  • New syscall tests: NetdeviceTest.InterfaceAddr (loopback, family + address) and NetdeviceTest.InterfaceAddrIoctlsNoIPv4Addr (TUN device with no addresses, expects EADDRNOTAVAIL for both ioctls; gated on CAP_NET_ADMIN).

Verification

Probe calling SIOCGIFMTU, then SIOCGIFADDR and SIOCGIFNETMASK with the ifreq poisoned with 0xAA bytes:

case native Linux (6.x) runsc before runsc after
GIFADDR, IPv6-only interface EADDRNOTAVAIL success, poison bytes returned EADDRNOTAVAIL
GIFNETMASK, IPv6-only interface EADDRNOTAVAIL success, poison bytes returned EADDRNOTAVAIL
GIFADDR, interface with IPv4 02000000 + addr poison family bytes + addr 02000000 + addr (matches Linux)
GIFNETMASK, interface with IPv4 family + mask family + mask family + mask

The IPv6-only scenario was reproduced with runsc under Docker on an IPv6-only network (docker network create --ipv6 --ipv4=false), matching the issue's EKS setup. A standalone mirror of the new test cases passes on native Linux and patched runsc, and fails on stock runsc with exactly the three asserted behaviors.

Authored with the help of an AI assistant, per AGENTS.md the ABI change was verified against Linux kernel behavior as described above.

… IPv4

On an interface with no IPv4 address assigned (for example an IPv6-only
interface), SIOCGIFADDR and SIOCGIFNETMASK returned success without
writing the ifreq, letting callers read back stale ifreq union data.
ifconfig displays this as a fabricated IPv4 address. Linux returns
EADDRNOTAVAIL for both ioctls in this case
(net/ipv4/devinet.c:devinet_ioctl).

Also populate sa_family (AF_INET) and the port bytes in the SIOCGIFADDR
success path; previously only the address bytes were written, while
Linux fills in the full sockaddr_in. SIOCGIFNETMASK already did this.

Verified against Linux: a probe running SIOCGIFMTU, SIOCGIFADDR, and
SIOCGIFNETMASK against an interface without IPv4 returns EADDRNOTAVAIL
for the latter two on native Linux and on patched runsc, while stock
runsc returns success with the caller's poison bytes intact. On an
interface with IPv4, patched runsc output matches native Linux byte for
byte.

Fixes google#13431
@google-cla

google-cla Bot commented Jun 12, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

hostarch.ByteOrder.PutUint16(ifr.Data[2:], 0)
copy(ifr.Data[4:8], addr.Addr)
break
return nil

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why return here instead of breaking? The other "success" cases don't return, they allow execution after the switch statement. It doesn't matter today since nothing happens outside the switch, but that might change in the future. Same for line 3692

@ayushr2

ayushr2 commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the fix! Could you sign the CLA?

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.

SIOCGIFADDR returns success with stale ifreq data on IPv6-only interfaces

2 participants