3

私は非常に単純なプログラムを持っています。私の意図は、標準入力を標準出力にコピーすることです。ソースコードは次のとおりです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace LALA
{
    class LALA
    {
        static void Main()
        {
            int bufferSize = 40;
            Console.OpenStandardInput().CopyTo(Console.OpenStandardOutput(), bufferSize);
        }
    }
}

40に設定bufferSizeすると、どの入力に対しても「C o n」、「C on s」、または「C onsol e」などが表示されます。41 に設定bufferSizeすると、すべて問題ありません。

私の質問は、私が何か間違ったことをしているのですか?偶然に 41 を超える値が機能するのでしょうか?

明確にするために、これは asd と入力したときの出力です。

c:\some_path>ConsoleApplication2.exe
asd
C o n
4

1 に答える 1

1

サプライズ!これは内部バッファ オーバーフローのようです。私が見つけたものを共有しますが、まだ実際の証拠ではありません。

Stream クラスのメソッドのコードCopyToは次のとおりです: (リフレクターで取得)

public void CopyTo(Stream destination, int bufferSize)
{
    //bunch of non relevant validations...

    this.InternalCopyTo(destination, bufferSize);
}

コードInternalCopyToは次のとおりです。

private void InternalCopyTo(Stream destination, int bufferSize)
{
    byte[] array = new byte[bufferSize];
    int count;
    while ((count = this.Read(array, 0, array.Length)) != 0)
    {
        destination.Write(array, 0, count);
    }
}

コンソール ストリーム インスタンスの型は__ConsoleStream(System.IO の封印された内部クラス) で、そのReadメソッド コードは次のとおりです。

public override int Read([In] [Out] byte[] buffer, int offset, int count)
{
    //bunch of non relevant validations...

    int errorCode = 0;
    int num = __ConsoleStream.ReadFileNative(this._handle, buffer, offset, count, 0, out errorCode);
    if (num == -1)
    {
        __Error.WinIOError(errorCode, string.Empty);
    }
    return num;
}

最後ReadFileNativeに __ConsoleStream のコード:

private unsafe static int ReadFileNative(SafeFileHandle hFile, byte[] bytes, int offset, int count, int mustBeZero, out int errorCode)
{
    if (bytes.Length - offset < count)
    {
        throw new IndexOutOfRangeException(Environment.GetResourceString("IndexOutOfRange_IORaceCondition"));
    }
    if (bytes.Length == 0)
    {
        errorCode = 0;
        return 0;
    }
    __ConsoleStream.WaitForAvailableConsoleInput(hFile);
    int result;
    int num;
    fixed (byte* ptr = bytes)
    {
        num = __ConsoleStream.ReadFile(hFile, ptr + (IntPtr)offset / 1, count, out result, Win32Native.NULL);
    }
    if (num != 0)
    {
        errorCode = 0;
        return result;
    }
    errorCode = Marshal.GetLastWin32Error();
    if (errorCode == 109)
    {
        return 0;
    }
    return -1;
}

ReadFileメソッドは低レベルの呼び出しです。

[DllImport("kernel32.dll", SetLastError = true)]
private unsafe static extern int ReadFile(SafeFileHandle handle, byte* bytes, int numBytesToRead, out int numBytesRead, IntPtr mustBeZero);

この時点での私の仮定は、舞台裏で 40 バイトが内部データのどこかに「予約」されているため、バッファがそれ以下の場合、このコンソール アプリケーションの場合はプロセス名である予約データが表示されます。

もっと時間があれば、これを調査し続けて再現を試みます。このケースは非常に特殊です。両方のストリームが同じ「ファイル」を指しているため、読み取り中に書き込むことができるからです。

于 2012-12-23T22:54:58.127 に答える