Skip to content

Commit

Permalink
Protect entryCache with lock
Browse files Browse the repository at this point in the history
If ToEntry() is called concurrently after entryCache is cleared with
ClearEntryCache(), then it is possible for a panic to occur as
entryCache is being concurrently read/written.

Fixes #261
  • Loading branch information
sengleung committed Dec 13, 2023
1 parent 5ad0d2f commit 9eb5fef
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 8 deletions.
9 changes: 8 additions & 1 deletion pkg/yang/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -559,11 +559,18 @@ func ToEntry(n Node) (e *Entry) {
}
}
ms := RootNode(n).Modules
if e := ms.entryCache[n]; e != nil {

// Protect the entryCache map from concurrent access.
ms.entryCacheMu.RLock()
e = ms.entryCache[n]
ms.entryCacheMu.RUnlock()
if e != nil {
return e
}
defer func() {
ms.entryCacheMu.Lock()
ms.entryCache[n] = e
ms.entryCacheMu.Unlock()
}()

// Copy in the extensions from our Node, if any.
Expand Down
17 changes: 10 additions & 7 deletions pkg/yang/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ import (
// Modules contains information about all the top level modules and
// submodules that are read into it via its Read method.
type Modules struct {
Modules map[string]*Module // All "module" nodes
SubModules map[string]*Module // All "submodule" nodes
includes map[*Module]bool // Modules we have already done include on
nsMu sync.Mutex // nsMu protects the byNS map.
byNS map[string]*Module // Cache of namespace lookup
typeDict *typeDictionary // Cache for type definitions.
Modules map[string]*Module // All "module" nodes
SubModules map[string]*Module // All "submodule" nodes
includes map[*Module]bool // Modules we have already done include on
nsMu sync.Mutex // nsMu protects the byNS map.
byNS map[string]*Module // Cache of namespace lookup
typeDict *typeDictionary // Cache for type definitions.
entryCacheMu sync.RWMutex // entryCacheMu protects the entryCache map.
// entryCache is used to prevent unnecessary recursion into previously
// converted nodes.
entryCache map[Node]*Entry
Expand Down Expand Up @@ -323,7 +324,7 @@ func (ms *Modules) Process() []error {
// Reset globals that may remain stale if multiple Process() calls are
// made by the same caller.
ms.mergedSubmodule = map[string]bool{}
ms.entryCache = map[Node]*Entry{}
ms.ClearEntryCache()

errs := ms.process()
if len(errs) > 0 {
Expand Down Expand Up @@ -446,5 +447,7 @@ func (ms *Modules) include(m *Module) error {
// ClearEntryCache clears the entryCache containing previously converted nodes
// used by the ToEntry function.
func (ms *Modules) ClearEntryCache() {
ms.entryCacheMu.Lock()
defer ms.entryCacheMu.Unlock()
ms.entryCache = map[Node]*Entry{}
}

0 comments on commit 9eb5fef

Please sign in to comment.