From 3987db83bd513f7763b1f73c2337860b1be151b0 Mon Sep 17 00:00:00 2001 From: Jacob Tanenbaum Date: Thu, 1 Feb 2024 11:42:01 -0500 Subject: [PATCH] remove the risk of file descriptor reuse from arping using sock.deinitialize() outside of the goroutine that is using the socket leaves the potential for erroneous and unexpected behavior. When calling Ping() very quickly the socket fd can be reused while a goroutine is still trying to read from it and cause the goroutine to deadlock. removing the sock.deinitialize() from the timeout select case as it will be taken care of by the goroutine upon its return Signed-off-by: Jacob Tanenbaum --- arping.go | 3 +-- arping_linux.go | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/arping.go b/arping.go index b305731..570ef9d 100644 --- a/arping.go +++ b/arping.go @@ -121,7 +121,6 @@ func PingOverIface(dstIP net.IP, iface net.Interface) (net.HardwareAddr, time.Du if err != nil { return nil, 0, err } - defer sock.deinitialize() type PingResult struct { mac net.HardwareAddr @@ -131,6 +130,7 @@ func PingOverIface(dstIP net.IP, iface net.Interface) (net.HardwareAddr, time.Du pingResultChan := make(chan PingResult, 1) go func() { + defer sock.deinitialize() // send arp request verboseLog.Printf("arping '%s' over interface: '%s' with address: '%s'\n", dstIP, iface.Name, srcIP) if sendTime, err := sock.send(request); err != nil { @@ -163,7 +163,6 @@ func PingOverIface(dstIP net.IP, iface net.Interface) (net.HardwareAddr, time.Du case pingResult := <-pingResultChan: return pingResult.mac, pingResult.duration, pingResult.err case <-time.After(timeout): - sock.deinitialize() return nil, 0, ErrTimeout } } diff --git a/arping_linux.go b/arping_linux.go index fc225da..9492a5e 100644 --- a/arping_linux.go +++ b/arping_linux.go @@ -23,12 +23,15 @@ func initialize(iface net.Interface) (s *LinuxSocket, err error) { } func (s *LinuxSocket) send(request arpDatagram) (time.Time, error) { + socketTimeout := timeout.Nanoseconds() + t := syscall.NsecToTimeval(socketTimeout) + syscall.SetsockoptTimeval(s.sock, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &t) return time.Now(), syscall.Sendto(s.sock, request.MarshalWithEthernetHeader(), 0, &s.toSockaddr) } func (s *LinuxSocket) receive() (arpDatagram, time.Time, error) { buffer := make([]byte, 128) - socketTimeout := timeout.Nanoseconds() * 2 + socketTimeout := timeout.Nanoseconds() t := syscall.NsecToTimeval(socketTimeout) syscall.SetsockoptTimeval(s.sock, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &t) n, _, err := syscall.Recvfrom(s.sock, buffer, 0)