java-topology/defects/ruby/patch/solargraph-0001-inference-stack-thread-local-set.patch

87 lines
3.5 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# UNDF: UNDF-2026-000000265
From: agent-blackops <blackops@unturf.com>
Date: Thu, 26 Mar 2026 00:00:00 +0000
Subject: [PATCH] source/chain: replace @@inference_stack class-variable Array with thread-local Set
CWE-407 + thread-safety defect in Chain#infer_from_definitions.
@@inference_stack was a class-level Array shared across all threads.
Two defects:
1. CWE-407: `@@inference_stack.include?(pin)` is O(D) per pin where D
is the current inference depth. Called inside two loops in
`infer_from_definitions` — once for typify pins and once for probe
pins — making total cost O(D × |pins|) per infer call.
2. Thread-safety: a class variable mutated with push/pop from multiple
Ractors/threads (e.g., concurrent LSP requests) causes races.
One thread's push/pop interleaves with another's, corrupting the
recursion guard.
Fix: replace with `Thread.current[:solargraph_inference_stack]`,
initialised lazily as `Set.new` per thread. Set#include? is O(1).
Each thread owns its own stack, eliminating the race. add/delete
replace push/pop (order is irrelevant for a recursion guard).
@@inference_depth and @@inference_cache are left as class variables —
they are either counters (depth) or caches that benefit from sharing
(cache). Only the identity-guard set needs per-thread isolation.
Defect-Id: solargraph-0001
Severity: MEDIUM
CWE: CWE-407 (Inefficient Algorithmic Complexity), CWE-362 (Race Condition)
---
lib/solargraph/source/chain.rb | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/lib/solargraph/source/chain.rb b/lib/solargraph/source/chain.rb
index xxxxxxx..yyyyyyy 100644
--- a/lib/solargraph/source/chain.rb
+++ b/lib/solargraph/source/chain.rb
@@ -38,7 +38,7 @@ module Solargraph
@@inference_stack = []
+ # CWE-407 fix: removed @@inference_stack class variable — now thread-local Set (see below)
@@inference_depth = 0
@@inference_invalidation_key = nil
@@inference_cache = {}
@@ -220,6 +220,11 @@ module Solargraph
+ # Returns the per-thread inference stack Set (O(1) include?).
+ # CWE-407 fix: replaces shared @@inference_stack Array.
+ def inference_stack
+ Thread.current[:solargraph_inference_stack] ||= Set.new # CWE-407 fix
+ end
+
def infer_from_definitions pins, context, api_map, locals
types = []
unresolved_pins = []
@@ -227,19 +232,18 @@ module Solargraph
pins.each do |pin|
# Avoid infinite recursion
- next if @@inference_stack.include?(pin)
+ next if inference_stack.include?(pin) # CWE-407 fix: O(1) set lookup
- @@inference_stack.push pin
+ inference_stack.add(pin) # CWE-407 fix
type = pin.typify(api_map)
- @@inference_stack.pop
+ inference_stack.delete(pin) # CWE-407 fix
if type.defined?
@@ -255,11 +259,11 @@ module Solargraph
@@inference_depth += 1
unresolved_pins.each do |pin|
# Avoid infinite recursion
- if @@inference_stack.include?(pin.identity)
+ if inference_stack.include?(pin.identity) # CWE-407 fix: O(1) set lookup
next
end
- @@inference_stack.push(pin.identity)
+ inference_stack.add(pin.identity) # CWE-407 fix
type = pin.probe(api_map)
- @@inference_stack.pop
+ inference_stack.delete(pin.identity) # CWE-407 fix
types.push type if type
end
@@inference_depth -= 1