Skip to content

ProcessHelper._lastPruneTime: TOCTOU race allows concurrent prune execution #332

@Christophe-Rogiers

Description

@Christophe-Rogiers

Description

In src/Servy.Core/Helpers/ProcessHelper.cs (lines 113–138):

private static DateTime _lastPruneTime = DateTime.MinValue;

private static void PruneDeadProcesses()
{
    if (DateTime.UtcNow - _lastPruneTime < PruneInterval) return;
    // ... prune logic ...
    _lastPruneTime = DateTime.UtcNow;
}

_lastPruneTime is a static field accessed from multiple threads (via RefreshServiceInternal which runs parallel tasks). The check-then-act pattern is a TOCTOU race: multiple threads can pass the interval check simultaneously and all execute the prune loop.

Additionally, DateTime is a 64-bit struct — on x86, reads/writes are not atomic (they require two 32-bit operations), making torn reads possible.

Severity

Info — redundant concurrent prune executions; torn reads possible on x86.

Suggested fix

Use Interlocked.Exchange with a long (ticks) instead of DateTime:

private static long _lastPruneTicks = 0;

private static void PruneDeadProcesses()
{
    long now = DateTime.UtcNow.Ticks;
    long last = Interlocked.Read(ref _lastPruneTicks);
    if (now - last < PruneInterval.Ticks) return;
    if (Interlocked.CompareExchange(ref _lastPruneTicks, now, last) != last) return;
    // ... prune logic (only one thread enters) ...
}

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions