コース一覧
    C言語中級 文字列・構造体
    バッファオーバーフロー体験

    C言語中級 文字列・構造体

    C言語の文字列と構造体を学び、複数のデータを整理して扱うコースです。

    1
    文字列処理の実践
    01. strlenを自作する15分
    02. strcpyを自作する15分
    03. バッファオーバーフロー体験15分
    04. 文字列を分解する15分
    05. 動的な文字列15分
    06. つくる 名前入力の安全化15分
    07. 第3章クイズ10分
    2
    構造体とポインタ
    01. 構造体ポインタと->15分
    02. 構造体の動的確保15分
    03. 動的な構造体配列15分
    04. 関数ポインタ15分
    05. qsortで並べ替える15分
    06. つくる 動的Student完成15分
    07. 第4章クイズ10分

    バッファオーバーフロー体験

    あふれるとは何が起きることか

    前回書いた my_strcpy は、写す先の広さを見ていませんでした。標準の strcpy も同じです。写す元の '\0' が出るまで書き続けます。

    c

    char name[9];
    strcpy(name, "Yamamotokurosawa");   /* 17バイト必要なのに9バイトしかない */

    name は9バイトぶんしか用意されていないのに、17バイト書き込まれます。はみ出した8バイトは、name の隣に置かれていた何かの上に乗ります。第2章で見たとおり、ローカル変数はスタックの上に順番に並んでいます。隣にあるのは別の変数かもしれないし、関数から戻る先を覚えている場所かもしれません。

    これが バッファオーバーフロー です。厄介なのは次の点です。

    • その場では落ちないことがある。壊した場所が使われるまで症状が出ない
    • 症状が出たときには、原因の行はとっくに通り過ぎている
    • 入力の長さ次第で、攻撃者が「戻る先」を狙って書き換えられる

    Cで報告される深刻な脆弱性の多くは、元をたどるとこの形です。落ちるかどうかは環境まかせで、確実に壊れるとすら言えません。だから今回の課題では、わざとあふれさせるプログラムは書きません。壊れ方は上の説明で押さえて、あふれない書き方を手に入れるほうに時間を使います。

    器の広さを関数に教える

    安全にするための考え方は1つだけです。写す関数に、写す先が何バイトあるかを一緒に渡します。

    c

    int safe_copy(char *dest, int size, const char *src) {
        int i = 0;
        while (i + 1 < size && src[i] != '\0') {
            dest[i] = src[i];
            i++;
        }
        dest[i] = '\0';
        if (src[i] != '\0') {
            return 1;
        }
        return 0;
    }

    条件が i < size ではなく i + 1 < size なのは、最後の1バイトを終端のために空けておくためです。ここを間違えると、器いっぱいに文字を詰めて終端が入らない、という一番たちの悪いバグになります。

    ループを抜けたあと src[i] を見れば、写しきれたかどうかが分かります。まだ文字が残っていれば切り詰めたということなので、呼び出し側に知らせます。黙って切るだけだと、名前が途中で切れていることに誰も気付けません。

    strncpy という標準関数もありますが、写す元が長いときに終端を置いてくれないという落とし穴があります。名前が似ているだけで安全版ではないので、strncpy を使うときも自分で終端を置く必要があります。

    では、入力がどれだけ長くても壊れない受け取り方を書いてみましょう。

    要件

    1. safe_copy(char *dest, int size, const char *src) を自分で書く
    2. 写す先の広さを超えて書き込まない。終端のヌル文字は必ず置く
    3. 切り詰めたら 1、全部入ったら 0 を返す
    4. 1行目に写した結果の名前を表示する
    5. 2行目に、切り詰めた場合は 切り詰めました、入りきった場合は そのまま入りました と表示する

    ヒント

    while の条件を i + 1 < size にすると、最後の1バイトが終端用に残ります

    ループを抜けたあとに src[i] がまだ '\0' でなければ、切り詰めたということです

    name は9バイトなので入る文字は8文字までです

    生田 陸人
    監修生田 陸人
    ゆめさくエンジニア / 現役ソフトウェアエンジニア監修者プロフィールを見る →
    編集 ゆめさく編集部·公開 2026/08/07

    関連レッスン

    • 文字列を分解する

      区切り文字で分ける処理を書けるようになります。

    • 動的な文字列

      長さに合わせて必要なぶんだけ確保できるようになります。

    • つくる 名前入力の安全化

      入力処理を、あふれない形に全面的に書き直せるようになります。

    • 第3章クイズ

      文字列の内部構造と安全な扱い方の理解を確認します。

    このレッスンに出てくる用語

    意味があいまいなまま進んだ語は、ここから読み直せます。

    • 変数データに名前をつけて参照する仕組み
    • スタック後入れ先出し(LIFO)のデータ構造
    • 関数処理に名前を付けて再利用できる単位
    • ループ繰り返し処理。矢印で戻すか専用記号で示す
    main.c
    エディタを読み込んでいます

    メモ

    バッファオーバーフロー体験

    ⌘S で保存