6

次のコードを検討してください。

using System;
using System.Runtime.InteropServices;

namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            const string test = "ABCDEF"; // Strings are immutable, right?
            char[] chars = new StringToChar{str=test}.chr;
            chars[0] = 'X';

            // On an x32 release or debug build or on an x64 debug build, 
            // the following prints "XBCDEF".
            // On an x64 release build, it prints "ABXDEF".
            // In both cases, we have changed the contents of 'test' without using
            // any 'unsafe' code...

            Console.WriteLine(test);
        }
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct StringToChar
    {
        [FieldOffset(0)]
        public string str;
        [FieldOffset(0)]
        public char[] chr;
    }
}

このコードを実行することで、例外が発生することなく文字列の内容を変更できます。そのために安全でないコードを宣言する必要はありませんでした。このコードは明らかに非常に危険です!

私の質問は単純にこれです:あなたは例外が上記のコードによってスローされるべきだと思いますか?

[編集1:他の人が私のためにこれを試した、そして何人かの人が異なる結果を得ることに注意してください-私がしていることの厄介さを考えるとそれほど驚くことではありません...;)]

[編集2:Windows 7Ultimate64ビットでVisualStudio2010を使用していることに注意してください]

[EDIT3:テスト文字列をconstにしました。これは、さらに危険なものにするためです!]

4

2 に答える 2

6

clr / src / vm / class.cppのSSCLI20ソースコード、MethodTableBuilder :: HandleExplicitLayoutは、いくつかの洞察を提供できます。これは非常に多くのコメントが付けられています。このコメントはルールを説明しています(読みやすくするために編集されています)。

// go through each field and look for invalid layout
// (note that we are more permissive than what Ecma allows. We only disallow 
// the minimum set necessary to close security holes.)
//
// This is what we implement:
//
// 1. Verify that every OREF is on a valid alignment
// 2. Verify that OREFs only overlap with other OREFs.
// 3. If an OREF does overlap with another OREF, the class is marked unverifiable.
// 4. If an overlap of any kind occurs, the class will be marked NotTightlyPacked (affects ValueType.Equals()).

ルール1は、参照割り当てがアトミックのままであることを保証します。ルール2は、実行したことを実行できる理由を示しています。オブジェクトタイプの参照は重複する可能性があります。ガベージコレクターを台無しにする値型の値との重複は許可されていません。ルール3は結果を述べており、タイプを検証不能にするだけです。

それ以外の場合は、安全でないキーワードなしで文字列を台無しにする唯一の方法ではありません。文字列を踏み鳴らす関数をピンボークするだけです。GCヒープまたはローダーヒープ(インターン文字列)の文字列コンテンツへのポインタを取得し、コピーは作成されません。これは検証不可能なコードでもあり、サンドボックスで実行している場合も同様に悪用できません。

ポイントを家に帰す:C#unsafeキーワードは、CLRが検証可能と見なすもの、つまり実際には安全なコードとはまったく関係がありません。ポインタまたはカスタム値型(固定)を使用して、露骨なケースを処理します。それがC#言語仕様のリークであるかどうかは議論の余地があります。Pinvokeがより明白なエッジケースです。オペレーティングシステムの機能をピンボークすることは、かなり安全です。一部のサードパーティCライブラリをピンボークすることはできません。

しかし、私は@fejに同意する必要があります。[FieldOffset]は「よろしいですか」という扱いを受けるべきでした。残念ながら、そのための構文はありません。確かに、これが実際に管理されたレイアウトに影響を与える必要がある理由はまだわかりません。この属性がマーシャリングされたレイアウトにのみ適用されることは、はるかに理にかなっています奇妙なことに、初期の頃は誰かがエースを持っていたのかもしれません。

于 2010-12-23T17:55:58.267 に答える
4

私の投票は、FieldOffsetを安全でないものにすることです。

于 2010-12-23T14:31:30.043 に答える