Почему этот код работает без ключевого слова unsafe?
В ответе на свой собственный спорный вопрос, Mash показал, что вам не нужно "небезопасное" ключевое слово для чтения и записи непосредственно в байты любого экземпляра объекта .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;
}
И тогда вы можете делать что-то вроде изменения неизменяемой строки. Следующий код печатает "бар" на моей машине:
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, развращая ссылки на объекты той же техникой.
Вопрос: Я думал, что (в чистом управляемом коде С#) ключевое слово unsafe было необходимо для выполнения подобных действий. Почему здесь не нужно? Означает ли это, что чистый управляемый "безопасный" код вообще не безопасен?
Ответы
Ответ 1
Хорошо, это противно... опасность использования союза. Это может сработать, но это не очень хорошая идея. Думаю, я бы сравнил ее с размышлениями (где вы можете делать большинство вещей). Мне было бы интересно узнать, работает ли это в среде с ограниченным доступом - если это так, это может представлять большую проблему...
Я только что протестировал его без флага "Полный доверие", и среда выполнения отклонила его:
Не удалось загрузить тип 'MemoryAccess' из сборки 'ConsoleApplication4, Версия = 1.0.0.0, Культура = нейтральная, PublicKeyToken = null ', потому что объекты перекрываются со смещением 0 и сборка должна быть поддающейся проверке.
И чтобы иметь этот флаг, вам уже нужно высокое доверие - так что вы уже можете делать больше неприятных вещей. Строки - это немного другой случай, потому что они не являются нормальными объектами .NET, но есть и другие примеры способов их изменения. Однако подход "union" является интересным. Для другого хакерского способа (с достаточным доверием):
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
Ответ 2
Упс, я запутался unsafe
с помощью fixed
. Здесь скорректированная версия:
Причина, по которой код примера не требует тегирования с ключевым словом unsafe
, заключается в том, что он не содержит указателей (см. ниже цитату, почему это считается небезопасным). Вы совершенно правы: "безопасный" лучше назвать "дружественным во времени". Для получения дополнительной информации по этой теме я отсылаю вас к Don Box и Chris Sells Essential.NET.
Чтобы указать MSDN,
В среде общего языка (CLR) небезопасный код называется непроверяемый код. Небезопасный код в С# не обязательно опасен; это просто код, безопасность которого не может быть проверяется CLR. CLR будет поэтому выполняйте небезопасный код, если он находится в полностью доверенной сборке. Если вы используете небезопасный код, это ваш ответственность за код не вводит риски безопасности или ошибки указателя.
Разница между фиксированной и небезопасной заключается в том, что фиксированное останавливает CLR от перемещения вещей в памяти, так что вещи, находящиеся за пределами времени выполнения, могут безопасно обращаться к ним, в то время как небезопасные - это точно противоположная проблема: в то время как CLR может гарантировать правильное разрешение для ссылки на dotnet, оно не может сделать это для указателя. Вы можете вспомнить, как различные Microsoft продолжают рассказывать о том, как ссылка не является указателем, и именно поэтому они создают такую суету в отношении тонкого различия.
Ответ 3
Вы по-прежнему отказываетесь от "управляемого" бит. Существует основополагающее предположение, что если вы можете это сделать, тогда вы знаете, что делаете.