首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 語言字符串函數安全性深度剖析與安全替代方案構建

     編輯:關於C語言

C 語言字符串函數安全性深度剖析與安全替代方案構建

C 語言字符串以「以 \0 結尾的字符數組」定義,標準庫函數 (strcpy, sprintf 等) **不檢查目標緩衝區邊界**,是緩衝區溢出、內存越界讀寫的頭號元兇。本文通過實測揭示 strcpy/strncpy/sprintf/_snprintf 的隱性缺陷,構建**邊界安全、保證 \0 終止、返回有效長度**的安全替代函數。

一、字符串拷貝函數遞進分析

1. strcpy — 完全無保護


char dst[128], src[256];
memset(dst, '*', 128); dst[127]='\0';
memset(src, '#', 256); src[255]='\0';
strcpy(dst, src); // 緩衝區溢出,程序崩潰

2. strncpy — 有長度限制但**不保證 \0 終止**


strncpy(dst, src, 128); // src 長 256,只拷前 128 字節
// dst 無 \0 結尾!後續 printf/strlen 會越界讀取,輸出亂碼

實測現象:strncpy(dst, src, 8) 只拷 8 字節,dst 末尾無 \0,打印顯示 ########*******... (8個 # 後接原 dst 殘留 *)。

3. 安全拷貝函數 xg_strncpy 設計目標

  • 參數:dst, src, dst_size (目標緩衝區總大小)
  • 拷貝長度 = min(strlen(src)+1, dst_size)
  • **強制** 在 dst[拷貝長度-1] = '\0'
  • 返回實際拷貝字節數 (含 \0)

size_t xg_strncpy(char *dst, const char *src, size_t dst_size) {
    if (dst_size == 0) return 0;
    size_t src_len = strlen(src);
    size_t copy_len = (src_len + 1 < dst_size) ? (src_len + 1) : dst_size;
    memcpy(dst, src, copy_len - 1);
    dst[copy_len - 1] = '\0';
    return copy_len;
}

關鍵修正:原文 memcpy(dst, src, dst_size); dst[dst_size-1]='\0' 會**越界讀取** src (若 src 短於 dst_size),Debug 模式下觸發內存讀越界錯誤。必須先計算實際拷貝長度。

二、格式化輸出函數遞進分析

1. sprintf — 無邊界檢查,極危險

同 strcpy,長字符串格式化直接溢出。

2. _snprintf — 有長度限制但**雙重缺陷**

  • 緩衝區空間不足時**不添加 \0** (同 strncpy)
  • 返回值**負數 (-1)** 表示截斷,**非寫入字節數** → 導致累積寫入邏輯 offset += _snprintf(buf+offset, ...) 在截斷時 offset 減少,寫向 buf[-1] → **緩衝區下溢**

3. 安全格式化函數 xg_printf 設計

  • 變參接口,兼容 printf 格式字符串
  • 內部用 _vsnprintf + 手動補 \0
  • **返回實際字符串長度 (不含 \0)**,便於安全累積

int xg_printf(char *buf, size_t buf_size, const char *fmt, ...) {
    if (buf_size == 0) return 0;
    va_list ap;
    va_start(ap, fmt);
    int written = _vsnprintf(buf, buf_size - 1, fmt, ap); // 預留 1 字節給 \0
    va_end(ap);
    buf[buf_size - 1] = '\0'; // 強制終止
    return (written < 0) ? (int)strlen(buf) : written; // 修正返回值
}

三、標準庫安全擴展對比 (C11 Annex K / TR 24731)

不安全函數C11 安全版本關鍵改進
strcpystrcpy_s檢查目標大小、保證 \0、返回錯誤碼
strncpystrncpy_s同上,補齊 \0
sprintfsprintf_s需目標尺寸參數
snprintfsnprintf_s保證 \0、返回寫入長度
strcatstrcat_s檢查剩餘空間
getsgets_s移除危險 gets

現狀:MSVC 完整支持 _s 系列;GCC/Clang 需啟用 __STDC_WANT_LIB_EXT1__ 且實現不完整。跨平台項目建議**自行封裝**或使用成熟庫 (如 safeclib)。

四、工程落地檢查清單

  • [ ] 代碼中**零**直接調用 strcpy/sprintf/strcat/gets
  • [ ] 所有字符串操作使用安全封裝函數 (內部或第三方)
  • [ ] 緩衝區大小以**常量/宏**定義,杜絕魔數
  • [ ] 靜態分析工具 (PC-Lint, Coverity, Clang Static Analyzer) 納入 CI 流程
  • [ ] 代碼審查重點檢查:字符串拷貝/格式化/拼接邊界處理

C 語言字符串安全的核心不在於「用哪個函數」,而在於**建立「每個緩衝區都有明確擁有者、明確大小、明確邊界檢查」的工程紀律**。安全封裝函數只是紀律的代碼體現。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved