Skip to content
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

x/net/icmp: descriptor closed twice (bad file descriptor error) #16969

Closed
adzeitor opened this issue Sep 2, 2016 · 2 comments
Closed

x/net/icmp: descriptor closed twice (bad file descriptor error) #16969

adzeitor opened this issue Sep 2, 2016 · 2 comments

Comments

@adzeitor
Copy link

adzeitor commented Sep 2, 2016

What version of Go are you using (go version)?

go version go1.7 linux/amd64

What operating system and processor architecture are you using (go env)?

GOARCH="amd64"
GOOS="linux"

What did you do?

Ping 127.0.0.1 (localhost) with many goroutines. Try run few times:

package main

import (
    "fmt"
    "log"
    "net"
    "os"

    "golang.org/x/net/icmp"
    "golang.org/x/net/ipv4"
    "golang.org/x/net/ipv6"
)

func doPing(host string) error {
    network := "udp4"
    address := "0.0.0.0"
    protocol := 1 // Internet Control Message
    mtype := ipv4.ICMPTypeEcho

    c, err := icmp.ListenPacket(network, address)
    if err != nil {
        return err
    }

    defer c.Close()

    dst := &net.UDPAddr{IP: net.ParseIP(host)}

    wm := icmp.Message{
        Type: mtype, Code: 0,
        Body: &icmp.Echo{
            ID: os.Getpid() & 0xffff, Seq: 1 << uint(0),
            Data: []byte("ello gov'nor"),
        },
    }

    wb, err := wm.Marshal(nil)
    if err != nil {
        return err
    }

    if n, err := c.WriteTo(wb, dst); err != nil {
        return err
    } else if n != len(wb) {
        return fmt.Errorf("got %v; want %v", n, len(wb))
    }

    rb := make([]byte, 1500)
    n, peer, err := c.ReadFrom(rb)
    if err != nil {
        return err
    }
    rm, err := icmp.ParseMessage(protocol, rb[:n])
    if err != nil {
        return err
    }
    switch rm.Type {
    case ipv4.ICMPTypeEchoReply, ipv6.ICMPTypeEchoReply:
        return nil
    default:
        return fmt.Errorf("got %+v from %v; want echo reply", rm, peer)
    }
}

func main() {
    for i := 1; i < 1000; i++ {
        go func() {
            err := doPing("127.0.0.1")
            if err != nil {
                log.Fatal(err)
            }
        }()
    }
}

What did you expect to see?

Nothing. No errors.

What did you see instead?

Maybe second close happens on another reused connection fd.

2016/09/02 18:51:08 file file+net datagram-oriented icmp: getsockopt: bad file descriptor

Minimal program to strace

package main
import "golang.org/x/net/icmp"

func main() {
  c, _ := icmp.ListenPacket("udp4", "0.0.0.0")
  c.Close()
}
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
close(3)                                = 0
socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP) = 3
setsockopt(3, SOL_IPV6, IPV6_V6ONLY, [1], 4) = 0
bind(3, {sa_family=AF_INET6, sin6_port=htons(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0
socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP) = 4
setsockopt(4, SOL_IPV6, IPV6_V6ONLY, [0], 4) = 0
bind(4, {sa_family=AF_INET6, sin6_port=htons(0), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0
close(4)                                = 0
close(3)                                = 0
socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP) = 3
bind(3, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
fcntl(3, F_DUPFD_CLOEXEC, 0)            = 4
fcntl(4, F_GETFL)                       = 0x2 (flags O_RDWR)
fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK)    = 0
getsockopt(4, SOL_SOCKET, SO_TYPE, [2], [4]) = 0
getsockname(4, {sa_family=AF_INET, sin_port=htons(20618), sin_addr=inet_addr("0.0.0.0")}, [16]) = 0
getpeername(4, 0xc42004dbc8, 0xc42004dbc4) = -1 ENOTCONN (Transport endpoint is not connected)
epoll_create1(EPOLL_CLOEXEC)            = 5
epoll_ctl(5, EPOLL_CTL_ADD, 4, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=3043491584, u64=140255200542464}}) = 0
close(3)                                = 0
close(3)                                = -1 EBADF (Bad file descriptor)
epoll_ctl(5, EPOLL_CTL_DEL, 4, 0xc42004dda4) = 0
close(4)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

Possible fix

When this defer syscall.Close(s) removed from icmp/listen_posix.go:69, no multiple closes on fd happens.

@adzeitor adzeitor changed the title x/net/icmp descriptor closed twice (bad file descriptor error) x/net/icmp: descriptor closed twice (bad file descriptor error) Sep 2, 2016
@mikioh
Copy link
Contributor

mikioh commented Sep 3, 2016

Thanks for the report. Will send a fix soon.

@mikioh mikioh added this to the Unreleased milestone Sep 3, 2016
@gopherbot
Copy link

CL https://golang.org/cl/28473 mentions this issue.

@golang golang locked and limited conversation to collaborators Sep 10, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants