Skip to content

[Robustness] ResourceHelper.TerminateBlockingProcesses — for .exe resources, kills ALL processes matching the filename, including unrelated instances belonging to other services #938

@Christophe-Rogiers

Description

@Christophe-Rogiers

Severity: Warning

File: src/Servy.Core/Helpers/ResourceHelper.cs (lines 282-294)

Code:

private bool TerminateBlockingProcesses(string extension, string targetFileName, string targetPath)
{
    var isExe = extension.Equals("exe", StringComparison.OrdinalIgnoreCase);
    var isDll = extension.Equals("dll", StringComparison.OrdinalIgnoreCase);

    if (isExe && !_processKiller.KillProcessTreeAndParents(targetFileName))
        return false;

    if (isDll && !_processKiller.KillProcessesUsingFile(targetPath))
        return false;

    return true;
}

Explanation:
For .exe resources, TerminateBlockingProcesses calls KillProcessTreeAndParents(string processName), which (via KillProcessTreeAndParents(string), ProcessKiller.cs lines 173-221) enumerates every process whose ProcessName equals the bare filename (case-insensitive) and kills its tree.

In Servy's deployment model the only .exe resource extracted this way today is Servy.Restarter.exe (Service.cs line 107, ServyRestarterExeFileName = "Servy.Restarter"). A single host commonly runs multiple Servy-managed services, each potentially having a Servy.Restarter.exe instance attached to its own service. When the wrapper service for service A starts and decides the embedded restarter on disk is stale (per ShouldCopyResource, ResourceHelper.cs lines 222-273), it calls TerminateBlockingProcesses("exe", "Servy.Restarter.exe", ...) — and that kills the restarters for services B, C, D, … as collateral damage, even though they are mid-recovery for unrelated processes.

The DLL branch handles this correctly: KillProcessesUsingFile(targetPath) enumerates only processes whose handles point at the specific path. The exe branch has no equivalent path filter.

Suggested fix:
Make the exe branch path-specific too:

if (isExe)
{
    // Only kill instances actually using THIS exe path, not every process named the same.
    if (!_processKiller.KillProcessesUsingFile(targetPath)) return false;

    // OR: enumerate processes and match by MainModule.FileName == targetPath
    // before delegating to KillProcessTreeAndParents(int pid, ...).
}

This preserves the file-replacement guarantee while avoiding cross-service damage.

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions