Merge nucleic/lucid-river-toad-6efj into dev
This commit is contained in:
@@ -334,6 +334,7 @@ internal static class InternalComProbe
|
||||
Console.WriteLine(" → a re-adopted session can be handed to "
|
||||
+ "Session.FromAbi() and driven by the EXISTING compat facade code.");
|
||||
Console.WriteLine(" → D13's internal arm shrinks to: find, then hand off.");
|
||||
ProbeFromAbiOwnership(projected);
|
||||
Marshal.Release(projected);
|
||||
}
|
||||
else
|
||||
@@ -352,6 +353,67 @@ internal static class InternalComProbe
|
||||
|
||||
private static string? sessionName;
|
||||
|
||||
/// <summary>
|
||||
/// Does <c>Session.FromAbi(ptr)</c> take a reference, or borrow the caller's?
|
||||
///
|
||||
/// This is not a detail — it is the difference between a leak and a use-after-free, and the
|
||||
/// facade will call it on every reattach. `QueryInterface` already handed us one reference;
|
||||
/// if `FromAbi` AddRefs as well we must `Release` ours, and if it merely wraps the pointer we
|
||||
/// must NOT. Neither CsWinRT's docs nor the IDL say which.
|
||||
///
|
||||
/// Measured rather than assumed, by the only reliable means COM offers: `AddRef`/`Release`
|
||||
/// return the new count, so an AddRef/Release pair straddling the call reads the delta.
|
||||
/// The absolute numbers are meaningless (proxies keep their own counts); the DIFFERENCE is
|
||||
/// the answer.
|
||||
/// </summary>
|
||||
private static void ProbeFromAbiOwnership(IntPtr sessionPtr)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine(" FromAbi ownership (leak vs. use-after-free):");
|
||||
try
|
||||
{
|
||||
// Baseline: AddRef then Release, reading the count at the peak.
|
||||
var before = Marshal.AddRef(sessionPtr);
|
||||
Marshal.Release(sessionPtr);
|
||||
|
||||
var projection = global::Microsoft.WSL.Containers.Session.FromAbi(sessionPtr);
|
||||
if (projection is null)
|
||||
{
|
||||
Console.WriteLine(" FromAbi returned null — cannot judge");
|
||||
return;
|
||||
}
|
||||
|
||||
var after = Marshal.AddRef(sessionPtr);
|
||||
Marshal.Release(sessionPtr);
|
||||
|
||||
Console.WriteLine($" refcount {before} → {after} across FromAbi()");
|
||||
Console.WriteLine(after > before
|
||||
? " → FromAbi ADDS a reference. The facade must Release its QI reference "
|
||||
+ "after handing the pointer over, or every reattach leaks the session."
|
||||
: " → FromAbi BORROWS the pointer. The facade must NOT Release its QI "
|
||||
+ "reference — the projection depends on it staying alive.");
|
||||
|
||||
// Prove the projection is actually usable, not just constructed: GetImages() is the
|
||||
// cheapest read on Session and mutates nothing. If this works, a re-adopted session
|
||||
// really is a first-class compat Session.
|
||||
try
|
||||
{
|
||||
var images = projection.GetImages();
|
||||
Console.WriteLine($" projection.GetImages() = {images.Count} image(s) "
|
||||
+ "— the re-adopted session is FULLY USABLE through the compat projection");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine($" projection.GetImages() threw {e.GetType().Name}: "
|
||||
+ $"{e.Message} — it projects but does not work; investigate before relying on it");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine($" FromAbi threw {e.GetType().Name}: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<string> Wrap(string text)
|
||||
{
|
||||
var words = text.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
Reference in New Issue
Block a user