Cppcheck 1.67在我的源代码中引发了一个可移植性问题:
sscanf(s, "%d%*[,;.]%d", &f, &a);
这是我从中得到的信息:
没有字段宽度限制的scanf会因某些版本的libc上的大量输入数据而崩溃。
格式字符串的初衷是接受两个整数之间的三个可能的限制字符之一,今天 - 感谢Cppcheck [1] - 我看到%*[,;.]
甚至接受限制字符串。但是我怀疑我的格式字符串可能会导致崩溃,因为忽略了无限制部分。
是否存在缓冲区溢出问题? ...可能在幕后?
[1] 如何在远视和失明之间迷茫:
我试图通过%1*[,;.]
(some API doc之后)修复它,但是Cppcheck坚持这个问题,所以我也尝试了%*1[,;.]
同样的“成功”。似乎我现在必须压制它......
答案 0 :(得分:2)
恭喜您在Cppcheck 1.67(当前版本)中找到错误。
您基本上有三种解决方法:
重做您的格式(指定该字段,因为您只想匹配一个字符)。
char tmp;
if(3 != sscanf(s, "%d %c%d", &f, &tmp, &a) || tmp!=',' && tmp!=';' && tmp!= '.')
goto error;
直接抑制警告(最好是内联抑制):
//cppcheck-suppress invalidscanf_libc
if(2 != sscanf(s, "%d%1*[,;.]%d", &f, &a))
goto error;
不要忘记将错误报告为“缺陷/误报”,这样您就可以尽快退休并忘记解决方法。
答案 1 :(得分:1)
何时量化C sscanf函数中忽略的模式匹配?
总是量化(见下文)可能是一个好主意,但过度量化也可能会分散您的意图。在上面的例子中,必须跳过单个分隔符char ,量化肯定是有用的。
缓冲区溢出可能存在问题吗? ......也许在幕后?
您的代码不会导致崩溃。至于处理幕后的问题"问题,我尝试了大量的输入字符串。在我测试的C库中,没有内部缓冲区溢出。我尝试了随Borland C ++ 5.6.4一起提供的C lib,发现我无法通过大输入(超过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 15 -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