java-topology/defects/lean4/patch/lean4-0007-runtime-process-envvar-keyset.patch
russell@unturf.com ae6e04c5bd feat: add lean4-0004 through lean4-0007 patches
lean4-0004: collapse 3-phase lock in ir_interpreter lookup_symbol to single unique_lock
lean4-0005: replace IO.Ref JobQueue with Std.Mutex in Lake job registry
lean4-0006: register thread-local reset for g_opts in kernel/trace.cpp
lean4-0007: build unordered_set of override keys outside env-var loop (Windows)
2026-04-13 10:24:23 -04:00

49 lines
2.3 KiB
Diff

# UNDF: UNDF-2026-000001265
# CWE-407: Algorithmic Complexity -- O(N^2) -> O(N) in Windows env var inheritance
#
# Defect: new_env_vars.count({key_begin, key_end}) constructs std::string from
# char range on every iteration of O(N) loop over inherited env vars.
# N env vars * N string constructions = O(N^2). Windows-only code path.
#
# Fix: pre-build std::unordered_set<std::string> of override keys before loop.
# O(1) amortized lookup, one allocation per key not per iteration.
#
# Complexity gate:
# N=500 env vars: must complete in <1ms
--- a/src/runtime/process.cpp
+++ b/src/runtime/process.cpp
@@ -14,6 +14,7 @@
#if defined(LEAN_WINDOWS)
#include <unordered_map>
+#include <unordered_set>
#include <windows.h>
#include <fcntl.h>
#include <io.h>
@@ -237,6 +237,12 @@ static obj_res spawn(string_ref const & proc_name, array_ref<string_ref> const &
if (env.size()) {
std::unordered_map<std::string, option_ref<string_ref>> new_env_vars; // C++17 gives us no-copy std::string_view for this, much better!
for (auto & entry : env) {
new_env_vars[entry.fst().data()] = entry.snd();
}
+ // Pre-build a set of override key strings so the inherit loop below
+ // performs O(1) lookups instead of constructing a std::string from a
+ // char* range on every iteration (was O(N^2) over N env vars).
+ std::unordered_set<std::string> override_keys;
+ override_keys.reserve(new_env_vars.size());
+ for (const auto & ev : new_env_vars) {
+ override_keys.insert(ev.first);
+ }
+
// First copy old evars not in new evars.
if (inherit_env) {
auto *esp = GetEnvironmentStrings();
@@ -244,7 +251,7 @@ static obj_res spawn(string_ref const & proc_name, array_ref<string_ref> const &
while (*key_begin) {
char *key_end = strchr(key_begin, '=');
char *entry_end = key_end + strlen(key_end);
- if (!new_env_vars.count({key_begin, key_end})) {
+ if (!override_keys.count(std::string(key_begin, key_end))) {
new_envp = std::copy(key_begin, entry_end + 1, new_envp);
}
key_begin = entry_end + 1;