]> go.fuhry.dev Git - fsnotify.git/commitdiff
Fix data race on kevent buffer.
authorTilak Sharma <tilaks@google.com>
Tue, 10 Jun 2014 20:09:37 +0000 (13:09 -0700)
committerNathan Youngman <git@nathany.com>
Fri, 13 Jun 2014 02:49:39 +0000 (20:49 -0600)
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

index 1941f9b66633ad2ad9d765ff7c489de54af11300..05fda24803f775cfca70d4fe12b2abce0de79d7f 100644 (file)
@@ -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)