Skip to content

ProjFS: only tolerate FilterAttach ACCESS_DENIED for unelevated callers#2051

Open
tyrielv wants to merge 1 commit into
microsoft:masterfrom
tyrielv:tyrielv/filter-attach-elevation-gate
Open

ProjFS: only tolerate FilterAttach ACCESS_DENIED for unelevated callers#2051
tyrielv wants to merge 1 commit into
microsoft:masterfrom
tyrielv:tyrielv/filter-attach-elevation-gate

Conversation

@tyrielv

@tyrielv tyrielv commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Narrows the ACCESS_DENIED tolerance added in #1980 so it only applies to unelevated callers of ProjFSFilter.TryAttachToVolume. This keeps the unelevated gvfs mount fix intact while ensuring the elevated GVFS.Service no longer silently swallows a genuine attach failure.

Background

#1980 made TryAttachToVolume treat ACCESS_DENIED from FilterAttach as success whenever the ProjFS service is running. Both callers share this method:

  • ProjFSFilter.IsReady() — runs in the (usually unelevated) gvfs mount process.
  • EnableAndAttachProjFSHandler.Run() — runs in GVFS.Service, which is elevated (LocalSystem).

The problem (error-swallowing risk)

FilterAttach requires SE_LOAD_DRIVER_PRIVILEGE, checked as a privilege gate at the API entry point:

  • An unelevated caller always gets ACCESS_DENIED, regardless of whether the filter is actually attached. When the service is running, the (elevated) service already attached the filter, so tolerating this is the correct fix — and is the whole point of ProjFS: tolerate ACCESS_DENIED on FilterAttach, remove dead bundled-driver code #1980.
  • An elevated caller (the service) holds the privilege, so ACCESS_DENIED is never the benign case there. It signals a real problem — AV/EDR lock, Group Policy restriction, or driver corruption — and swallowing it lets the mount proceed in a broken state with no actionable error.

Before #1980, the service called raw TryAttach and reported that failure. After #1980 it swallows it. This PR restores that safety for the elevated path.

The fix

Add a !WindowsPlatform.IsElevatedImplementation() guard to the tolerance condition. ACCESS_DENIED is a deterministic privilege gate, not a transient contention error, so distinguishing purely on elevation is precise:

This also aligns the code with the existing comment's stated intent ("when the caller lacks this privilege"), which the merged code never actually checked.

Explicitly out of scope

A bounded retry loop for "transient AV-scan contention" is not included: FilterAttach's ACCESS_DENIED is a deterministic privilege gate, not a sharing/contention error (which would surface as a different HRESULT). Retrying would add latency and still mask real failures.

Tests

  • GVFS.Platform.Windows, GVFS.Service, and GVFS.UnitTests build clean (0 warnings, 0 errors).
  • ProjFSFilterTests — 3/3 passed.

PR microsoft#1980 made TryAttachToVolume tolerate ACCESS_DENIED from FilterAttach
whenever the ProjFS service is running. Because both callers share this
method, the elevated GVFS.Service (running as LocalSystem) also began
swallowing ACCESS_DENIED — but the service holds SE_LOAD_DRIVER_PRIVILEGE,
so ACCESS_DENIED there is never the benign "unprivileged caller" case. It
signals a real problem (AV/EDR lock, Group Policy, or driver corruption),
and tolerating it lets a broken mount proceed instead of surfacing an
actionable error. Before microsoft#1980 the service called raw TryAttach and
reported that failure.

FilterAttach's ACCESS_DENIED is a privilege gate, not a transient
contention error: an unelevated caller always gets it (regardless of the
actual attach state), while an elevated caller only gets it when something
is genuinely wrong. Narrow the tolerance to unelevated callers so the
unelevated `gvfs mount` fix is preserved while the elevated service once
again surfaces genuine attach failures. This also aligns the code with the
existing comment's stated intent ("when the caller lacks this privilege").

Assisted-by: Claude Opus 4.8
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
@tyrielv tyrielv marked this pull request as ready for review July 9, 2026 20:54
@tyrielv tyrielv enabled auto-merge July 9, 2026 21:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant