Skip to content

Match strong-name identity when resolving PSES dependencies#2303

Draft
andyleejordan wants to merge 1 commit into
mainfrom
andyleejordan/alc-isolation-investigation
Draft

Match strong-name identity when resolving PSES dependencies#2303
andyleejordan wants to merge 1 commit into
mainfrom
andyleejordan/alc-isolation-investigation

Conversation

@andyleejordan

Copy link
Copy Markdown
Member

Summary

A focused trial of tightening how PsesLoadContext decides whether a candidate assembly can satisfy a dependency request. Patrick and I have suspected for a while that our version-only matching is inadequate; this proposes we trial requiring the full strong-name identity to match.

Problem

On .NET Core, PsesLoadContext.IsSatisfyingAssembly gates whether a DLL in $PSHOME or our bundled Common directory can satisfy a dependency request, using only:

  • the simple name (case-insensitive), and
  • candidate.Version >= required.Version.

That ignores the rest of the assembly identity. A same-named assembly with a different public key token (i.e. a genuinely different assembly) was treated as a drop-in replacement, and the mismatch only surfaced later as a FileLoadException/TypeLoadException at bind time instead of being declined up front.

Change

Also require:

  • Public key token — if the reference is strong-named, the candidate's token must match exactly; a non-strong-named reference imposes no token requirement.
  • Culture — must match, so we never substitute a satellite resource assembly for the neutral one (or vice versa).

The pure comparison moved into an internal static overload taking two AssemblyNames so it can be unit-tested without DLLs on disk.

Why this is safe to trial

  • The check can only return false in more cases, and only for assemblies that could not have satisfied the reference anyway.
  • On a mismatch we decline to short-circuit and fall through to the default load context's own (laxer) resolution — equivalent to returning "not mine".
  • Measured against a current build, no presently-bundled dependency changes resolution under the new rules (zero token mismatches across Common vs $PSHOME), so today this is purely added protection.

Tests

PsesLoadContextTests (net8.0 / CoreCLR only, since the Hosting assembly is .NET Core only) covers exact match, newer/older version, name case-insensitivity, differing public key token, strong-named-vs-unsigned, no-required-token, and culture match/mismatch. All 10 pass; net462 still compiles (reference and tests are guarded).

Context / scope

Part of the broader ALC isolation investigation behind the recurring "Assembly with same name is already loaded" / "Could not load file or assembly" reports. This is the resolver-correctness piece only — it does not by itself address the feature-side eager-loading (completion / Get-Help importing user modules) or the Windows PowerShell "no ALC at all" class of issues.

Open questions

  • Whether to also tighten the Version >= rule (e.g. require major-version compatibility) — deliberately left as-is here.
  • Whether the trial framing is enough to merge as-is, or if we'd rather gate it.

🤖 Drafted by Copilot (Claude Opus 4.8) for @andyleejordan to review and edit before merging.

`PsesLoadContext.IsSatisfyingAssembly` decided whether a candidate DLL in
`$PSHOME` or our bundled `Common` directory could satisfy a dependency
request using only the simple name and `Version >=`. That ignores the rest
of the assembly identity, so a same-named assembly with a different public
key token (i.e. a genuinely different assembly) was treated as a drop-in
replacement. When the runtime then bound against it, the mismatch surfaced
later as a `FileLoadException`/`TypeLoadException` rather than being declined
up front. Patrick and I had suspected for a while that the version-only
matching was inadequate, so this is a focused trial of tightening it.

We now also require the public key token and culture to match:

- If the requested reference is strong-named, the candidate's public key
  token must match exactly; a non-strong-named reference imposes no token
  requirement.
- The culture must match, so we never substitute a satellite resource
  assembly for the neutral one (or vice versa).

The check can only return `false` in more cases than before, and only for
assemblies that could not have satisfied the reference anyway. On a token
mismatch we now decline to short-circuit and fall through to the default
load context's own (laxer) resolution instead of forcing a copy that fails
at load. Measured against a current build, no presently-bundled dependency
changes resolution under the new rules, so this is purely added protection.

I pulled the pure comparison into an `internal` overload taking two
`AssemblyName`s and added `PsesLoadContextTests` covering the version, name,
public key token, and culture cases. The Hosting assembly (and thus
`PsesLoadContext`) is .NET Core only, so the project reference and tests are
guarded to `net8.0`/`CoreCLR`.

Drafted by Copilot (Claude Opus 4.8).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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