亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

為什么我的程序沒有崩潰,當我寫過一個數組的結束?

為什么我的程序沒有崩潰,當我寫過一個數組的結束?

C++
海綿寶寶撒 2019-08-03 03:03:21
為什么我的程序沒有崩潰,當我寫過一個數組的結束?為什么下面的代碼在沒有崩潰@運行時的情況下工作呢?而且,大小完全取決于機器/平臺/編譯器!我甚至可以在一臺64位的機器上捐出多達200美元。如何在操作系統中檢測到主函數中的分割錯誤?int main(int argc, char* argv[]){     int arr[3];     arr[4] = 99;}這個緩沖空間從何而來?這是分配給進程的堆棧嗎?
查看完整描述

3 回答

?
寶慕林4294392

TA貢獻2021條經驗 獲得超8個贊

我不久前為教育寫的東西.

考慮以下c-程序:

int q[200];main(void) {
    int i;
    for(i=0;i<2000;i++) {
        q[i]=i;
    }}

編譯并執行它之后,生成一個核心轉儲:

$ gcc -ggdb3 segfault.c
$ ulimit -c unlimited
$ ./a.outSegmentation fault (core dumped)

現在,使用gdb執行死后分析:

$ gdb -q ./a.out coreProgram terminated with signal 11, Segmentation fault.[New process 7221]#0  0x080483b4 in main () at s.c:88       
q[i]=i;(gdb) p i
$1 = 1008(gdb)

嗯,當一個人在分配的200個項目之外寫東西時,程序沒有分段錯誤,而是當我=1008時它崩潰了,為什么?

輸入頁面。

在UNIX/Linux上可以通過多種方式確定頁面大小,一種方法是使用系統函數sysconf(),如下所示:

#include <stdio.h>#include <unistd.h> // sysconf(3)int main(void) {
    printf("The page size for this system is %ld bytes.\n",
            sysconf(_SC_PAGESIZE));

    return 0;}

它提供了輸出:

此系統的頁大小為4096字節。

或者可以使用命令行實用程序getconf,如下所示:

$ getconf PAGESIZE4096

死后

事實證明,分段故障不是發生在i=200,而是發生在i=1008時,讓我們找出原因。啟動gdb進行一些死后分析:

$gdb -q ./a.out coreCore was generated by `./a.out'.Program terminated with signal 11, Segmentation fault.[New process 4605]
#0  0x080483b4 in main () at seg.c:66           q[i]=i;(gdb) p i
$1 = 1008(gdb) p &q
$2 = (int (*)[200]) 0x804a040(gdb) p &q[199]$3 = (int *) 0x804a35c

q結束于地址0x804a35c,或者更確切地說,Q[199]的最后一個字節位于該位置。頁面大小是我們前面看到的4096字節,機器的32位字大小給出了一個虛擬地址分解為一個20位的頁碼和一個12位偏移量。

q[]以虛擬頁碼結尾:

0x804a=32842偏移量:

0x35c=860,因此仍然存在:

4096-864=分配q[]的內存頁上剩下的3232個字節。這個空間可以容納:

3232/4=808整數,代碼將其視為在位置200至1008處包含q元素。

我們都知道這些元素不存在,編譯器也沒有抱怨,HW也沒有,因為我們對那個頁面有寫權限。只有當i=1008時,q[]才引用我們沒有寫權限的另一個頁面上的地址,虛擬內存HW才會檢測到這一點,并觸發一個分段錯誤。

整數以4個字節存儲,這意味著這個頁面包含808(3236/4)額外的假元素,這意味著從q[200]、q[201]到元素199+808=1007(q[1007])訪問這些元素仍然是完全合法的,而不會觸發SEG故障。當訪問q[1008]時,輸入一個權限不同的新頁面。




查看完整回答
反對 回復 2019-08-05
?
慕少森

TA貢獻2019條經驗 獲得超9個贊


關于局部變量緩沖區溢出崩潰的確切時間/位置取決于以下幾個因素:

  1. 調用函數時堆棧上的數據量,其中包含溢出的變量訪問。
  2. 總計寫入溢出變量/數組的數據量。

記住堆積如山向下..對象附近的堆棧指針開始進程執行。端部作為堆棧使用的內存。它不是從最后一個映射的單詞開始的,這是因為系統的初始化代碼可能決定在創建時將某種“啟動信息”傳遞給進程,并且經常在堆棧上這樣做。

那就是平日失敗模式-從包含溢出代碼的函數返回時出現崩潰。

如果共計寫入堆棧上的緩沖區的數據量大于以前使用的堆??臻g總量(由調用方/初始化代碼/其他變量使用),那么無論哪個內存訪問首先運行到堆棧的頂部(開始),都會發生崩潰。崩潰的地址就會越過一頁邊界-SIGSEGV由于要訪問堆棧頂部以外的內存,所以沒有映射。

如果這個總數現在小于堆棧中使用的部分的大小,那么它就會正常工作并崩潰。后來-實際上,在存儲堆棧上的地址的平臺上(x86/x64是這樣的),當從函數返回時。那是因為CPU指令ret實際上從堆棧(返回地址)獲取一個單詞,并將執行重定向到那里。如果該地址不包含預期的代碼位置,而是包含任何垃圾,則會發生異常并導致程序死亡。

為了說明這一點:何時main()調用時,堆??雌饋砣缦?在32位x86 UNIX程序上):

[ esp          ] <return addr to caller> (which exits/terminates process)

[ esp + 4      ] argc

[ esp + 8      ] argv

[ esp + 12     ] envp <third arg to main() on UNIX - environment variables>

[ ...          ]

[ ...          ] <other things - like actual strings in argv[], envp[]

[ END          ] PAGE_SIZE-aligned stack top - unmapped beyond

什么時候main()開始時,它將為各種用途分配堆棧上的空間,其中包括托管待溢出數組。這將使它看起來像:


[ esp          ] <current bottom end of stack>

[ ...          ] <possibly local vars of main()>

[ esp + X      ] arr[0]

[ esp + X + 4  ] arr[1]

[ esp + X + 8  ] arr[2]

[ esp + X + 12 ] <possibly other local vars of main()>

[ ...          ] <possibly other things (saved regs)>


[ old esp      ] <return addr to caller> (which exits/terminates process)

[ old esp + 4  ] argc

[ old esp + 8  ] argv

[ old esp + 12 ] envp <third arg to main() on UNIX - environment variables>

[ ...          ]

[ ...          ] <other things - like actual strings in argv[], envp[]

[ END          ] PAGE_SIZE-aligned stack top - unmapped beyond

這意味著你可以快樂地進入arr[2].


對于緩沖區溢出導致的不同崩潰的體驗,請嘗試如下:


#include <stdlib.h>

#include <stdio.h>


int main(int argc, char **argv)

{

    int i, arr[3];


    for (i = 0; i < atoi(argv[1]); i++)

        arr[i] = i;


    do {

        printf("argv[%d] = %s\n", argc, argv[argc]);

    } while (--argc);


    return 0;

}

看看異類與堆棧結束后溢出緩沖區時相比,當緩沖區溢出一點(例如,10位)時,就會發生崩潰。用不同的優化級別和不同的編譯器來嘗試它。很能說明問題,因為它顯示了這兩種行為的不當行為(不一定會全部打印出來)argv[]以及在不同的地方崩潰,甚至沒完沒了的循環(例如,編譯器放置i或argc到堆棧中,代碼在循環期間覆蓋它)。





查看完整回答
反對 回復 2019-08-05
  • 3 回答
  • 0 關注
  • 427 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號