簡體   English   中英

gcc -O2 創建一個無限循環,可能是因為未定義的行為

[英]gcc -O2 creates an endless loop, probably because of undefined behaviour

我編寫了這個 C 代碼來解決代碼 13 2020 的出現。我知道,嘗試通過蠻力解決它可能不可行,但程序為示例輸入提供了正確答案。

如果我嘗試讓 gcc 優化代碼,它會使用 -O1 給出正確的結果,但會使用 -O2 創建一個無限循環。 經過所有研究,我的結論是我的代碼中存在未定義的行為,我想這與“找到”可能永遠不會高於 0 的概率有關,因此“時間”會溢出。

問題來了:有人知道如何修補這種未定義的行為嗎?

“-Wall -Wextra -pedantic”甚至不發出警告之類的。

我只是找不到解決方案。 例如,如果我將 while 循環的頭部更改為 (,found && time < 10000000000),這樣就不會發生溢出,當使用 -O2 編譯時,它會立即以 10000000003 的時間值中斷循環。 但仍然使用 -O1 給出正確的結果。

這是代碼,正確的結果是“1068781”:

#include <stdio.h>
int main()
{
    unsigned int busses[] = {7, 0, 13, 2, 59, 1, 31, 0, 19};
    unsigned int busses_used = 9;
    unsigned int i = 0;
    unsigned int found = 0;
    unsigned long long time = 0;
    unsigned int offset = 0;
    unsigned int increment = 7;
    while (!found) {
        time += increment;
        offset = 0;
        for (i = 0; i < busses_used; i++) {
            if ((time + offset) % busses[i] == 0) {
                found = 1;
                offset++;
            } else {
                found = 0;
                break;
            }
            offset += busses[++i];
        }
    }
    printf("Endtime: %lld\n", time);
    return 0;
}

編輯:感謝 KamilCuk 指出代碼正在越界訪問數組並教如何找出它正在這樣做。 該問題通過在“busses”數組的末尾添加另一個 0 來解決,因此也將“busses_used”設置為 10 而不是 9。

我看不懂代碼,也不縮進,但循環很奇怪。 反正:

“-Wall -Wextra -pedantic”甚至不發出警告之類的。

而且還有其他檢測UB的方法! 使用更多-fsanitize=*選項編譯代碼會導致:

+ gcc -Wall -Wextra -ggdb3 -fsanitize=address -fsanitize=undefined -fsanitize=pointer-compare -fsanitize=pointer-subtract -fsanitize-address-use-after-scope /tmp/1.c
/tmp/1.c:22:29: runtime error: index 9 out of bounds for type 'unsigned int [9]'
/tmp/1.c:22:29: runtime error: load of address 0x7ffc48c7a894 with insufficient space for an object of type 'unsigned int'
0x7ffc48c7a894: note: pointer points here
  13 00 00 00 60 60 00 00  00 00 00 00 00 00 00 00  01 00 00 00 00 00 00 00  08 aa c7 48 fc 7f 00 00
              ^ 
=================================================================
==73835==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffc48c7a894 at pc 0x55c28a5bf773 bp 0x7ffc48c7a800 sp 0x7ffc48c7a7f0
READ of size 4 at 0x7ffc48c7a894 thread T0
    #0 0x55c28a5bf772 in main /tmp/1.c:22
    #1 0x7f0060bd1151 in __libc_start_main (/usr/lib/libc.so.6+0x28151)
    #2 0x55c28a5bf12d in _start (/tmp/tmp.qv8ZsZofOJ.out+0x112d)

Address 0x7ffc48c7a894 is located in stack of thread T0 at offset 84 in frame
    #0 0x55c28a5bf208 in main /tmp/1.c:3

  This frame has 1 object(s):
    [48, 84) 'busses' (line 4) <== Memory access at offset 84 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /tmp/1.c:22 in main
Shadow bytes around the buggy address:
  0x1000091874c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000091874d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000091874e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000091874f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100009187500: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 f1 f1 00 00
=>0x100009187510: 00 00[04]f3 f3 f3 f3 f3 00 00 00 00 00 00 00 00
  0x100009187520: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100009187530: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100009187540: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100009187550: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100009187560: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==73835==ABORTING

快速檢查后,對busses的越界訪問發生在這里:

offset += busses[++i];

有人知道如何修補這種未定義的行為嗎?

不知道,但是編寫一個不會越界訪問數組的算法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM