0Pricing
C Academy · レッスン

不正なアクセス

範囲外アクセスと未初期化です。

「不正なアクセス」はCoddyKit上の無料C Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C Academyコースには全4レッスンが含まれています。

リークの先へ

リークはメモリを無駄にしますが、不正なアクセスはメモリを破壊します。memcheckは次のような問題を検出します。

  • バッファーの末尾を越えて読み書きする
  • freeの後にメモリを使用する
  • 未初期化の値を読み取る
  • 不正なポインターを通して読み書きする

これらは、最も危険で再現が難しいクラッシュを引き起こします。

範囲外への書き込み

このプログラムは5個のintを格納する領域を確保しますが、インデックス5、つまり6番目の要素に書き込みます。

インデックス5は末尾の1要素分先にあります。memcheckはこの行でサイズ4の不正な書き込みを報告します。

#include <stdlib.h>

int main(void) {
    int *a = malloc(5 * sizeof(int));
    a[5] = 99; /* valid indices are 0..4 */
    free(a);
    return 0;
}

エラーの読み方

レポートには、操作、サイズ、ブロックとの位置関係が示されます。

Invalid write of size 4
Address 0x... is 0 bytes after a block of size 20 alloc'd

「20バイトのブロックの0バイト後」は、アクセス先が20バイト(5個のint)の確保領域の直後だったことを示します。

範囲外からの読み取り

読み取りも検出されます。ここではループが1要素分余計に実行されます。

プログラムが運よく「動作」する可能性があっても、memcheckは最後の反復でサイズ4の不正な読み取りを検出します。

#include <stdlib.h>
#include <stdio.h>

int main(void) {
    int *a = calloc(3, sizeof(int));
    for (int i = 0; i <= 3; i++)   /* should be i < 3 */
        printf("%d\n", a[i]);
    free(a);
    return 0;
}

未初期化値

mallocはメモリをゼロ初期化しません。値を代入する前に使用すると、予測できない値になります。

その値が制御フローや出力に影響すると、memcheckは条件分岐または移動が未初期化値に依存していると報告します。

#include <stdlib.h>
#include <stdio.h>

int main(void) {
    int *p = malloc(sizeof(int));
    if (*p == 0)              /* *p was never set */
        printf("zero\n");
    free(p);
    return 0;
}

未初期化値の報告が遅れる理由

memcheckは、未初期化メモリを読み取った瞬間には警告しません。「定義済みかどうか」を追跡し、分岐、出力、システムコールなど、未定義の値が実際に意味を持つ時点でのみ報告します。

これにより、未初期化のバイト列を無害にコピーしただけの場合の誤警告を避けられます。

解放後の使用

解放した後のメモリに触れることは未定義動作です。memcheckはこれを正確に検出します。

サイズ4の不正な読み取りと、Address ... is 0 bytes inside a block of size 4 free'dという注記を報告し、freeが実行された場所まで示します。

#include <stdlib.h>
#include <stdio.h>

int main(void) {
    int *p = malloc(sizeof(int));
    *p = 7;
    free(p);
    printf("%d\n", *p);   /* read after free */
    return 0;
}

二重解放

同じポインターを2回解放すると、アロケーターの管理情報が破壊されます。

memcheckはInvalid free()を報告し、現在の解放と元の割り当ての両方を表示するため、誤りが明確になります。

#include <stdlib.h>

int main(void) {
    int *p = malloc(sizeof(int));
    free(p);
    free(p);   /* freed twice */
    return 0;
}

起点を追跡する

未初期化値のエラーには、--track-origins=yesを追加します。

するとmemcheckは、問題のある値が使用された場所だけでなく、作成された場所も報告するため、曖昧な警告を正確な修正箇所に変えられます。

valgrind --track-origins=yes ./prog

スタックとグローバル

memcheckはヒープエラーの検出を得意としています。一方、スタックやグローバル配列のオーバーフローに対する検出範囲は限定的です。

そのような場合は、AddressSanitizer(gcc -fsanitize=address)のほうが適していることがよくあります。2つのツールは互いに補完し合います。

境界を1つずれる問題: ヌル終端文字

よくあるヒープオーバーフローの1つが、文字列終端文字の入れ忘れです。strlenは'hello'に対して5を返しますが、末尾の'\0'を格納するには文字列に6バイト必要です。

strcpyが末尾の1バイトを終端の外側に書き込むと、memcheckはInvalid writeを報告します。

#include <stdlib.h>
#include <string.h>

int main(void) {
    char *s = malloc(strlen("hello")); /* needs +1 */
    strcpy(s, "hello");                 /* writes the '\0' past end */
    free(s);
    return 0;
}

クイックチェック

このコード断片に対してmemcheckが何を報告するか確認してください。

まとめ

これで、不正なアクセスによるバグを見つけられるようになりました。

  • 範囲外の読み取り/書き込みは、ブロックからのオフセットとともにInvalid read/writeとして表示されます
  • 未初期化値は、判断や出力に影響した時点で指摘されます
  • 解放後使用と二重解放は、両方の呼び出し箇所とともに検出されます
  • --track-origins=yesで未定義値の起点を特定し、スタックのバグにはASanを組み合わせます

次は、レポート全体の読み方です。

よくある質問

「不正なアクセス」レッスンは無料ですか?

はい。「不正なアクセス」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C Academyコースには全4レッスンが含まれています。

「不正なアクセス」で何を学びますか?

範囲外アクセスと未初期化です。 ブラウザで直接実行するハンズオンコードでC Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

C Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのC Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「不正なアクセス」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このC Academyレッスンでコードを書いて実行できますか?

はい。すべてのC Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Valgrind を使う理由
  2. リークの検出
  3. 不正なアクセス
  4. レポートの読み方
← C Academyに戻る