

書籍の「C言語本格入門: 基礎知識からコンピュータの本質まで」を参考にして、`strcpy` の動きを見ています。

書籍では8行目の`char string_2[16] = "";`を`char string_2[2] =
"";`に変更することで、以下のような実行結果になるとされていました。

**書籍の実行結果:**

[code]

    $ ./strcpy_test
    string_2 is = 
    Segmentation fault (core dumped)
    
[/code]

`string_1` よりもサイズの小さい `string_2`
にコピーしているのでバグが発生することを期待しているのですが、このようにコピーできた結果が返ってきます。

**実際の実行結果:**

[code]

    string_2 is = 
    string_2 is = hello, world
    sizeof(string_2) is 2
    
[/code]

実行環境は Window 11 (wsl2-Ubuntu) と Arch Linux で、コンパイラはどちらもgccを使用しました。

**ソースコード:**

[code]

    #include <stdio.h>
    #include <string.h>
    
    int
    main(int argc, char *argv[])
    {
        char string_1[16] = "hello, world";
        char string_2[2] = "";
    
        printf("string_2 is = %s\n", string_2);
        
        strcpy(string_2, string_1);
    
        printf("string_2 is = %s\n", string_2);
        printf("sizeof(string_2) is %ld\n",sizeof(string_2));
        
        return 0;
    }
    
[/code]


CC BY-SA 4.0
2023-05-11T15:35:52.593
94823
2023-05-13T07:41:39.950
2023-05-13T07:41:39.950
3060
54043
question
c
strcpyの動きについて、本書と結果が違う。


既にコメントが付いているように「エラーになることを期待している」が具体的にどのようなこと期待しているのか説明していただきたいです。

それとは別に`strcpy`の各種説明を読むとわかることがあります。

  * [`strcpy` Visual C++](https://learn.microsoft.com/ja-jp/cpp/c-runtime-library/reference/strcpy-wcscpy-mbscpy?view=msvc-170)
  * [`strcpy` Linux Manpage](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/strcpy.3.html)
  * [`strcpy` cppreference](https://ja.cppreference.com/w/c/string/byte/strcpy)

特にVisual C++では

> エラーを示す戻り値は予約されていません。

と説明されているように、`strcpy`がエラーを返すことはありません。

* * *

[Visual C++ でコード分析](https://learn.microsoft.com/ja-jp/cpp/code-
quality/?view=msvc-170)を行うと

[code]

    warning C6386: Buffer overrun while writing to 'string_2'.
    
[/code]

とコードに問題があることが指摘されます。他のコンパイラーにも同様に解析する機能があり指摘されるはずです。


CC BY-SA 4.0
2023-05-11T22:18:13.687
94824
2023-05-11T22:18:13.687
4236
94823
answer


言語仕様に基づいて説明します。 以下に記述する項目の番号は C99 での説明箇所ですが後の規格改定でも内容は変更されていません。

まず 7.21.1 の「文字列関数の規約」にはこうあります。

> If an array is accessed beyond the end of an object, the behavior is
> undefined.  
>  (配列へのアクセスがオブジェクトの終端を越える場合，その動作は，未定義とする。)

そして「未定義」という言葉の説明は 3.4.3 にあります。

> behavior, upon use of a nonportable or erroneous program construct or of
> erroneous data, for which this International Standard imposes no
> requirements  
>
> (可搬性がない若しくは正しくないプログラム構成要素を使用したときの動作，又は正しくないデータを使用したときの動作であり，この規格が何ら要求を課さないもの。)

つまり未定義とされていることをした場合は「何が起きても良い」ことを意味します。
もちろんデタラメな動作を引き起こすプログラムを書きたいわけではないでしょうから事実上は「起こしてはならないこと」です。

偶然にも他の用途で使われていないメモリであればなんともなく動作するかもしれませんし、他の変数に割り当てられている領域ならその変数の内容がデタラメになるかもしれませんし、保護された領域なら不正なアクセスとして検出されるかもしれません。
あるいは可能ならコンパイル時に検出されることもあるでしょう。


CC BY-SA 4.0
2023-05-12T01:27:36.023
94825
2023-05-12T01:27:36.023
3364
94823
answer


> 書籍は「C言語本格入門: 基礎知識からコンピュータの本質まで」です。

[サポートページ:C言語本格入門
〜基礎知識からコンピュータの本質まで:|技術評論社](https://gihyo.jp/book/2018/978-4-7741-9616-9/support)
からサンプルファイルをダウンロードして内容を確認してみると、コンパイルオプションには `-g -Wall` が指定されています。

**`sample/Chapter_09/Makefile.strcpy_test`**

[code]

    PROGRAM =       strcpy_test
    OBJS    =       strcpy_test.o
    SRCS    =       $(OBJS:%.o=%.c)
    CC      =       gcc
    CFLAGS  =       -g -Wall
    LDFLAGS =
    
[/code]

ところで gcc の場合、この様なプログラムに対して `buffer overflow`
を検出する機能があります。その機能は、最適化オプション(`-O`)を指定すると有効になります。具体的には、`__strcpy_chk()`
ルーチンが付加されます。

**`char * __strcpy_chk ()`**  
[gcc.gnu.org Git - gcc.git/blob -
gcc/testsuite/gcc.c-torture/execute/builtins/lib/chk.c](https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/testsuite/gcc.c-torture/execute/builtins/lib/chk.c;hb=560a3e35fe01c499bd5b1e95ddc4c3e958cf5abd#l174)

**`void __chk_fail ()`**  
[gcc.gnu.org Git - gcc.git/blob -
libssp/ssp.c](https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=libssp/ssp.c;hb=560a3e35fe01c499bd5b1e95ddc4c3e958cf5abd#l190)

[code]

    $ lsb_release -ir
    Distributor ID: Ubuntu
    Release:    23.04
    
    $ gcc --version
    gcc (Ubuntu 12.2.0-17ubuntu1) 12.2.0
    
    $ gcc -g -Wall -o strcpy_test strcpy_test.c
    $ ./strcpy_test
    string_2 is = 
    string_2 is = hello, world
    sizeof(string_2) is 2
    
    # optimize
    $ gcc -O -o strcpy_test strcpy_test.c
    $ ./strcpy_test
    string_2 is =
    *** buffer overflow detected ***: terminated
    Aborted (core dumped)
    
[/code]

**アセンブリコード上の違い**

[code]

    $ gcc -S -o strcpy_test.S strcpy_test.c
    $ gcc -O -S -o strcpy_test_optimize.S strcpy_test.c
    $ diff -u strcpy_test.S strcpy_test_optimize.S                                                                                                                            
    --- strcpy_test.S       2023-05-12 21:15:01.671658547 +0900
    +++ strcpy_test_optimize.S      2023-05-12 21:15:04.667704581 +0900
    @@ -1,6 +1,6 @@
            .file   "strcpy_test.c"
            .text
               :
    
    +       call    __strcpy_chk@PLT
               :
    
[/code]


CC BY-SA 4.0
2023-05-12T12:22:13.223
94831
2023-05-12T12:22:13.223
47127
94823
answer