From e1eb18dccf12cb7098d8e1ed043fcec86c1c65f3 Mon Sep 17 00:00:00 2001 From: Tilak Sharma Date: Tue, 10 Jun 2014 13:09:37 -0700 Subject: [PATCH] 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. --- fsnotify_bsd.go | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) 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) -- 2.50.1