Laden...

Wie funktioniert RuntimeHelpers.PrepareConstrainedRegions?

Erstellt von Palladin007 vor 6 Jahren Letzter Beitrag vor 6 Jahren 1.973 Views
Hinweis von MrSparkle vor 6 Jahren

Abgeteilt von Coding Styles Horror

Palladin007 Themenstarter:in
2.080 Beiträge seit 2012
vor 6 Jahren

'n Abend,

folgendes Stückchen Code hab ich im QuellCode von der .NET-SecureString-Klasse gefunden

try {
}
finally {
    if (m_encrypted) {
        // RtlEncryptMemory return an NTSTATUS
        int status = Win32Native.SystemFunction041(m_buffer, (uint)m_buffer.Length * 2, ProtectionScope);
        if (status < 0)
        { // non-negative numbers indicate success
#if FEATURE_CORECLR
            throw new CryptographicException(Win32Native.RtlNtStatusToDosError(status));
#else
            throw new CryptographicException(Win32Native.LsaNtStatusToWinError(status));
#endif
        }
        m_encrypted = false;
    }
}

Der Inhalt im finally interessiert nicht.
Spannender finde ich dagegen das try 😄

Hier kann man's nachlesen:

Microsoft Reference Source: SecureString.UnProtectMemory()

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

D
985 Beiträge seit 2014
vor 6 Jahren

Ist aber alles so wie dokumentiert: RuntimeHelpers.PrepareConstrainedRegions

Palladin007 Themenstarter:in
2.080 Beiträge seit 2012
vor 6 Jahren

Tatsache - wie dokumentiert
Ich versteh nur nicht, wozu das ganze gut sein soll 😠
Aber man gewöhnt sich dran, ich versteh immer weniger, je länger ich im .NET-Code wühle 😁

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

Palladin007 Themenstarter:in
2.080 Beiträge seit 2012
vor 6 Jahren

Ich hatte eigentlich nicht vor, ein Thread daraus zu machen 😄

Aber wenn mir jemand die Funktionsweise und vor allem den Sinn und warum man try-catch nicht normal nutzt, erklären kann, würde ich mich freuen ^^

Ich hab mitlerweile das gefunden:
https://stackoverflow.com/questions/1101147/code-demonstrating-the-importance-of-a-constrained-execution-region

Daraus wird zumindest klar, was das Ergebnis ist, aber nicht, wozu der Aufwand notwendig ist.
Den gezeigten Code könnte man ja auch so umschreiben, dass die Exception im try geworfen wird.

Oder soll das sowas wie eine Art Transaktion (ähnlich der Datenbank-Transaktion) ermöglichen, wenn der Inhalt zu komplex ist um das "normal" zu handlen?

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

3.003 Beiträge seit 2006
vor 6 Jahren

könnte man ja auch so umschreiben, dass die Exception im try geworfen wird.

Ja, könnte man, aber dann hätte man im Fall, dass eine Exception geworfen wird, die Erzeugung dieser Exception am Hacken, obwohl man die sowieso wegwerfen möchte. Man spart sich eine Propagation.

Eine exception, die im finally geworfen wird, sorgt dafür, dass eventuell im try geworfene Ausnahmen verworfen werden. Stattdessen wird die finally-Ausnahme hochgebubbelt.

C# Language Spec
*blätter*
S. 438 bei mir, Abschnitt 8.9.5. Lohnt sich, den ganzen Abschnitt mal gründlich zu lesen.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

Palladin007 Themenstarter:in
2.080 Beiträge seit 2012
vor 6 Jahren

Bei mir ist Abschnitt 8.9.5 auf Seite 253 😛

If the finally block throws another exception, processing of the current exception is terminated.

Wolltest Du auf das hindeuten?
Wenn ich die MyFn-Methode aus dem Beispiel aus dem Stackoverflow-Link so abändere:

static void MyFn()
{
    RuntimeHelpers.PrepareConstrainedRegions();
    try { throw new Exception("Test"); } // <-- Diese Exception wird "ignoriert"
    finally
    {
        cerWorked = false;
        Console.WriteLine(cerWorked); // <-- Dieser Aufruf wird "ignoriert"
        StackOverflow();
    }
}

dann wird die Test-Exception im try tatsächlich ignoriert.
Zusätzlich wird die Änderung an cerWorked zurück gespielt, sodass die Ausgabe true ist.
Interessant ist aber auch, dass das Console.WriteLine im finally ignoriert wird.

Verstehe ich es richtig, dass alles in so einer ConstrainedRegion sozusagen in einer Art Sandbox läuft?
Sobald fest steht, dass keine Exception fliegt, wird alles aus dieser Sandbox "raus geholt", die Variable wird geschrieben und das Console.WriteLine ausgeführt.

Aber was bringt das in der UnProtectMemory-Methode?
Da hat das try keinen Inhalt, es kann also nicht darum gehen, eine Exception aus dem try zu ignorieren.
Die m_encrypted-Variable wird bei einer Exception sowieso nicht geändert.
Bleibt nur noch der Aufruf von Win32Native.SystemFunction041, was nach meiner Recherche RtlEncryptMemory ist und den Speicherbereich entschlüsselt.

Ist der Sinn dahinter, dass der entschlüsselte Speicherbereich auf diese Weise möglichst schnell wieder verworfen wird?

PS:
Und warum ist das dann bei ProtectMemory genauso?
Es soll ja eigentlich so sein, dass der Speicherbereich möglichst kurz unverschlüsselt ist.
Dann wäre es doch eigentlich in meinem Interesse, dass die ProtectMemory-Methode den Speicherbereich verschlüsselt zurück lässt, auch wenn es einen Fehler gab?

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

D
985 Beiträge seit 2014
vor 6 Jahren

Es geht einfach darum einen Code-Block als atomar (atomic) zu markieren, der entweder ohne Fehler komplett durchläuft oder eben (bei jedweder Exception) überhaupt nicht.

Kann man sich wie eine Transaktion vorstellen, denn auch diese sind ja atomar.

Eigentlich ist das Beispiel auf der MSDN Seite doch gut erklärt.


public MySafeHandle AllocateHandle()
{
    // Allocate SafeHandle first to avoid failure later.
    MySafeHandle sh = new MySafeHandle();

    RuntimeHelpers.PrepareConstrainedRegions();
    try { }
    finally
    {
        MyStruct myStruct = new MyStruct();
        NativeAllocateHandle(ref myStruct); // <-- HIER wird es kritisch
        sh.SetHandle(myStruct.m_outputHandle);
    }

    return sh;
}

Wenn es zwischen dem NativeAllocateHandle und sh.SetHandle ein Thread.Abort oder eine OutOfMemoryException kommt, dann hat man ohne die Constrained Region ein Speicherleck.