Safe functions: Check more possible function argument values

This commit is contained in:
Daniel Marjamäki 2019-07-10 16:59:05 +02:00
parent 9f548efbd3
commit c9906125de
7 changed files with 128 additions and 2 deletions

View File

@ -181,6 +181,11 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
else if (std::strcmp(argv[i], "--inconclusive") == 0)
mSettings->inconclusive = true;
// Experimental: this flag says that all functions are "safe".
// todo: add proper configuration options
else if (std::strcmp(argv[i], "--all-functions-are-safe") == 0)
mSettings->allFunctionsAreSafe = true;
// Enforce language (--language=, -x)
else if (std::strncmp(argv[i], "--language=", 11) == 0 || std::strcmp(argv[i], "-x") == 0) {
std::string str;

View File

@ -39,6 +39,7 @@ Settings::Settings()
experimental(false),
force(false),
inconclusive(false),
allFunctionsAreSafe(false),
inlineSuppressions(false),
jobs(1),
jointSuppressionReport(false),

View File

@ -146,6 +146,9 @@ public:
/** @brief Inconclusive checks */
bool inconclusive;
/** @brief Experimental flag that says all functions are "safe" */
bool allFunctionsAreSafe;
/** @brief Is --inline-suppr given? */
bool inlineSuppressions;

View File

@ -2079,6 +2079,11 @@ const Token * Function::constructorMemberInitialization() const
return nullptr;
}
bool Function::isSafe(const Settings *settings) const
{
return settings->allFunctionsAreSafe;
}
Function* SymbolDatabase::addGlobalFunction(Scope*& scope, const Token*& tok, const Token *argStart, const Token* funcStart)
{
Function* function = nullptr;

View File

@ -839,6 +839,8 @@ public:
setFlag(fHasBody, state);
}
bool isSafe(const Settings *settings) const;
const Token *tokenDef; ///< function name token in class definition
const Token *argDef; ///< function argument start '(' in class definition
const Token *token; ///< function name token in implementation

View File

@ -5338,6 +5338,98 @@ static void valueFlowDynamicBufferSize(TokenList *tokenlist, SymbolDatabase *sym
}
}
static bool getMinMaxValues(const ValueType *vt, const cppcheck::Platform &platform, int64_t *minValue, int64_t *maxValue)
{
if (!vt || !vt->isIntegral() || vt->pointer)
return false;
int bits;
switch (vt->type) {
case ValueType::Type::BOOL:
bits = 1;
break;
case ValueType::Type::CHAR:
bits = platform.char_bit;
break;
case ValueType::Type::SHORT:
bits = platform.short_bit;
break;
case ValueType::Type::INT:
bits = platform.int_bit;
break;
case ValueType::Type::LONG:
bits = platform.long_bit;
break;
case ValueType::Type::LONGLONG:
bits = platform.long_long_bit;
break;
default:
return false;
};
if (bits == 1) {
*minValue = 0;
*maxValue = 1;
} else if (bits < 62) {
if (vt->sign == ValueType::Sign::UNSIGNED) {
*minValue = 0;
*maxValue = (1LL << bits) - 1;
} else {
*minValue = -(1LL << (bits - 1));
*maxValue = (1LL << (bits - 1)) - 1;
}
} else if (bits == 64) {
if (vt->sign == ValueType::Sign::UNSIGNED) {
*minValue = 0;
*maxValue = LLONG_MAX; // todo max unsigned value
} else {
*minValue = LLONG_MIN;
*maxValue = LLONG_MAX;
}
} else {
return false;
}
return true;
}
static void valueFlowSafeFunctions(TokenList *tokenlist, SymbolDatabase *symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
{
if (settings->platformType == cppcheck::Platform::PlatformType::Unspecified)
return;
for (const Scope *functionScope : symboldatabase->functionScopes) {
if (!functionScope->bodyStart)
continue;
const Function *function = functionScope->function;
if (!function)
continue;
if (!function->isSafe(settings))
continue;
for (const Variable &arg : function->argumentList) {
int64_t minValue, maxValue;
if (!getMinMaxValues(arg.valueType(), *settings, &minValue, &maxValue))
continue;
std::list<ValueFlow::Value> argValues;
argValues.emplace_back(minValue);
argValues.emplace_back(maxValue);
valueFlowForward(const_cast<Token *>(functionScope->bodyStart->next()),
functionScope->bodyEnd,
&arg,
arg.declarationId(),
argValues,
false,
false,
tokenlist,
errorLogger,
settings);
}
}
}
ValueFlow::Value::Value(const Token *c, long long val)
: valueType(INT),
intvalue(val),
@ -5436,6 +5528,7 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
valueFlowContainerSize(tokenlist, symboldatabase, errorLogger, settings);
valueFlowContainerAfterCondition(tokenlist, symboldatabase, errorLogger, settings);
}
valueFlowSafeFunctions(tokenlist, symboldatabase, errorLogger, settings);
}
valueFlowDynamicBufferSize(tokenlist, symboldatabase, errorLogger, settings);

View File

@ -119,6 +119,8 @@ private:
TEST_CASE(valueFlowContainerSize);
TEST_CASE(valueFlowDynamicBufferSize);
TEST_CASE(valueFlowSafeFunctions);
}
static bool isNotTokValue(const ValueFlow::Value &val) {
@ -295,8 +297,8 @@ private:
settings.debugwarnings = false;
}
std::list<ValueFlow::Value> tokenValues(const char code[], const char tokstr[]) {
Tokenizer tokenizer(&settings, this);
std::list<ValueFlow::Value> tokenValues(const char code[], const char tokstr[], const Settings *s = nullptr) {
Tokenizer tokenizer(s ? s : &settings, this);
std::istringstream istr(code);
errout.str("");
tokenizer.tokenize(istr, "test.cpp");
@ -3902,6 +3904,21 @@ private:
"}";
ASSERT_EQUALS(true, testValueOfX(code, 4U, 100, ValueFlow::Value::BUFFER_SIZE));
}
void valueFlowSafeFunctions() {
const char *code;
std::list<ValueFlow::Value> values;
Settings s;
s.allFunctionsAreSafe = true;
code = "void f(short x) {\n"
" return x + 0;\n"
"}";
values = tokenValues(code, "+", &s);
ASSERT_EQUALS(2, values.size());
ASSERT_EQUALS(-0x8000, values.front().intvalue);
ASSERT_EQUALS(0x7fff, values.back().intvalue);
}
};
REGISTER_TEST(TestValueFlow)