Skip to content
This repository has been archived by the owner on Jun 8, 2022. It is now read-only.

Commit

Permalink
Fix data race on kevent buffer.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Tilak Sharma committed Jun 10, 2014
1 parent 441bbc8 commit 35a7e13
Showing 1 changed file with 9 additions and 13 deletions.
22 changes: 9 additions & 13 deletions fsnotify_bsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,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)
Expand Down Expand Up @@ -207,15 +205,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")
Expand Down Expand Up @@ -246,14 +242,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)
Expand Down

0 comments on commit 35a7e13

Please sign in to comment.