

環境 ubuntu  
clionにてrun

[code]

    #include<stdio.h>
    #include<stdlib.h>
    
    int main(void){
    
        char *old_string,*new_string;
        old_string=malloc(sizeof(char));
        int i;
    
        for(i=0;i<10000;i++){
            old_string[i]=i%10;
    
            //new_string=realloc(old_string,i+2);
            //old_string=new_string;
            printf("インデックス：%d 中身：%d\n",i,old_string[i]);
        }
        free(old_string);
    
        return 0;
    }
    
[/code]

結果

[code]

    インデックス：1 中身：1
    インデックス：2 中身：2
    インデックス：3 中身：3
    インデックス：4 中身：4
    インデックス：5 中身：5
    インデックス：6 中身：6
    インデックス：7 中身：7
    ・・・
    インデックス:9999 中身:9
    
[/code]

最初はコメントアウトをしているとこを残して、malloc()とrealloc()による配列領域拡大を試してみようと思いました。実際うまくいきました。

しかし、コメントアウトをして、つまりサイズ１の領域確保のままでも結果が同じになってしまいます。

ガバガバすぎると思うんですけど、C言語の仕様なんでしょうか？

さすがに1Gぐらいまわせば何かにぶつかりそうですけど


CC BY-SA 4.0
2021-08-15T22:29:24.713
80831
2021-08-17T01:37:48.163
2021-08-16T13:12:22.240
40961
40961
question
c
malloc(sizeof(char))→10000要素の配列を作れてしまうのはなぜですか？


掲示のソースコードを `heap_overflow.c` として、gcc の `-fsanitize=address`
オプションを付けてコンパイル・実行してみます。

**gcc(1)**

> **-fsanitize=address**
>
> Enable AddressSanitizer, a fast memory error detector. Memory access
> instructions are instrumented to detect out-of-bounds and use-after-free
> bugs. The option enables `-fsanitize-address-use-after-scope`. See
> `<https://github.com/google/sanitizers/wiki/AddressSanitizer>` for more
> details. The run-time behavior can be influenced using the `ASAN_OPTIONS`
> environment variable. When set to `"help=1"`, the available options are
> shown at startup of the instrumented program. See
> `<https://github.com/google/sanitizers/wiki/AddressSanitizerFlags#run-time-
> flag>` for a list of supported options. The option cannot be combined with
> `-fsanitize=thread`.

以下の実行結果から、実際には heap buffer overflow が発生している事が判ります。

[code]

    $ gcc -std=gnu2x -fsanitize=address -Wall -g heap_overflow.c -o heap_overflow && ./heap_overflow
    
    インデックス：0 中身：0
    =================================================================
    ==118931==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000011 at pc 0x563844e782bc bp 0x7ffdd177b0b0 sp 0x7ffdd177b0a0
    WRITE of size 1 at 0x602000000011 thread T0
        #0 0x563844e782bb in main heap_overflow.c:12
        #1 0x7ff235322564 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x28564)
        #2 0x563844e7816d in _start (heap_overflow+0x116d)
    
    0x602000000011 is located 0 bytes to the right of 1-byte region [0x602000000010,0x602000000011)
    allocated by thread T0 here:
        #0 0x7ff23559ac47 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:145
        #1 0x563844e7823e in main heap_overflow.c:7
        #2 0x7ff235322564 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x28564)
    
    SUMMARY: AddressSanitizer: heap-buffer-overflow heap_overflow.c:12 in main
    Shadow bytes around the buggy address:
      0x0c047fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    =>0x0c047fff8000: fa fa[01]fa fa fa fa fa fa fa fa fa fa fa fa fa
      0x0c047fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
      0x0c047fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
      0x0c047fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
      0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
      0x0c047fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
    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
    ==118931==ABORTING
    
[/code]


CC BY-SA 4.0
2021-08-15T23:18:33.913
80832
2021-08-15T23:18:33.913
47127
80831
answer


> ガバガバすぎると思うんですけど、  
>  C言語の仕様なんでしょうか？

はい、境界を越えないように制御するのはプログラマの責任です。  
その上で、C言語には既定では境界チェック機能がありません。  
metropolisさんが回答されているように、追加のチェック機能を盛り込むことも可能です。  
そのほかの方法として、手元のVisual
C++ですとコード分析機能を使うと次のように警告が出るため、実行前に問題を把握することができます。もちろん、metropolisさんが紹介している`-fsanitize=address`も使えるため、実行時に検出することも可能です。

[code]

    warning C6200: Index '9999' is out of valid index range '0' to '0' for non-stack buffer 'old_string'.
    
[/code]

> さすがに1Gぐらいまわせば何かにぶつかりそうですけど

[`malloc`](https://linuxjm.osdn.jp/html/LDP_man-
pages/man3/malloc.3.html)に関しては、[`brk`](https://linuxjm.osdn.jp/html/LDP_man-
pages/man2/brk.2.html)でメモリブロックを取得し、その中を適切に切り分けたものを返す仕様となっています。そのため、`brk`で確保したサイズまではアクセスできてしまいます。それ以上のサイズにアクセスすると、アクセス違反でプロセスが停止するはずです。  
現状でも`malloc`の管理領域を壊している可能性があるため、次の`malloc` / `realloc` / `free`
等が正常に動作するとは限りません。


CC BY-SA 4.0
2021-08-15T23:28:20.147
80833
2021-08-17T01:37:48.163
2021-08-17T01:37:48.163
4236
4236
80831
answer