力試しでヘッダを書いてChatGPTとClaudeに破壊を試みてもらいました。
人生でQueueを書く機会って結構あると思うんですが、品質に関してはあんまり考えてなかったのでAIに聞けば脆弱性が見つかるんじゃないかと色々試してみました。試してみましたが見つかりませんでした。いかがでいしたか?
くコ:彡いか
Queueの本体
ヘッダーファイル:queue..h
#ifndef QUEUE_H #define QUEUE_H #include <stdint.h> #define DTYPE uint8_t /* キュー API */ DTYPE dequeue(void); void enqueue(DTYPE x); uint32_t is_empty(void); uint32_t is_full(void); void initialize_queue(void); #endif /* QUEUE_H */
メインファイル:queue.c
#include"queue.h" #include <stdio.h> #include <stdint.h> #include <string.h> #define LEN 256 static DTYPE ring_buffer[256]; static uint32_t head = 0; static uint32_t tail = 0; DTYPE dequeue(void){ if(is_empty()){ return NULL; } int tail_address = tail % LEN; if(tail > LEN && head > LEN){ tail = tail - LEN; head = head - LEN; } tail = tail + 1; return ring_buffer[tail_address]; } void initialize_queue(){ head = 0; tail = 0; } void enqueue(DTYPE x){ if(is_full()){ return; } int head_address = head % LEN; if(tail > LEN && head > LEN){ tail = tail - LEN; head = head - LEN; } head = head + 1; ring_buffer[head_address] = x; } uint32_t is_empty(){ if(head - tail <= 0){ return 1; }else{ return 0; } } uint32_t is_full(){ if(head - tail >= 255){ return 1; }else{ return 0; } }
ChatGPT
// test_queue.c #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <time.h> #include "queue.h" #define LEN 256 #define PASS "\033[32mPASS\033[0m" #define FAIL "\033[31mFAIL\033[0m" static void check_print(const char *name, int ok){ printf("%-35s : %s\n", name, ok ? PASS : FAIL); if(!ok){ fflush(stdout); } } /* 単純な理想キュー(検査用) */ typedef struct { int buf[LEN]; int head, tail; // head index of next push, tail index of next pop int count; } ideal_q; static void ideal_init(ideal_q *q){ q->head = q->tail = 0; q->count = 0; for(int i=0;i<LEN;i++) q->buf[i] = -1; } static int ideal_empty(ideal_q *q){ return q->count == 0; } static int ideal_full(ideal_q *q){ return q->count >= (LEN-1); } static void ideal_enqueue(ideal_q *q, int v){ if(ideal_full(q)) return; q->buf[q->head] = v & 0xFF; q->head = (q->head + 1) % LEN; q->count++; } static int ideal_dequeue(ideal_q *q, int *out){ if(ideal_empty(q)){ *out = -1; return 0; } *out = q->buf[q->tail]; q->tail = (q->tail + 1) % LEN; q->count--; return 1; } /* テスト1: 空キューからのdequeue の挙動 */ static int test_empty_dequeue(){ initialize_queue(); uint8_t v = dequeue(); // 実装は空なら NULL を返す(=0) int is_emp = is_empty(); // DTYPE は uint8_t なので NULL==0。empty のとき 0 を返すのは ambiguous。 // 成功条件: 実装が空であると報告すること(is_empty==1)かつ // dequeue() が 0 を返す(現在の実装の仕様を確認するため)。 int ok = (is_emp == 1); check_print("test_empty_dequeue: is_empty()==1?", ok); if(!ok) return 0; printf(" dequeue() returned: %u (note: 0 == NULL for uint8_t)\n", (unsigned)v); return 1; } /* テスト2: 基本FIFO */ static int test_basic_fifo(){ initialize_queue(); int N = 100; for(int i=0;i<N;i++) enqueue((uint8_t)i); int ok = 1; for(int i=0;i<N;i++){ uint8_t v = dequeue(); if(v != (uint8_t)i){ printf(" FIFO mismatch at i=%d: got=%u expected=%d\n", i, (unsigned)v, i); ok = 0; break; } } check_print("test_basic_fifo", ok); return ok; } /* テスト3: 満杯の振る舞い */ static int test_fill_and_overflow(){ initialize_queue(); // 実装は最大 LEN-1 (=255) 要素を入れる設計っぽい for(int i=0;i<LEN;i++){ enqueue((uint8_t)i); } // ここで is_full() が 1 であることを期待 int full = is_full(); check_print("test_fill_and_overflow: is_full()", full==1); // 255 個取り出して順序確認 int ok = 1; for(int i=0;i<LEN-1;i++){ uint8_t v = dequeue(); if(v != (uint8_t)i){ printf(" fill/overflow mismatch at i=%d: got=%u expected=%d\n", i, (unsigned)v, i); ok = 0; break; } } check_print("test_fill_and_overflow: drain order", ok); return full && ok; } /* テスト4: ラップアラウンド/長期ストレス(理想キューと常時比較) */ static int test_stress_random(unsigned long ops, unsigned seed){ initialize_queue(); ideal_q iq; ideal_init(&iq); unsigned next_val = 1; srand(seed); for(unsigned long i=0;i<ops;i++){ int do_enqueue; // enqueue をやや優先して長時間満杯状態を作りやすくする if(ideal_full(&iq)) do_enqueue = 0; else if(ideal_empty(&iq)) do_enqueue = 1; else do_enqueue = (rand() & 1); if(do_enqueue){ uint8_t v = (uint8_t)(next_val & 0xFF); enqueue(v); ideal_enqueue(&iq, v); next_val++; }else{ int ideal_v; int ideal_has = ideal_dequeue(&iq, &ideal_v); uint8_t got = dequeue(); if(!ideal_has){ // ideal empty -> we expect implementation to be empty (dequeue returns 0 and is_empty==1) if(is_empty() != 1){ printf(" Stress mismatch: ideal empty but is_empty()==0 at op %lu\n", i); return 0; } // dequeue() returns 0 both for empty and data==0; we can't fully distinguish here, // but if ideal had element and value==0 we'd already have set ideal_has==1. }else{ if(got != (uint8_t)ideal_v){ printf(" Stress mismatch at op %lu: got=%u expected=%d\n", i, (unsigned)got, ideal_v); return 0; } } } } check_print("test_stress_random", 1); return 1; } /* テスト5: 空時に 0 の値を入れたときの区別不可能性を示す */ static int test_zero_ambiguity(){ initialize_queue(); // 空で dequeue() -> returns 0 (NULL) uint8_t v_empty = dequeue(); int emp = is_empty(); // enqueue 0, dequeue -> 0 (but same as empty) enqueue(0); uint8_t v_data = dequeue(); int emp_after = is_empty(); int ambiguous = (v_empty == 0) && (v_data == 0) && (emp && emp_after); if(ambiguous){ printf(" Ambiguity detected: empty-dequeue -> %u, enqueue(0)/dequeue -> %u\n", (unsigned)v_empty, (unsigned)v_data); printf(" Explanation: dequeue() returns 0 for empty (NULL). Since DTYPE==uint8_t, value 0 is indistinguishable from empty.\n"); } check_print("test_zero_ambiguity (empty vs value 0)", ambiguous==1); return ambiguous; } int main(void){ int all_ok = 1; printf("=== queue implementation stress tests ===\n"); if(!test_empty_dequeue()) all_ok = 0; if(!test_basic_fifo()) all_ok = 0; if(!test_fill_and_overflow()) all_ok = 0; // ランダムストレステスト:比較的長めに回す(必要なら増やして下さい) unsigned long ops = 200000; // 20万操作 unsigned seed = (unsigned)time(NULL); printf("Running random stress (%lu ops, seed=%u)...\n", ops, seed); if(!test_stress_random(ops, seed)) all_ok = 0; if(!test_zero_ambiguity()) all_ok = 0; printf("=== RESULT: %s ===\n", all_ok ? PASS : FAIL); return all_ok ? 0 : 1; }
全部PASS
Claude4.1
#include "queue.h" #include <stdio.h> #include <assert.h> #include <stdint.h> // テスト結果を表示するマクロ #define TEST_PASS(name) printf("[PASS] %s\n", name) #define TEST_FAIL(name, msg) printf("[FAIL] %s: %s\n", name, msg) void test_basic_functionality() { printf("\n=== Test 1: 基本機能テスト ===\n"); initialize_queue(); // 空の確認 if (is_empty() != 1) { TEST_FAIL("初期状態", "キューが空でない"); return; } // エンキュー・デキューの基本動作 enqueue(42); DTYPE val = dequeue(); if (val != 42) { TEST_FAIL("基本エンキュー・デキュー", "値が一致しない"); return; } TEST_PASS("基本機能テスト"); } void test_full_capacity() { printf("\n=== Test 2: 容量限界テスト ===\n"); initialize_queue(); int count = 0; // 満杯になるまでエンキュー while (!is_full() && count < 257) { enqueue(count & 0xFF); count++; } if (count != 255) { char msg[100]; sprintf(msg, "容量が255でない (実際: %d)", count); TEST_FAIL("容量確認", msg); return; } // 満杯状態で追加エンキューを試みる enqueue(99); // デキューして数を確認 int dequeue_count = 0; while (!is_empty() && dequeue_count < 257) { dequeue(); dequeue_count++; } if (dequeue_count != 255) { char msg[100]; sprintf(msg, "デキュー数が255でない (実際: %d)", dequeue_count); TEST_FAIL("満杯時の動作", msg); return; } TEST_PASS("容量限界テスト"); } void test_empty_dequeue() { printf("\n=== Test 3: 空キューからのデキュー ===\n"); initialize_queue(); DTYPE val1 = dequeue(); // 空の状態でデキュー enqueue(0); // 0をエンキュー DTYPE val2 = dequeue(); // 0をデキュー DTYPE val3 = dequeue(); // 再び空の状態でデキュー if (val1 != val2 || val2 != val3) { // 3つとも0のはず TEST_PASS("空デキューのエラー値識別不可"); } else { TEST_FAIL("空デキューのエラー値", "正常な0と区別できない"); } } void test_wraparound_behavior() { printf("\n=== Test 4: 循環動作テスト ===\n"); initialize_queue(); // 満杯にする for (int i = 0; i < 255; i++) { enqueue(i & 0xFF); } // 全て取り出す for (int i = 0; i < 255; i++) { DTYPE val = dequeue(); if (val != (i & 0xFF)) { TEST_FAIL("循環動作1", "値の順序が不正"); return; } } // 再度満杯にする for (int i = 0; i < 255; i++) { enqueue((i + 100) & 0xFF); } // 最初の値を確認 DTYPE first = dequeue(); if (first != 100) { char msg[100]; sprintf(msg, "2回目の循環で最初の値が不正 (期待: 100, 実際: %d)", first); TEST_FAIL("循環動作2", msg); return; } TEST_PASS("循環動作テスト"); } void test_alternating_pattern() { printf("\n=== Test 5: 交互操作パターンテスト ===\n"); initialize_queue(); // 満杯にする for (int i = 0; i < 255; i++) { enqueue(i & 0xFF); } int failed = 0; // 1つデキュー、1つエンキューを繰り返す for (int i = 0; i < 1000; i++) { DTYPE val = dequeue(); enqueue(0xFF); // 常に満杯状態を維持しているはず if (!is_full()) { failed = 1; break; } } if (failed) { TEST_FAIL("交互操作", "満杯状態が維持されない"); } else { TEST_PASS("交互操作パターンテスト"); } } void test_stress_overflow() { printf("\n=== Test 6: 長期ストレステスト ===\n"); initialize_queue(); // 大量の操作でオーバーフローを狙う int failed = 0; for (long i = 0; i < 100000L; i++) { enqueue(1); if (is_empty()) { failed = 1; break; } dequeue(); if (!is_empty()) { failed = 1; break; } if (i % 10000 == 0) { printf(" Progress: %ld iterations\n", i); } } if (failed) { TEST_FAIL("ストレステスト", "予期しない状態変化"); } else { TEST_PASS("長期ストレステスト"); } } void test_data_integrity() { printf("\n=== Test 7: データ整合性テスト ===\n"); initialize_queue(); // 特定のパターンでデータを入れる for (int i = 0; i < 100; i++) { enqueue(i & 0xFF); } // 50個取り出す for (int i = 0; i < 50; i++) { DTYPE val = dequeue(); if (val != (i & 0xFF)) { TEST_FAIL("データ整合性1", "値が不正"); return; } } // さらに100個追加 for (int i = 100; i < 200; i++) { enqueue(i & 0xFF); } // 残りを取り出して確認 for (int i = 50; i < 100; i++) { DTYPE val = dequeue(); if (val != (i & 0xFF)) { TEST_FAIL("データ整合性2", "値が不正"); return; } } for (int i = 100; i < 200; i++) { DTYPE val = dequeue(); if (val != (i & 0xFF)) { TEST_FAIL("データ整合性3", "値が不正"); return; } } if (!is_empty()) { TEST_FAIL("データ整合性4", "キューが空でない"); return; } TEST_PASS("データ整合性テスト"); } void test_boundary_conditions() { printf("\n=== Test 8: 境界条件テスト ===\n"); initialize_queue(); // 254個入れる(満杯の1つ手前) for (int i = 0; i < 254; i++) { enqueue(i & 0xFF); } if (is_full()) { TEST_FAIL("境界条件1", "254個で満杯判定"); return; } // もう1つ追加(満杯になるはず) enqueue(254); if (!is_full()) { TEST_FAIL("境界条件2", "255個で満杯にならない"); return; } // 1つ取り出す dequeue(); if (is_full()) { TEST_FAIL("境界条件3", "254個で満杯判定が続く"); return; } TEST_PASS("境界条件テスト"); } void test_reinitialize() { printf("\n=== Test 9: 再初期化テスト ===\n"); initialize_queue(); // データを入れる for (int i = 0; i < 100; i++) { enqueue(i & 0xFF); } // 再初期化 initialize_queue(); if (!is_empty()) { TEST_FAIL("再初期化", "キューが空にならない"); return; } // 新しいデータを入れて取り出す enqueue(42); DTYPE val = dequeue(); if (val != 42) { char msg[100]; sprintf(msg, "再初期化後の値が不正 (期待: 42, 実際: %d)", val); TEST_FAIL("再初期化後の動作", msg); return; } TEST_PASS("再初期化テスト"); } void test_massive_cycle() { printf("\n=== Test 10: 大量サイクルテスト ===\n"); initialize_queue(); int failed = 0; // 255個の追加と削除を繰り返す for (int cycle = 0; cycle < 100; cycle++) { // 満杯にする for (int i = 0; i < 255; i++) { enqueue((cycle + i) & 0xFF); } if (!is_full()) { failed = 1; break; } // 全て取り出す for (int i = 0; i < 255; i++) { DTYPE val = dequeue(); if (val != ((cycle + i) & 0xFF)) { failed = 1; break; } } if (!is_empty()) { failed = 1; break; } if (cycle % 10 == 0) { printf(" Cycle %d completed\n", cycle); } } if (failed) { TEST_FAIL("大量サイクル", "サイクル中にエラー発生"); } else { TEST_PASS("大量サイクルテスト"); } } int main() { printf("=== キュー実装の破壊的テスト ===\n"); test_basic_functionality(); test_full_capacity(); test_empty_dequeue(); test_wraparound_behavior(); test_alternating_pattern(); test_stress_overflow(); test_data_integrity(); test_boundary_conditions(); test_reinitialize(); test_massive_cycle(); printf("\n=== テスト完了 ===\n"); return 0; }
全部パス
このqueueはEmptyのときNullを返すとか問題あるものの、一応全部のテストをパスしました。 こんなテスト意味あるのかな。