Safe functions: Check more possible function argument values
This commit is contained in:
parent
9f548efbd3
commit
c9906125de
|
@ -181,6 +181,11 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
|
||||||
else if (std::strcmp(argv[i], "--inconclusive") == 0)
|
else if (std::strcmp(argv[i], "--inconclusive") == 0)
|
||||||
mSettings->inconclusive = true;
|
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)
|
// Enforce language (--language=, -x)
|
||||||
else if (std::strncmp(argv[i], "--language=", 11) == 0 || std::strcmp(argv[i], "-x") == 0) {
|
else if (std::strncmp(argv[i], "--language=", 11) == 0 || std::strcmp(argv[i], "-x") == 0) {
|
||||||
std::string str;
|
std::string str;
|
||||||
|
|
|
@ -39,6 +39,7 @@ Settings::Settings()
|
||||||
experimental(false),
|
experimental(false),
|
||||||
force(false),
|
force(false),
|
||||||
inconclusive(false),
|
inconclusive(false),
|
||||||
|
allFunctionsAreSafe(false),
|
||||||
inlineSuppressions(false),
|
inlineSuppressions(false),
|
||||||
jobs(1),
|
jobs(1),
|
||||||
jointSuppressionReport(false),
|
jointSuppressionReport(false),
|
||||||
|
|
|
@ -146,6 +146,9 @@ public:
|
||||||
/** @brief Inconclusive checks */
|
/** @brief Inconclusive checks */
|
||||||
bool inconclusive;
|
bool inconclusive;
|
||||||
|
|
||||||
|
/** @brief Experimental flag that says all functions are "safe" */
|
||||||
|
bool allFunctionsAreSafe;
|
||||||
|
|
||||||
/** @brief Is --inline-suppr given? */
|
/** @brief Is --inline-suppr given? */
|
||||||
bool inlineSuppressions;
|
bool inlineSuppressions;
|
||||||
|
|
||||||
|
|
|
@ -2079,6 +2079,11 @@ const Token * Function::constructorMemberInitialization() const
|
||||||
return nullptr;
|
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* SymbolDatabase::addGlobalFunction(Scope*& scope, const Token*& tok, const Token *argStart, const Token* funcStart)
|
||||||
{
|
{
|
||||||
Function* function = nullptr;
|
Function* function = nullptr;
|
||||||
|
|
|
@ -839,6 +839,8 @@ public:
|
||||||
setFlag(fHasBody, state);
|
setFlag(fHasBody, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isSafe(const Settings *settings) const;
|
||||||
|
|
||||||
const Token *tokenDef; ///< function name token in class definition
|
const Token *tokenDef; ///< function name token in class definition
|
||||||
const Token *argDef; ///< function argument start '(' in class definition
|
const Token *argDef; ///< function argument start '(' in class definition
|
||||||
const Token *token; ///< function name token in implementation
|
const Token *token; ///< function name token in implementation
|
||||||
|
|
|
@ -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)
|
ValueFlow::Value::Value(const Token *c, long long val)
|
||||||
: valueType(INT),
|
: valueType(INT),
|
||||||
intvalue(val),
|
intvalue(val),
|
||||||
|
@ -5436,6 +5528,7 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
|
||||||
valueFlowContainerSize(tokenlist, symboldatabase, errorLogger, settings);
|
valueFlowContainerSize(tokenlist, symboldatabase, errorLogger, settings);
|
||||||
valueFlowContainerAfterCondition(tokenlist, symboldatabase, errorLogger, settings);
|
valueFlowContainerAfterCondition(tokenlist, symboldatabase, errorLogger, settings);
|
||||||
}
|
}
|
||||||
|
valueFlowSafeFunctions(tokenlist, symboldatabase, errorLogger, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
valueFlowDynamicBufferSize(tokenlist, symboldatabase, errorLogger, settings);
|
valueFlowDynamicBufferSize(tokenlist, symboldatabase, errorLogger, settings);
|
||||||
|
|
|
@ -119,6 +119,8 @@ private:
|
||||||
TEST_CASE(valueFlowContainerSize);
|
TEST_CASE(valueFlowContainerSize);
|
||||||
|
|
||||||
TEST_CASE(valueFlowDynamicBufferSize);
|
TEST_CASE(valueFlowDynamicBufferSize);
|
||||||
|
|
||||||
|
TEST_CASE(valueFlowSafeFunctions);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isNotTokValue(const ValueFlow::Value &val) {
|
static bool isNotTokValue(const ValueFlow::Value &val) {
|
||||||
|
@ -295,8 +297,8 @@ private:
|
||||||
settings.debugwarnings = false;
|
settings.debugwarnings = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<ValueFlow::Value> tokenValues(const char code[], const char tokstr[]) {
|
std::list<ValueFlow::Value> tokenValues(const char code[], const char tokstr[], const Settings *s = nullptr) {
|
||||||
Tokenizer tokenizer(&settings, this);
|
Tokenizer tokenizer(s ? s : &settings, this);
|
||||||
std::istringstream istr(code);
|
std::istringstream istr(code);
|
||||||
errout.str("");
|
errout.str("");
|
||||||
tokenizer.tokenize(istr, "test.cpp");
|
tokenizer.tokenize(istr, "test.cpp");
|
||||||
|
@ -3902,6 +3904,21 @@ private:
|
||||||
"}";
|
"}";
|
||||||
ASSERT_EQUALS(true, testValueOfX(code, 4U, 100, ValueFlow::Value::BUFFER_SIZE));
|
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)
|
REGISTER_TEST(TestValueFlow)
|
||||||
|
|
Loading…
Reference in New Issue