From: Tilak Sharma Date: Tue, 10 Jun 2014 20:09:37 +0000 (-0700) Subject: Fix data race on kevent buffer. X-Git-Tag: v1.7.2~314 X-Git-Url: https://go.fuhry.dev/?a=commitdiff_plain;h=e1eb18dccf12cb7098d8e1ed043fcec86c1c65f3;p=fsnotify.git Fix data race on kevent buffer. In the BSD implementation of fsnotify, the watcher's kbuf buffers a kevent between syscall.SetKevent (which prepares the kevent) and syscall.Kevent (which registers the kevent). The implementation intends to protect access to kbuf, but fails to do so in addWatch and removeWatch. This change fixes the data race by allocating a new kevent buffer for every method invocation. --- diff --git a/fsnotify_bsd.go b/fsnotify_bsd.go index 1941f9b..05fda24 100644 --- a/fsnotify_bsd.go +++ b/fsnotify_bsd.go @@ -76,8 +76,6 @@ type Watcher struct { Event chan *FileEvent // Events are returned on this channel done chan bool // Channel for sending a "quit message" to the reader goroutine isClosed bool // Set to true when Close() is first called - kbuf [1]syscall.Kevent_t // An event buffer for Add/Remove watch - bufmut sync.Mutex // Protects access to kbuf. } // NewWatcher creates and returns a new kevent instance using kqueue(2) @@ -201,15 +199,13 @@ func (w *Watcher) addWatch(path string, flags uint32) error { w.enFlags[path] = flags w.enmut.Unlock() - w.bufmut.Lock() - watchEntry := &w.kbuf[0] + var kbuf [1]syscall.Kevent_t + watchEntry := &kbuf[0] watchEntry.Fflags = flags syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_ADD|syscall.EV_CLEAR) entryFlags := watchEntry.Flags - w.bufmut.Unlock() - - wd, errno := syscall.Kevent(w.kq, w.kbuf[:], nil, nil) - if wd == -1 { + success, errno := syscall.Kevent(w.kq, kbuf[:], nil, nil) + if success == -1 { return errno } else if (entryFlags & syscall.EV_ERROR) == syscall.EV_ERROR { return errors.New("kevent add error") @@ -240,14 +236,14 @@ func (w *Watcher) removeWatch(path string) error { if !ok { return errors.New(fmt.Sprintf("can't remove non-existent kevent watch for: %s", path)) } - w.bufmut.Lock() - watchEntry := &w.kbuf[0] + var kbuf [1]syscall.Kevent_t + watchEntry := &kbuf[0] syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_DELETE) - success, errno := syscall.Kevent(w.kq, w.kbuf[:], nil, nil) - w.bufmut.Unlock() + entryFlags := watchEntry.Flags + success, errno := syscall.Kevent(w.kq, kbuf[:], nil, nil) if success == -1 { return os.NewSyscallError("kevent_rm_watch", errno) - } else if (watchEntry.Flags & syscall.EV_ERROR) == syscall.EV_ERROR { + } else if (entryFlags & syscall.EV_ERROR) == syscall.EV_ERROR { return errors.New("kevent rm error") } syscall.Close(watchfd)