簡體   English   中英

意外的snprintf行為

[英]Unexpected snprintf behaviour

我在幾個平台上用c ++注意到(我認為)snprintf非常奇怪的行為。 請考慮以下代碼(導致觀察到的行為的最小工作示例):

#include <stdio.h>

char test1[512];
char test2[512];
char test3[1024];
char test4[1024];

int main()
{
    snprintf(test1, sizeof(test1), "test1");
    snprintf(test2, sizeof(test2), "test2");
    snprintf(test3, sizeof(test3), "%s %s", test1, test2);
    return 0;
}

使用--tool = exp-sgcheck運行valgrind時,會報告以下錯誤(對於第3個snprintf語句):

==30302== Invalid read of size 1
==30302==    at 0x568E4EB: vfprintf (in /lib64/libc-2.19.so)
==30302==    by 0x56B7608: vsnprintf (in /lib64/libc-2.19.so)
==30302==    by 0x5695209: snprintf (in /lib64/libc-2.19.so)
==30302==    by 0x4006AD: main (1.cc:12)
==30302==  Address 0x601460 expected vs actual:
==30302==  Expected: global array "test1" of size 1,024 in object with soname "NONE"
==30302==  Actual:   global array "test2" of size 512 in object with soname "NONE"
==30302==  Actual:   is 0 after Expected

因此,將test1作為參數傳遞給第一個%s導致在test1數組結束后進行讀取。

此行為導致Windows驅動程序中出現多個頁面錯誤(是的,我知道它是靜態數據...)。 幸運的是代碼是可移植的,當移植到linux valgrind報告錯誤時。

但據我所知,snprintf應該在第6個字節用\\ 0終止test1(它確實如此)。 那么為什么在test1數組結束后讀取第3個snprintf語句呢? 將第3個snprintf語句更改為

snprintf(test3, sizeof(test3), "%.512s %s", test1, test2);

解決了兩個平台上的問題。 將代碼編譯為C代碼(而不是C ++)不會導致錯誤。

更新:在Linux(也可能是Windows)上,如果代碼編譯時包含調試信息並禁用優化(-g -O0表示gcc),則只會發生錯誤。

由於全局對象(如示例中的數組)是0初始化的,因此最后一個snprintf應該永遠不會讀取超出字符串的結尾,而不管先前的sprintfs是否復制了終止0 char。 唯一的解釋是,先前的snprintf復制的內容遠遠超過提交的“test1”到目標test1 ,用非0覆蓋所有0(對於隨機內存,不會有0不可能)。

這是不太可能的 - 這樣一個明顯的錯誤早就發現了。 關於驅動程序中的錯誤,我懷疑內存是被完全不相關的“進程”覆蓋的(在一般意義上,可能是另一個驅動程序)。 對於桌面應用程序,我沒有解釋為什么它會失敗。 嘗試使用gcc 4.8.3在Codingground上運行示例運行得很好並在最后添加printf()時打印了預期的字符串。

順便說一句,原始代碼在啟用優化的情況下運行正常並不奇怪:由於沒有可觀察到的影響,編譯器可能只發出一個NOP。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM