fix(windows): preserve transient runtime admission failures (#2467)

Fixes #2257. Thanks to @chqcjq for the native self-test retry diagnosis in #2266.

Preserve retry disposition through analysis workflows. Native warm admission requires exact sharing/lock codes and affirmative pre-allocation evidence; cleanup ownership takes precedence. No automatic retry or new analysis cache.

Verified with real Windows 11 CLI, same-connection SDK MCP normal/conflict/recovery, the Windows native verifier, 99 focused tests and static/docs gates. Official npm 11.16.0 launcher sources were the application input. No IDA or Ghidra engine was used for this change. Win32 5 was not reproduced; no resolution claim for #1701/#2196. Performance measurements and limits are recorded in experiments/analysis-efficiency/windows-runtime-retry.md.
This commit is contained in:
Ysopwnable authored and GitHub committed 2026-10-13 11:42:54 +08:00
1 parent 23de4f77c7
commit 172407787f
20 files changed
+491 -62

No files matched your search

+11
View File
@@ -85,6 +85,17 @@ Windows failures retain their constraint, requested coordinate, Win32 code,
and system message. Unsupported filesystems and reparse paths are distinct
from OS access denial and missing packaged controls.
A failed runtime-parent admission additionally reports
`runtimeAllocation: "not-started"`: no runtime directory was created. The
Windows adapter uses that fact with numeric sharing/lock codes 32/33 to retain
a retryable private-runtime failure, including after successful native
authority caching. Missing allocation evidence, other codes, and opaque error
text do not establish a safe retry. Analysis errors propagate the producer's
retry disposition through JavaScript, source-map and WABT workflows; transient
failures report `category: unavailable` and retain the original reason.
Incomplete cleanup blocks an operation retry until its owned resources settle.
REA adds no automatic retry loop or new analysis cache for this behavior.
Ordinary drive-absolute paths accept backslashes, forward slashes, or mixed
separators. File identity retains the original requested spelling. Separator
translation does not resolve dot components, repeated separators, or extend
+2
View File
@@ -87,6 +87,8 @@ napi_value failureValue(napi_env env, const Failure& failure) {
set(env, error, "requestedPath", string(env, failure.path));
set(env, error, "win32Code", number(env, failure.win32));
set(env, error, "win32Message", string(env, detail));
if (failure.runtimeDirectoryUnallocated)
set(env, error, "runtimeAllocation", string(env, std::string("not-started")));
return error;
}
+2
View File
@@ -33,6 +33,8 @@ struct Failure : std::runtime_error {
DWORD win32;
std::wstring path;
std::string constraint;
// Affirmative proof for a failed runtime-parent admission; omission is unknown.
bool runtimeDirectoryUnallocated = false;
Failure(std::string constraint, std::wstring path = L"", DWORD code = GetLastError())
: std::runtime_error(constraint), win32(code), path(std::move(path)),
constraint(std::move(constraint)) {}
+6 -1
View File
@@ -212,7 +212,12 @@ static void verifyPrivate(HANDLE handle, const std::wstring& path, bool root) {
}
std::unique_ptr<Runtime> createRuntime(const std::wstring& parentPath, const std::wstring& prefix) {
auto parent = openFile(parentPath, 0, true);
std::unique_ptr<File> parent;
try { parent = openFile(parentPath, 0, true); }
catch (Failure& failure) {
failure.runtimeDirectoryUnallocated = true;
throw;
}
require(!prefix.empty() && prefix.find_first_of(L"\\/:\"<>|?*") == std::wstring::npos,
"Runtime prefix must be one filename component", parentPath, ERROR_INVALID_PARAMETER);
std::array<unsigned char, 16> random;