Почему этот код работает без ключевого слова unsafe?

В ответе на его собственный спорный вопрос, Mash показал, что вам не нужно ключевое слово "unsafe" для чтения и записи непосредственно в байты любого экземпляра объекта .NET. Вы можете объявить следующие типы:

   [StructLayout(LayoutKind.Explicit)]
   struct MemoryAccess
   {

      [FieldOffset(0)]
      public object Object;

      [FieldOffset(0)]
      public TopBytes Bytes;
   }

   class TopBytes
   {
      public byte b0;
      public byte b1;
      public byte b2;
      public byte b3;
      public byte b4;
      public byte b5;
      public byte b6;
      public byte b7;
      public byte b8;
      public byte b9;
      public byte b10;
      public byte b11;
      public byte b12;
      public byte b13;
      public byte b14;
      public byte b15;
   }

И тогда вы можете делать такие вещи, как изменение «неизменяемой» строки. Следующий код печатает «bar» на моей машине:

 string foo = "foo";
 MemoryAccess mem = new MemoryAccess();
 mem.Object = foo;
 mem.Bytes.b8 = (byte)'b';
 mem.Bytes.b10 = (byte)'a';
 mem.Bytes.b12 = (byte)'r';
 Console.WriteLine(foo);

Вы также можете вызвать AccessViolationException, повредив ссылки на объекты с помощью такая же техника.

Вопрос. Я думал, что (в чисто управляемом коде C#) Ключевое слово unsafe было необходимо, чтобы делать подобные вещи. Почему здесь не надо? Означает ли это, что чистый управляемый "безопасный" код вообще небезопасен?


person Wim Coenen    schedule 27.04.2009    source источник
comment
Спасибо, что изменили способ задать тот же вопрос. Предыдущая ветка была раздута.   -  person Mash    schedule 27.04.2009
comment
@Маш: Нет проблем. Надеюсь, это привлечет больше внимания к вашему первоначальному вопросу.   -  person Wim Coenen    schedule 27.04.2009
comment
@wcoenen: На самом деле это не важно, даже если бы я думал об этом - мой вопрос касается контента сообщества, и я ничего от него не зарабатываю. Поэтому единственно важным является позитивное обсуждение. И кажется, что ваш вопрос выглядит лучше :)   -  person Mash    schedule 27.04.2009


Ответы (3)


Хорошо, это противно... опасности использования союза. Это может сработать, но это не очень хорошая идея - думаю, я бы сравнил это с отражением (где вы можете делать большинство вещей). Мне было бы интересно посмотреть, работает ли это в среде с ограниченным доступом - если да, это может представлять большую проблему...


Я только что протестировал его без флага «Полное доверие», и среда выполнения отклоняет его:

Не удалось загрузить тип «MemoryAccess» из сборки «ConsoleApplication4, версия = 1.0.0.0, культура = нейтральная, PublicKeyToken = null», поскольку объекты перекрываются со смещением 0, а сборка должна быть проверяемой.

А чтобы иметь этот флаг, уже нужно высокое доверие - так можно уже и побольше гадостей делать. Строки — это немного другой случай, потому что они не являются обычными объектами .NET, но есть и другие примеры способов их изменения — однако подход «объединения» интересен. Для другого хакерского способа (с достаточным доверием):

string orig = "abc   ", copy = orig;
typeof(string).GetMethod("AppendInPlace",
    BindingFlags.NonPublic | BindingFlags.Instance,
    null, new Type[] { typeof(string), typeof(int) }, null)
    .Invoke(orig, new object[] { "def", 3 });
Console.WriteLine(copy); // note we didn't touch "copy", so we have
                         // mutated the same reference
