Skip to content

compiler: gigacrash when compiling gvisor netstack #5002

@soypat

Description

@soypat

Full log of tinygo build . attached

$ tinygo build .
SIGSEGV: segmentation violation
PC=0x635f4d5 m=10 sigcode=1 addr=0x40
signal arrived during cgo execution

When compiling following program

module noisy

go 1.24.2

require (
	github.com/noisysockets/netstack v0.9.0
	gvisor.dev/gvisor v0.0.0-20250819051428-5ba23728415e
)

require (
	github.com/google/btree v1.1.2 // indirect
	golang.org/x/sys v0.26.0 // indirect
	golang.org/x/time v0.7.0 // indirect
)
//go:build linux

// tapstack/main.go
package main

import (
	"crypto/rand"
	"encoding/binary"
	"flag"
	"fmt"
	"log"
	"net"
	"time"
	"unsafe"

	"golang.org/x/sys/unix"

	// Netstack (noisysockets fork)
	"github.com/noisysockets/netstack/pkg/tcpip"
	"github.com/noisysockets/netstack/pkg/tcpip/adapters/gonet"
	"github.com/noisysockets/netstack/pkg/tcpip/link/fdbased"
	"github.com/noisysockets/netstack/pkg/tcpip/network/arp"
	"github.com/noisysockets/netstack/pkg/tcpip/network/ipv4"
	"github.com/noisysockets/netstack/pkg/tcpip/network/ipv6"
	"github.com/noisysockets/netstack/pkg/tcpip/stack"
	"github.com/noisysockets/netstack/pkg/tcpip/transport/tcp"
)

const (
	nicID = tcpip.NICID(1)

	// from <linux/if_tun.h>
	cIFFTAP   = 0x0002
	cIFFNO_PI = 0x1000
)

type ifreq struct {
	Name  [unix.IFNAMSIZ]byte
	Flags uint16
	_     [24 - 2]byte // sizeof(struct ifreq) varies; this is enough for TUNSETIFF
}

func openTAP(name string) (int, error) {
	fd, err := unix.Open("/dev/net/tun", unix.O_RDWR|unix.O_NONBLOCK, 0)
	if err != nil {
		return -1, err
	}
	var req ifreq
	copy(req.Name[:], name)
	req.Flags = cIFFTAP | cIFFNO_PI
	// TUNSETIFF = 0x400454ca on most arch; use unix.IoctlSetInt is tricky here,
	// so call Ioctl with the struct pointer directly.
	const TUNSETIFF = 0x400454ca
	if _, _, e := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(TUNSETIFF), uintptr(unsafe.Pointer(&req))); e != 0 {
		unix.Close(fd)
		return -1, e
	}
	return fd, nil
}

func parseCIDR4(cidr string) ([4]byte, int) {
	ip, ipnet, err := net.ParseCIDR(cidr)
	if err != nil {
		log.Fatalf("invalid -cidr %q: %v", cidr, err)
	}
	ip = ip.To4()
	if ip == nil {
		log.Fatalf("-cidr must be IPv4")
	}
	prefix, _ := ipnet.Mask.Size()
	var ip4 [4]byte
	copy(ip4[:], ip)
	return ip4, prefix
}

func defaultSubnet() tcpip.Subnet {
	return (tcpip.AddressWithPrefix{
		Address:   tcpip.AddrFrom4([4]byte{}),
		PrefixLen: 0,
	}).Subnet()
}

func randLaaMAC() tcpip.LinkAddress {
	var b [6]byte
	if _, err := rand.Read(b[:]); err != nil {
		// fall back to time-based
		now := time.Now().UnixNano()
		binary.LittleEndian.PutUint64(append(b[:0], make([]byte, 8)...), uint64(now))
		copy(b[:], append(b[:0], byte(now), byte(now>>8), byte(now>>16), byte(now>>24), byte(now>>32), byte(now>>40)))
	}
	// Set locally-administered, unicast: set bit1, clear bit0 of first byte.
	b[0] = (b[0] | 0x02) &^ 0x01
	return tcpip.LinkAddress(b[:])
}

