歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

寫出完美的snprintf

平時公司的代碼安全掃描會給出不安全代碼的告警,其中會檢查代碼中間的strcpy和sprintf函數,而要求使用strncpy和snprintf。今天我們討論一下怎樣寫出完美的snprintf。

snprintf是一個在C99才被加入如標准的函數,原來的各個編譯器都有自己的實現,至少.NET2003編譯器還要是使用_snprintf這樣的函數名稱。

而這些編譯器間都有差異,而且Glibc庫又有自己的不同的實現。

查詢一下snprintf的函數的MSDN說明。如下:

Let len be the length of the formatted data string (not including the terminating null). len and count are in bytes for _snprintf, wide characters for _snwprintf.

If len < count, then len characters are stored in buffer, a null-terminator is appended, and len is returned.

If len = count, then len characters are stored in buffer, no null-terminator is appended, and len is returned.

If len > count, then count characters are stored in buffer, no null-terminator is appended, and a negative value is returned.

If buffer is a null pointer and count is nonzero, or format is a null pointer, the invalid parameter handler is invoked, as described in Parameter Validation. If execution is allowed to continue, these functions return -1 and set errno to EINVAL.

For information about these and other error codes, see _doserrno, errno, _sys_errlist, and _sys_nerr.

當buffer長度不夠時,返回的是負數。

而LINUX的說明如下:

Return value

      Upon  successful  return,  these  functions  return  the number of characters printed (not

      including the trailing '\0' used to end output to strings).  The functions snprintf()  and

      vsnprintf()  do not write more than size bytes (including the trailing '\0').  If the out-

      put was truncated due to this limit then the return value is the number of characters (not

      including  the  trailing '\0') which would have been written to the final string if enough

      space had been available. Thus, a return value of size or more means that the  output  was

      truncated.  (See  also  below under NOTES.)  If an output error is encountered, a negative

      value is returned.

NOTES

      The glibc implementation of the functions snprintf() and vsnprintf() conforms to  the  C99

      standard,  i.e.,  behaves  as  described above, since glibc version 2.1. Until glibc 2.0.6

      they would return -1 when the output was truncated.

在比較新的版本中,其遵守C99的規范,當buffer長度不夠時,返回的是超過Buffer長度的正數。

 

你會發現,如果傳遞的buf的長度不夠的情況下,null-terminator都沒有加入。。。。。那麼你使用的時候還是可能溢出。而且返回值的判斷在不同的平台還可能不一樣。

當然我理解使用snprintf的主要好處在於安全性,但是如果使用不對仍然可能有悲劇發生,比如你的更新SQL語句被截斷了WHERE條件。所以返回值還是要判斷。

 

那麼最簡單的方法還是傳遞的給snprintf的長度參數count應該buf長度-1,然後還要將最後一個字符改為null-terminator。然後再加入相應的判斷。

發現返回值小於0或者大於(可能有等於,看你傳遞的長度參數和Buffer的關系)實際長度時認為出現問題。

 

經過測試的正確寫法:

max_len = sizeof(buf)-1;

len = snprintf(buf, max_len, ...);

if ((len < 0) || (len > max_len))

{

    //錯誤處理

}

else

{

    buf[max_len]=0;

    do job...

}

 

代碼說明:

 

 
#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <string.h>  
#include <errno.h>  

int main(int argc, char** argv)  
{   
    if (argc < 2)
    {
        printf("usage:./snprintf_perfect xxxx\n");
        return -1;
    }
    char buffer[16];
    size_t max_len = sizeof(buffer) - 1;
    int len = snprintf(buffer, max_len, "%s", argv[1]);
    if ((len < 0) || (len > max_len))
    {
        printf("overflow!!\n");
    }
    else
    {
        buffer[max_len] = 0;
        printf("%s\n", buffer);
    }
    
    return 0;
}
 

結果說明:

# 長度為buffer長度-1

[root@rocket linux_programming]# ./snprintf_perfect 012345678901234

01234567890123

# 長度為buffer長度,溢出!

[root@rocket linux_programming]# ./snprintf_perfect 0123456789012345

overflow!!

Copyright © Linux教程網 All Rights Reserved