Skip to content

Commit

Permalink
remove the risk of file descriptor reuse from arping
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
JacobTanenbaum committed Feb 9, 2024
1 parent c50f431 commit 3987db8
Show file tree
Hide file tree
Showing 2 changed files with 5 additions and 3 deletions.
3 changes: 1 addition & 2 deletions arping.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
}
Expand Down
5 changes: 4 additions & 1 deletion arping_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 3987db8

Please sign in to comment.