func main() {
	dev := flag.String("dev", "tap0", "TAP device name (e.g. tap0)")
	cidr := flag.String("cidr", "10.0.0.2/24", "IPv4 address/prefix for the stack (e.g. 10.0.0.2/24)")
	gw := flag.String("gw", "", "default gateway IPv4 (optional, e.g. 10.0.0.1)")
	port := flag.Int("port", 8080, "TCP port to listen on (echo server)")
	mtu := flag.Int("mtu", 1500, "interface MTU")
	macStr := flag.String("mac", "", "MAC address to use on TAP (optional; random LAA if empty)")
	flag.Parse()

	// Open TAP (no kernel IP header; raw Ethernet frames).
	fd, err := openTAP(*dev)
	if err != nil {
		log.Fatalf("openTAP(%s): %v", *dev, err)
	}

	// Choose MAC address.
	var linkAddr tcpip.LinkAddress
	if *macStr != "" {
		la, err := tcpip.ParseMACAddress(*macStr)
		if err != nil {
			log.Fatalf("parse MAC: %v", err)
		}
		linkAddr = la
	} else {
		linkAddr = randLaaMAC()
	}

	// Wire TAP into netstack as an Ethernet link endpoint.
	linkEP, err := fdbased.New(&fdbased.Options{
		FDs:            []int{fd},
		MTU:            uint32(*mtu),
		EthernetHeader: true,
		Address:        linkAddr,
	})
	if err != nil {
		log.Fatalf("fdbased.New: %v", err)
	}

	// Build the stack (ARP, IPv4/IPv6, TCP).
	s := stack.New(stack.Options{
		NetworkProtocols: []stack.NetworkProtocolFactory{
			arp.NewProtocol,
			ipv4.NewProtocol,
			ipv6.NewProtocol,
		},
		TransportProtocols: []stack.TransportProtocolFactory{
			tcp.NewProtocol,
		},
	})

	// Create NIC on the link endpoint.
	if err := s.CreateNIC(nicID, linkEP); err != nil {
		log.Fatalf("CreateNIC: %v", err)
	}

	// Assign IPv4 address.
	ip4, prefix := parseCIDR4(*cidr)
	addrWithPrefix := tcpip.AddressWithPrefix{
		Address:   tcpip.AddrFrom4(ip4),
		PrefixLen: prefix,
	}
	terr := s.AddProtocolAddress(nicID, tcpip.ProtocolAddress{
		Protocol:          ipv4.ProtocolNumber,
		AddressWithPrefix: addrWithPrefix,
	}, stack.AddressProperties{})
	if terr != nil {
		log.Fatalf("AddProtocolAddress: %v", terr)
	}

	// Routes: on-link subnet + optional default route via gw.
	routes := []tcpip.Route{
		{Destination: addrWithPrefix.Subnet(), NIC: nicID},
	}
	if *gw != "" {
		ip := net.ParseIP(*gw).To4()
		if ip == nil {
			log.Fatalf("-gw must be IPv4")
		}
		routes = append(routes, tcpip.Route{
			Destination: defaultSubnet(),
			Gateway:     tcpip.AddrFrom4([4]byte{ip[0], ip[1], ip[2], ip[3]}),
			NIC:         nicID,
		})
	}
	s.SetRouteTable(routes)

	// Listen on TCP <stack-ip>:port using the gonet adapter.
	laddr := tcpip.FullAddress{
		NIC:  nicID,
		Addr: addrWithPrefix.Address,
		Port: uint16(*port),
	}
	ln, err := gonet.ListenTCP(s, laddr, ipv4.ProtocolNumber)
	if err != nil {
		log.Fatalf("ListenTCP: %v", err)
	}
	defer ln.Close()

	fmt.Printf("netstack up on %s (MAC %s, MTU %d)\n", *dev, linkAddr.String(), *mtu)
	fmt.Printf("IPv4 %s, listening on TCP %d\n", addrWithPrefix.String(), *port)
	if *gw != "" {
		fmt.Printf("Default route via %s\n", *gw)
	}
	fmt.Println("Echo server ready.")

	for {
		c, err := ln.Accept()
		if err != nil {
			log.Fatalf("Accept: %v", err)
		}
		go func(conn net.Conn) {
			defer conn.Close()
			_ = conn.SetDeadline(time.Now().Add(5 * time.Minute))
			buf := make([]byte, 32<<10)
			for {
				n, err := conn.Read(buf)
				if n > 0 {
					_, _ = conn.Write(buf[:n]) // echo
				}
				if err != nil {
					return
				}
			}
		}(c)
	}
}

log.txt

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions