2014-10-27 54 views
3

Cppcheck 1.67在我的源代码在该行提出了一个便携的问题:当量化忽略模式匹配在C sscanf函数

sscanf(s, "%d%*[,;.]%d", &f, &a); 

这是我从中得到的消息:

scanf函数没有字段宽度限制可能会导致某些版本的libc上的巨大输入数据崩溃。

格式字符串的初衷是为了今天接受两个整数之间的三种可能限制器字符之一, - 感谢Cppcheck [1] - 我看到%*[,;.]接受甚至限制字符的字符串。不过,我怀疑我的格式字符串可能会导致崩溃,因为无限的部分被忽略。

是否有缓冲区溢出问题?...也许在幕后?


[1] 如何失去了远见和失明之间获得:

我试图修复它通过%1*[,;.]some API doc后),但Cppcheck的问题坚持,所以我也尝试过%*1[,;.]与“成功”相同。似乎我现在不得不压制它...

+1

所以,你有一个工作正确的格式,并且将其更改为别的东西(?可能不是正确的),以抑制假阳性,没有成功?看起来更像是一个“inline-suppressions”的工作:在这里搜索http://cppcheck.sourceforge.net/manual.pdf – Deduplicator 2014-10-27 12:39:40

+0

指出的信息没有意义,因为'%* [,;。]'不使用缓冲区。 – BLUEPIXY 2014-10-27 12:42:52

+0

@Deduplicator不,我试图改进模式以满足Cppcheck帮助检测的*实际*需求。 – Wolf 2014-10-27 12:42:55

回答

2

祝贺您在Cppcheck 1.67(当前版本)中发现错误。

你有三个基本的解决方法:

  1. 就忽略了假阳性。
  2. 重做你的格式(指定该字段,因为你只想匹配一个字符)。

    char tmp; 
    if(3 != sscanf(s, "%d %c%d", &f, &tmp, &a) || tmp!=',' && tmp!=';' && tmp!= '.') 
        goto error; 
    
  3. 直接禁止警告(最好是直列镇压):

    //cppcheck-suppress invalidscanf_libc 
    if(2 != sscanf(s, "%d%1*[,;.]%d", &f, &a)) 
        goto error; 
    

不要忘记报告错误,如 “缺陷/假阳性”,这样你就可以退休了并尽可能快地忘记该解决方法。

+0

再次请重新考虑#2中的分离器处理。正如我发现的那样,'“%d%c%d”'完全可以做这项工作,因为它是一个分隔符(不能是数字)。 – Wolf 2014-10-28 10:36:28

+0

但是可能会有空白。除非你说不可能,并且这种可能性在原始'scanf'中是隐含的。 – Deduplicator 2014-10-28 16:49:57

+0

(原文中没有空格)对不起,标题错误地表示问题是关于'scanf'函数,文本没有这个错误。 – Wolf 2014-10-28 17:26:51

1

何时量化C sscanf函数中忽略的模式匹配?

可能总是量化(见下文)可能是一个好主意,但过度量化也可能会分散您的意图。在上述情况下,必须跳过单个分隔符char,量化肯定会有用。

是否有缓冲区溢出问题? ......也许在幕后?

,将没有造成代码崩溃。至于处理“幕后”问题,我尝试了大量的输入字符串。在我测试的C库中,没有内部缓冲区溢出。我尝试了Borland C++ 5.6.4附带的C库,发现我无法触发缓冲区溢出带有大量输入(超过4亿个字符)。

出人意料的是,Cppcheck不是完全错误 - 有一个便携性问题,但不同的一个:

#include <stdio.h> 
#include <assert.h> 
#include <sstream> 

int traced_sscanf_set(const int count, const bool limited) 
{ 
    const char sep = '.'; 
    printf("\n"); 
    std::stringstream ss; 
    ss << "123" << std::string(count, sep) << "456"; 
    std::string s = ss.str(); 
    printf("string of size %d with %d '%c's in it\n", s.size(), count, sep); 
    std::stringstream fs; 
    fs << "%d%"; 
    if (limited) { 
     fs << count; 
    } 
    fs << "*["<< sep << "]%d"; 
    std::string fmt = fs.str(); 
    printf("fmt: \"%s\"\n", fmt.c_str()); 
    int a = 0; 
    int b = 0; 
    const sscanfResult = sscanf(s.c_str(), fmt.c_str(), &a, &b); 
    printf("sscanfResult=%d, a=%d, b=%d\n", sscanfResult, a, b); 
    return sscanfResult; 
} 

void test_sscanf() 
{ 
    assert(traced_sscanf_set(0x7fff, true)==2); 
    assert(traced_sscanf_set(0x7fff, false)==2); 
    assert(traced_sscanf_set(0x8000, true)==2); 
    assert(traced_sscanf_set(0x8000, false)==1); 
} 

我查库,内部限制消耗的输入(和跳过)至32767(2 -1)字符,如果format参数中没有明确指定的限制。

对于那些有兴趣谁,这里是跟踪输出:

string of size 32773 with 32767 '.'s in it 
fmt: "%d%32767*[.]%d" 
sscanfResult=2, a=123, b=456 

string of size 32773 with 32767 '.'s in it 
fmt: "%d%*[.]%d" 
sscanfResult=2, a=123, b=456 

string of size 32774 with 32768 '.'s in it 
fmt: "%d%32768*[.]%d" 
sscanfResult=2, a=123, b=456 

string of size 32774 with 32768 '.'s in it 
fmt: "%d%*[.]%d" 
sscanfResult=1, a=123, b=0