java-topology/defects/r-lang-0001/patch/r-lang-0001-namespace-users-hashset.patch
russell@unturf.com 5df58dac32 octave+r-lang: 5-MOAD scan; 1 CWE-407 defect (r-lang-0001), octave MOAD 0002-0005 CLEAN
r-lang-0001: namespace.R getNamespaceUsers() O(N*I) match() linear scan and
namespaceImportMethods() O(G*V) %in% double scan; fix: hash env + reverse index.
Unit test: 5.4x (namespace users) and 5.6x (import methods) speedup. PASS.

Octave: MOAD-0002 through 0005 all CLEAN (single-threaded, no ThreadLocal,
no credential logging found, no concurrent cache races).
2026-03-31 22:31:27 -04:00

46 lines
2.3 KiB
Diff

# UNDF: (leave blank — assigned later)
--- a/src/library/base/R/namespace.R
+++ b/src/library/base/R/namespace.R
@@ -76,10 +76,11 @@ getNamespaceInfo <- function(ns, which)
getNamespaceUsers <- function(ns) {
nsname <- getNamespaceName(asNamespace(ns))
users <- character()
- for (n in loadedNamespaces()) {
- inames <- names(getNamespaceImports(n))
- if (match(nsname, inames, 0L))
- users <- c(n, users)
- }
+ # CWE-407 fix: inames is rebuilt as a vector each iteration; match() is O(|inames|).
+ # Total complexity was O(N * I) where N = loadedNamespaces(), I = avg imports per ns.
+ # Fix: build a named environment (hash-backed) once per outer iteration, O(1) lookup.
+ for (n in loadedNamespaces()) {
+ inames_set <- new.env(hash = TRUE, parent = emptyenv())
+ for (nm in names(getNamespaceImports(n))) assign(nm, TRUE, envir = inames_set)
+ if (exists(nsname, envir = inames_set, inherits = FALSE))
+ users <- c(n, users)
+ }
users
}
@@ -1136,9 +1137,11 @@ namespaceImportMethods <- function(self, ns, vars, from = NULL)
for(i in seq_along(allFuns)) {
## import methods tables if asked for
## or if the corresponding generic was imported
g <- allFuns[[i]]
p <- allPackages[[i]]
- if(exists(g, envir = self, inherits = FALSE) # already imported
- || g %in% vars) { # requested explicitly
+ # CWE-407 fix: g %in% vars is O(|vars|) per iteration — O(|allFuns| * |vars|) total.
+ # Fix: convert vars to a hash-backed environment once before the loop.
+ if(exists(g, envir = self, inherits = FALSE) # already imported
+ || exists(g, envir = vars_set, inherits = FALSE)) { # requested explicitly
tbl <- methods:::.TableMetaName(g, p)
if(is.null(.mergeImportMethods(self, ns, tbl))) { # a new methods table
allVars <- c(allVars, tbl) # import it;else, was merged
@@ -1148,7 +1151,8 @@ namespaceImportMethods <- function(self, ns, vars, from = NULL)
}
}
- if(g %in% vars && !exists(g, envir = self, inherits = FALSE)) {
+ if(exists(g, envir = vars_set, inherits = FALSE) &&
+ !exists(g, envir = self, inherits = FALSE)) {
if(!is.null(f <- get0(g, envir = ns)) && methods::is(f, "genericFunction")) {