person Marc Gravell    schedule 27.04.2009
comment
@Marc Марк: не могли бы вы рассказать о других методах изменения строк, которые вы имеете в виду, пожалуйста? - person Brann; 27.04.2009
comment
Этот код работает нормально без небезопасного флага. Но сначала требуется полное доверие и использование CAS для установки разрешений, которые не помогают (отказывая в вещах, которые выглядят как связанные). Не могли бы вы привести пример, который изменяет содержимое строки и не использует небезопасные блоки? - person Mash; 27.04.2009
comment
Отредактировано относительно того, является ли это небезопасным флагом или полным доверием - я изменил разные вещи, поэтому, возможно, потерял след... Повторное изменение содержимого строки - не пришло мне в голову. Очень некрасиво ;-p - person Marc Gravell; 27.04.2009
comment
О, вы, вероятно, могли бы использовать отражение (поскольку у вас есть полное доверие) для вызова AppendInPlace или аналогичного; добавлен пример. - person Marc Gravell; 27.04.2009
comment
Хороший пример AppendInPlace (требуется доступ к закрытым методам), но из исходного кода ясно, что AppendInPlace — это просто небезопасное расширение массива и не позволяет изменять неуправляемые части управляемых объектов или нарушать границы объекта. В любом случае, спасибо, например. - person Mash; 27.04.2009
comment
Причина, по которой это слово в «Полном доверии», заключается в том, что верификатор отключен в «Полном доверии». - person Dinis Cruz; 01.12.2011

Упс, я перепутал unsafe с fixed. Вот исправленная версия:

Причина, по которой пример кода не требует пометки ключевым словом unsafe, заключается в том, что он не содержит указателей (см. цитату ниже, почему это считается небезопасным). Вы совершенно правы: «безопасный» лучше было бы назвать «дружественным во время выполнения». Для получения дополнительной информации по этой теме я отсылаю вас к Дону Боксу и Крису Селлсу Essential .NET.

Чтобы процитировать MSDN,

В общеязыковой среде выполнения (CLR) небезопасный код называется непроверяемым кодом. Небезопасный код на C# не обязательно опасен; это просто код, безопасность которого не может быть проверена CLR. Поэтому CLR будет выполнять небезопасный код, только если он находится в полностью доверенной сборке. Если вы используете небезопасный код, вы несете ответственность за то, чтобы ваш код не представлял угрозы безопасности или ошибки указателя.

Разница между фиксированным и небезопасным заключается в том, что фиксированный не позволяет среде CLR перемещать элементы в памяти, так что объекты вне среды выполнения могут безопасно получить к ним доступ, в то время как небезопасный касается прямо противоположной проблемы: в то время как CLR может гарантировать правильное разрешение для dotnet, он не может сделать это для указателя. Вы, наверное, помните, что разные Microsoft говорили о том, что ссылка не является указателем, и именно поэтому они поднимают такую ​​шумиху по поводу тонкого различия.

person Peter Wone    schedule 27.04.2009
comment
Этот код действительно позволяет вам выйти за границы объекта и изменить любую часть доступной процессу памяти. Вы можете реконструировать блоки синхронизации и ввести дескриптор любых объектов, войти в данные GC. Что на самом деле небезопасно дает больше, чем? - person Mash; 27.04.2009
comment
Вот о чем мы говорим. В этом коде нет признаков того, что CLR не может проверить его безопасность (на самом деле не может). Каждый объект в .NET внутренне использует небезопасный код, но это не приводит к возможности создания кода, объявленного безопасным, который может получать доступ и изменять данные как небезопасные. - person Mash; 27.04.2009
comment
Здесь действительно есть что-то подозрительное. Код безопасен, но позволяет нам читать память, прилегающую к определенному объекту. Таким образом, вы можете читать данные, которые могут быть перемещены во время выполнения в любой момент, если вы не используете ключевое слово fixed. Это точно такая же проблема, что и указатели. - person Wim Coenen; 27.04.2009
comment
Интересно, что объединение ссылочных типов и типов значений запрещено компилятором, а не 2 ссылочных типа. Среда выполнения проверяет это где-то, когда вы не находитесь в режиме полного доверия, но все же вы получаете небезопасный доступ из явно безопасного кода. И это разные вещи — режим доверия и безопасный код. Небезопасный код, как я уже сказал, всегда используется из безопасного кода, но указано, что если ваша библиотека не использует небезопасный код (кроме внутренностей BCL) - она ​​безопасна по своей природе. И этот пример показывает, что это не так. - person Mash; 27.04.2009
comment
за исключением того, что верификатор отключен при полном доверии, что означает, что CLR ничего не проверяет. CLR полагается на компилятор для создания проверяемого/безопасного кода. - person Dinis Cruz; 01.12.2011

Вы все еще отказываетесь от «управляемого» бита. В основе лежит предположение, что если вы можете это сделать, значит, вы знаете, что делаете.

person CurtainDog    schedule 27.04.2009