網絡上流傳極廣的《12 個有趣的 C 語言問答》被許多技術網站轉載,甚至被視為面試寶典。實則這是一篇充滿誤導、錯誤示範甚至危險代碼模式的「垃圾文」。本系列將逐題拆解,糾正謬誤,建立專業的 C 語言工程認知。
char buff[10];
memset(buff, 0, sizeof(buff));
gets(buff); // 危險!無長度檢查
printf("The buffer entered is [%s]\n", buff);
gets() 不檢查輸入長度,必然導致緩衝區溢出gets(),C99 標記棄用memset 清零無法防禦溢出寫入// 方案 1:fgets(推薦)
char buff[10];
if (fgets(buff, sizeof(buff), stdin)) {
// 移除可能的換行符
buff[strcspn(buff, "\n")] = '\0';
printf("Input: [%s]\n", buff);
}
// 方案 2:C11 gets_s(可選)
// char buff[10];
// gets_s(buff, sizeof(buff));
char passwd[10];
memset(passwd, 0, sizeof(passwd));
strcpy(passwd, argv[1]); // 無長度檢查!
if (strcmp("LinuxGeek", passwd) == 0) flag = 1;
原文聲稱:輸入超長密碼導致溢出覆蓋 flag,繞過驗證。並建議使用 strncpy()。
strncpy() 不保證以 \0 結尾,可能導致字符串處理錯誤-fstack-protector),原文需關閉保護才能復現#define PASSWD_MAX 9 // 預留 \0 位置
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s \n", argv[0]);
return 1;
}
// 長度預檢
if (strlen(argv[1]) > PASSWD_MAX) {
puts("Password too long");
return 1;
}
char passwd[PASSWD_MAX + 1] = {0};
strcpy(passwd, argv[1]); // 安全:已驗證長度
// 常數時間比較(防時序攻擊)
const char *expected = "LinuxGeek";
int match = 1;
for (int i = 0; expected[i] || passwd[i]; i++) {
if (expected[i] != passwd[i]) match = 0;
}
if (match) puts("Access granted");
else puts("Access denied");
return 0;
}
| 原文模式 | 風險 | 專業替代 |
|---|---|---|
| gets() | 緩衝區溢出 | fgets() / gets_s() |
| strcpy() 無檢查 | 溢出/注入 | strncpy_s() / strlcpy() / 手動檢查+strcpy |
| sprintf() | 格式化溢出 | snprintf() |
| scanf("%s") | 無長度限制 | scanf("%9s") / fgets() |