2014-03-28 89 views
18

的情況如下:在64位機器上發生32位整數溢出時會發生什麼?

  1. 32位整數溢出
  2. malloc的,這是期待 64位整數64位機器上使用該整數作爲輸入現在

其聲明是正確的(如果有的話)

說有符號的二進制整數111111110011011000001010110由於溢出,01000簡直是負值。這是一個實際存在的問題,因爲您可能需要分配比您在32位整數中描述的字節更多的字節。但是,它會以64位整數讀入。

  1. Malloc讀取此作爲一個64位整數,#是代表任何數據的原始整數之後存儲一個通配符位發現11111111001101100000101011001000################################。換句話說,它讀取的結果接近其最大值2^64,並嘗試分配一些字節。它失敗。
  2. Malloc將其作爲64位整數讀取,並將其轉換爲0000000000000000000000000000000011111111001101100000101011001000,這可能是因爲它是如何將它加載到寄存器中而留下大量零位的。它不會失敗,但會分配負的內存,就像讀取一個正的無符號值一樣。
  3. Malloc將其作爲一個64位整數讀取,轉換爲################################11111111001101100000101011001000,可能是因爲它是如何將它加載到寄存器中的,並且#代表通配符,表示之前在寄存器中的任何數據。取決於最後一個值,它不可預測地失敗。
  4. 該整數不會溢出,因爲即使它是32位,它仍然在一個64位寄存器,因此malloc工作正常。

我實際上測試過這一點,導致malloc失敗(這意味着要麼1或3是正確的)。我假設1是最合乎邏輯的答案。我也知道修復(使用size_t作爲輸入而不是int)。

我真的很想知道實際發生了什麼。出於某種原因,我沒有找到關於32位整數是如何在64位機器上實際處理這種意外「鑄造」的任何澄清。我甚至不確定它是否在註冊表中很重要。

+1

它取決於體系結構。有些體系結構本身並不具有32位數字,他們將所有操作視爲64位。無論是否有符號溢出都是未定義的行爲,無符號溢出都有很好的文檔記錄,並且如何補充兩種工作方式。 – Mgetz

+0

這取決於系統,但很可能它會像任何其他整數一樣溢出 –

+4

@ nikolaMM94但是,溢出有符號整數是未定義的行爲 - 根據具體情況可能會發生許多不同的事情。 – nos

回答

12

一旦整數溢出,使用它的值會導致未定義的行爲。根據標準,在溢出無效後使用int的結果的程序 - 基本上,關於其行爲的所有下注均已關閉。我們來看看計算機上會出現什麼情況,負數以二進制補碼形式存儲。在這種計算機上添加兩個大的32位整數時,如果發生溢出,則會得到負面結果。

但是,根據C++標準,malloc的參數類型,即size_t, is always unsigned。將負數轉換爲無符號數時,它會得到符號擴展(see this answer for a discussion and a reference to the standard),這意味着原始數據的最高有效位(對所有負數爲1)被設置爲無符號結果的前32位。

因此,您得到的是您的第三種情況的修改版本,不同之處在於不是「通配符位#」而是一直到頂部。結果是一個巨大的無符號數字(大約16 exbibytes左右);自然malloc未能分配那麼多的內存。

+1

你的推理基於假設溢出之後'int'是否定的,這不一定是這種情況。它可能是'1','malloc'然後會分配正確的'1'字節......它實際上是崩潰(和漏洞)的常見來源。 –

+0

@MatthieuM。 OP提到他已經測試過這個,而'malloc'沒有分配,使他相信它是「1或3」。這就是爲什麼我有理由相信他會得到一個負數。 – dasblinkenlight

+1

目前還不清楚OP測試的是什麼,僅僅因爲他曾*得到一個負數,一旦擴展產生了一個大數字並不意味着它總是會出現這種情況。 –

18

推理的問題在於,它始於假定整數溢出會導致確定性和可預測的操作。

這不幸的是,情況並非如此:未定義行爲意味着,任何事情都可能發生,且特別是的編譯器可以優化,就好像它可能永遠不會發生

因此,如果存在這種可能的溢出,幾乎不可能預測編譯器會產生什麼樣的程序。

  • 一種可能的輸出是編譯器elides分配,因爲它不能發生
  • 一種可能的輸出是所得到的值是0擴展或符號擴展(取決於是否它已知是正的或不)並解釋爲一個無符號整數。你可能會從0什麼size_t(-1),因此可以分配要麼太少或太多的內存,甚至無法分配,...
  • ...

未定義行爲=>所有的賭注都關閉

+0

感謝您提供可能的行爲實例,但實際上可能有些可能,而不是談論「鼻魔」和「格式化硬盤」等等。 –

+0

@凱爾,鼻子惡魔肯定是虛構的,但這種錯誤可能會格式化硬盤,或者如果導彈發生在導彈控制系統上,就會發射導彈! (malloc'ing沒有足夠的字節,然後寫入超越)。 –

+0

@MattMcNabb當然,作爲對具有未定義行爲的代碼的「合理」編譯器響應之一的副作用。但是沒有人會編寫一個(主流)編譯器,在看到一個具有未定義行爲的語句時,會插入代碼*設計*以造成損害,因爲這樣做在技術上會根據標準被允許。因此,我認爲,考慮編譯器實際可能產生的代碼類型是很有價值的;然而,我發現人們經常回避這些對話,並且評論大意是因爲任何代碼都符合要求,這並不重要。 –

3

所以,如果我們有一個特定的代碼示例,一個特定的編譯器和平臺,我們可以確定編譯器正在做什麼。 Deep C採取了哪種方法,但即使如此,它可能不是完全可預測的,這是未定義行爲的標誌,推廣關於未定義行爲不是一個好主意。

我們只需要看看gcc文檔的建議,看看它可以得到多麼混亂。該文件提供了關於integer overflow一些好的建議,它說:

實踐中,許多可移植的C程序假定有符號整數溢出環繞可靠使用兩個碼算術運算。然而,C標準認爲程序行爲在溢出時是未定義的,在少數情況下,C程序不適用於某些現代實現,因爲它們的溢出不會像作者期望的那樣迴繞。

,並在次節 Practical Advice for Signed Overflow Issues說:

理想情況下,最安全的方法是完全避免符號整數溢出[...]

在這一天結束。它是未定義的行爲,因此在一般情況下是不可預知的,但在gcc的情況下,在其實現定義的部分Integer上表示整數溢出迴繞:

對於轉換爲寬度爲N的類型,該值以模2^N減少爲在該類型的範圍內;沒有信號提出。

,但他們對整數溢出的意見,他們解釋如何optimization can cause problems with wraparound

編譯器有時生成代碼,並與環繞整數運算不兼容。

所以這很快變得複雜。

+0

編譯器可以假定不會發生未定義的行爲,因此沒有32位整數溢出。因此,它可以生成任何代碼,只要沒有溢出就會生成正確的結果。一種可能性是將x,y都轉換爲帶符號的64位,並將其加爲64位,然後解釋爲無符號。這是正確的,只要沒有32位溢出,所以它是合法的。當然,不同的代碼給出不同的結果也是合法的。 – gnasher729

+0

@ gnasher729這基本上就是我說的 –

相關問題