15

【茶包射手日記】怪異的 .NET 數學運算溢位錯誤

 3 years ago
source link: https://blog.darkthread.net/blog/weird-arithmetic-overflow/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
【茶包射手日記】怪異的 .NET 數學運算溢位錯誤-黑暗執行緒

同事報案,某支提供檔案下載的 ASP.NET 程式,用瀏覽器可正常開啟,但有一支用 HttpWebRequest 存取它的 .NET 程式,原本可以執行,現在則出現 Arithmetic operation resulted in an overflow./數學運算導致溢位,由於下載程式裡幾乎沒什麼加減乘除計算,出現溢位錯誤讓人有點迷惑。

加入 Log 蒐集資訊,查出爆炸點在 byte[] b = new byte[response.ContentLength],進一步印出 ContentLength 找到原因,ContentLength = -1! (下圖是用 LINQPad 重現茶包的現場模擬)

由此我學到一個冷知識 - 在 C# new byte[-1] 的錯誤訊息不是陣列大小不可為負數,而是Arithmetic operation resulted in an overflow./數學運算導致溢位。

回頭追 ASPX 端,這才得知前陣子程式有修改,被改成類似以下寫法:

<%@ Page Language="C#"%>
<script runat="server">
        void Page_Load(object sender, EventArgs e)
    {
        Response.Clear();
        Response.Buffer = true;
        Response.ContentType = "image/png";
        Response.BinaryWrite(SomeFuncToGetByteArray());
        Response.Flush();
        Response.End();
    }
</script>

依據經驗,推測 Response.Flush() 是禍首。實測拿掉 Reponse.Buffer = true 及 Response.Flush() 後,HttpWebRequest 程式即告正常。

來解說發生了什麼事。當程式呼叫 Response.Flush(),ASP.NET 將改以分塊傳輸編碼(Chunked Transfer Encoding)形式傳送內容,Reponse Header 會宣告 Transfer-Encoding: chunked,內容則拆成一到多個區塊分次傳送,最後會附加一個大小為 0 的區塊作為結束。每個區塊的組成為:16 進位內容長度(Bytes) + CRLF + 內容本身 + CRLF。以下用實例比較 Response.End() 與 Response.Flush() 的傳回結果:

AspFlush.aspx

<%@ Page Language="C#"%>
<script runat="server">
    void Page_Load(object sender, EventArgs e)
    {
        Response.ContentType = "text/plain";
        Response.Write("我達達的馬蹄是美麗的錯誤\n");
        Response.Flush();
        Response.Write("我不是歸人,\n");
        Response.Flush();
        Response.Write("是個過客");
        Response.End();
    }
</script>

AspEnd.aspx

<%@ Page Language="C#"%>
<script runat="server">
    void Page_Load(object sender, EventArgs e)
    {
        Response.ContentType = "text/plain";
        Response.Write("我達達的馬蹄是美麗的錯誤\n");
        Response.Write("我不是歸人,\n");
        Response.Write("是個過客");
        Response.End();
    }
</script>

上述兩支 ASPX 用瀏覽器檢視的結果相同,但 Response 結構有很大差異。Response.Flush() 時 Header 會有 Transfer-Encoding: chunked,沒有 Content-Length,接收端需解析區塊長度、組裝內容及判斷結束(換言之,不能單純用 GetResponseStream() 一次抓回);而單純 Response.End() 時,所有內容一次傳完,並會有 Content-Length:

最後,判定程式中為實驗加入的 Response.Flush() 並無必要性,將其還原後問題排除。

and has 1 comment

Comments

Post a comment

Comment
Name Captcha 92 - 70 =

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK