Severity: Warning
File: src/Servy.Core/Helpers/ProcessKiller.cs:167-222
Code:
```csharp
public bool KillProcessTreeAndParents(string processName, bool killParents = true)
{
...
var protectedPids = GetAncestorPids();
var byParent = BuildParentChildMapNative();
var allProcesses = Process.GetProcesses();
try
{
var targetProcesses = allProcesses
.Where(p => string.Equals(p.ProcessName, processName, StringComparison.OrdinalIgnoreCase))
.Where(p => !protectedPids.Contains(p.Id)) // <-- only filters by PID
.ToList();
...
foreach (var proc in targetProcesses)
{
KillProcessTree(proc, selfPid, protectedPids, byParent); // root killed unconditionally
}
...
}
...
}
```
Explanation:
The class maintains `CriticalSystemProcesses` (line 28) — a hardcoded safelist of "never kill" Windows process names (`csrss`, `lsass`, `wininit`, `services`, `svchost`, `explorer`, `MsMpEng`, ...). That safelist is consulted in:
- `IsProtected` (line 48), used by descendants in `WalkAndKillChildren` (line 140) and parents in `KillParentProcesses` (via `IsProtected` line 385)
- `KillProcessesUsingFile` (line 308-314)
But `KillProcessTreeAndParents(string processName)` does not consult it. If a caller passes `"svchost"` or `"explorer"` (deliberately or via configuration error), `targetProcesses` is built without the name-based filter, and `KillProcessTree` then calls `process.Kill()` (line 267) on each match without re-checking the name.
`KillProcessTree` only checks:
```csharp
if (protectedPids.Contains(process.Id)) return;
```
which only protects PIDs in the current ancestor chain — it does not protect, e.g., the dozens of unrelated svchost instances on the system.
The actual kill of System (PID 4) and other kernel-protected processes will fail at the Win32 layer, but the attempt is still made and the function will succeed in killing user-mode services that happen to share the name (e.g., killing all `svchost.exe` instances kills most of the OS).
Suggested fix:
Apply the safelist as a third filter in `KillProcessTreeAndParents(string)`, mirroring the guard in `KillProcessesUsingFile`:
```csharp
// Reject critical system process names up-front, before any process is killed
if (CriticalSystemProcesses.Contains(processName))
{
Logger.Warn($"Refused to kill critical system process by name: '{processName}'.");
return false;
}
```
Place this immediately after the `.exe` normalization at line 175. The PID-based overload `KillProcessTreeAndParents(int pid)` should also resolve the process name and apply the same check before walking the tree.
Severity: Warning
File: src/Servy.Core/Helpers/ProcessKiller.cs:167-222
Code:
```csharp
public bool KillProcessTreeAndParents(string processName, bool killParents = true)
{
...
var protectedPids = GetAncestorPids();
var byParent = BuildParentChildMapNative();
}
```
Explanation:
The class maintains `CriticalSystemProcesses` (line 28) — a hardcoded safelist of "never kill" Windows process names (`csrss`, `lsass`, `wininit`, `services`, `svchost`, `explorer`, `MsMpEng`, ...). That safelist is consulted in:
But `KillProcessTreeAndParents(string processName)` does not consult it. If a caller passes `"svchost"` or `"explorer"` (deliberately or via configuration error), `targetProcesses` is built without the name-based filter, and `KillProcessTree` then calls `process.Kill()` (line 267) on each match without re-checking the name.
`KillProcessTree` only checks:
```csharp
if (protectedPids.Contains(process.Id)) return;
```
which only protects PIDs in the current ancestor chain — it does not protect, e.g., the dozens of unrelated svchost instances on the system.
The actual kill of System (PID 4) and other kernel-protected processes will fail at the Win32 layer, but the attempt is still made and the function will succeed in killing user-mode services that happen to share the name (e.g., killing all `svchost.exe` instances kills most of the OS).
Suggested fix:
Apply the safelist as a third filter in `KillProcessTreeAndParents(string)`, mirroring the guard in `KillProcessesUsingFile`:
```csharp
// Reject critical system process names up-front, before any process is killed
if (CriticalSystemProcesses.Contains(processName))
{
Logger.Warn($"Refused to kill critical system process by name: '{processName}'.");
return false;
}
```
Place this immediately after the `.exe` normalization at line 175. The PID-based overload `KillProcessTreeAndParents(int pid)` should also resolve the process name and apply the same check before walking the tree.