diff --git a/Makefile b/Makefile index 827f5233221..3cec427f9b8 100644 --- a/Makefile +++ b/Makefile @@ -278,6 +278,7 @@ LIBOBJ = $(libcppdir)/valueflow.o \ $(libcppdir)/vf_string.o \ $(libcppdir)/vf_switchvariable.o \ $(libcppdir)/vf_symbolicinfer.o \ + $(libcppdir)/vf_symbolicoperators.o \ $(libcppdir)/vf_unknownfunctionreturn.o \ $(libcppdir)/vfvalue.o @@ -489,7 +490,7 @@ validateRules: ###### Build -$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/addoninfo.h lib/analyzer.h lib/astutils.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/findtoken.h lib/forwardanalyzer.h lib/infer.h lib/library.h lib/mathlib.h lib/platform.h lib/programmemory.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vf_analyze.h lib/vf_analyzers.h lib/vf_array.h lib/vf_arraybool.h lib/vf_arrayelement.h lib/vf_bailout.h lib/vf_bitand.h lib/vf_common.h lib/vf_conditionexpressions.h lib/vf_debug.h lib/vf_enumvalue.h lib/vf_functionreturn.h lib/vf_globalconstvar.h lib/vf_globalstaticvar.h lib/vf_impossiblevalues.h lib/vf_infercondition.h lib/vf_iteratorinfer.h lib/vf_iterators.h lib/vf_number.h lib/vf_pointeralias.h lib/vf_reverse.h lib/vf_rightshift.h lib/vf_sameexpressions.h lib/vf_settokenvalue.h lib/vf_string.h lib/vf_switchvariable.h lib/vf_symbolicinfer.h lib/vf_unknownfunctionreturn.h lib/vfvalue.h +$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/addoninfo.h lib/analyzer.h lib/astutils.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/findtoken.h lib/forwardanalyzer.h lib/infer.h lib/library.h lib/mathlib.h lib/platform.h lib/programmemory.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vf_analyze.h lib/vf_analyzers.h lib/vf_array.h lib/vf_arraybool.h lib/vf_arrayelement.h lib/vf_bailout.h lib/vf_bitand.h lib/vf_common.h lib/vf_conditionexpressions.h lib/vf_debug.h lib/vf_enumvalue.h lib/vf_functionreturn.h lib/vf_globalconstvar.h lib/vf_globalstaticvar.h lib/vf_impossiblevalues.h lib/vf_infercondition.h lib/vf_iteratorinfer.h lib/vf_iterators.h lib/vf_number.h lib/vf_pointeralias.h lib/vf_reverse.h lib/vf_rightshift.h lib/vf_sameexpressions.h lib/vf_settokenvalue.h lib/vf_string.h lib/vf_switchvariable.h lib/vf_symbolicinfer.h lib/vf_symbolicoperators.h lib/vf_unknownfunctionreturn.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/valueflow.cpp $(libcppdir)/tokenize.o: lib/tokenize.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/astutils.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/summaries.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h @@ -618,7 +619,7 @@ $(libcppdir)/fwdanalysis.o: lib/fwdanalysis.cpp lib/addoninfo.h lib/astutils.h l $(libcppdir)/importproject.o: lib/importproject.cpp externals/picojson/picojson.h externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/config.h lib/errortypes.h lib/filesettings.h lib/importproject.h lib/json.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/importproject.cpp -$(libcppdir)/infer.o: lib/infer.cpp lib/calculate.h lib/config.h lib/errortypes.h lib/infer.h lib/mathlib.h lib/valueptr.h lib/vfvalue.h +$(libcppdir)/infer.o: lib/infer.cpp lib/calculate.h lib/config.h lib/errortypes.h lib/infer.h lib/mathlib.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueptr.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/infer.cpp $(libcppdir)/keywords.o: lib/keywords.cpp lib/config.h lib/keywords.h lib/standards.h lib/utils.h @@ -753,6 +754,9 @@ $(libcppdir)/vf_switchvariable.o: lib/vf_switchvariable.cpp lib/addoninfo.h lib/ $(libcppdir)/vf_symbolicinfer.o: lib/vf_symbolicinfer.cpp lib/astutils.h lib/config.h lib/errortypes.h lib/infer.h lib/library.h lib/mathlib.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueptr.h lib/vf_settokenvalue.h lib/vf_symbolicinfer.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_symbolicinfer.cpp +$(libcppdir)/vf_symbolicoperators.o: lib/vf_symbolicoperators.cpp lib/astutils.h lib/config.h lib/errortypes.h lib/infer.h lib/library.h lib/mathlib.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/vf_common.h lib/vf_settokenvalue.h lib/vf_symbolicoperators.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_symbolicoperators.cpp + $(libcppdir)/vf_unknownfunctionreturn.o: lib/vf_unknownfunctionreturn.cpp lib/addoninfo.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/vf_common.h lib/vf_settokenvalue.h lib/vf_unknownfunctionreturn.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_unknownfunctionreturn.cpp diff --git a/lib/astutils.cpp b/lib/astutils.cpp index af9f73a38ab..2a655e33add 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -959,7 +959,7 @@ bool extractForLoopValues(const Token *forToken, const Token *incExpr = forToken->next()->astOperand2()->astOperand2()->astOperand2(); if (!initExpr || !initExpr->isBinaryOp() || initExpr->str() != "=" || !Token::Match(initExpr->astOperand1(), "%var%")) return false; - std::vector minInitValue = getMinValue(ValueFlow::makeIntegralInferModel(), initExpr->astOperand2()->values()); + std::vector minInitValue = getMinValue(makeIntegralInferModel(), initExpr->astOperand2()->values()); if (minInitValue.empty()) { const ValueFlow::Value* v = initExpr->astOperand2()->getMinValue(true); if (v) diff --git a/lib/cppcheck.vcxproj b/lib/cppcheck.vcxproj index 026a0c06ddb..778bfa1bba4 100644 --- a/lib/cppcheck.vcxproj +++ b/lib/cppcheck.vcxproj @@ -113,6 +113,7 @@ + @@ -222,6 +223,7 @@ + diff --git a/lib/infer.cpp b/lib/infer.cpp index c49931bdade..c5c578c9773 100644 --- a/lib/infer.cpp +++ b/lib/infer.cpp @@ -20,6 +20,7 @@ #include "calculate.h" #include "errortypes.h" +#include "token.h" #include "valueptr.h" #include @@ -29,8 +30,6 @@ #include #include -class Token; - template static const ValueFlow::Value* getCompareValue(const std::list& values, Predicate pred, Compare compare) { @@ -388,3 +387,35 @@ std::vector getMaxValue(const ValuePtr& model, cons return model->match(v); }).maxvalue; } + +namespace { + struct IntegralInferModel : InferModel { + bool match(const ValueFlow::Value& value) const override { + return value.isIntValue(); + } + ValueFlow::Value yield(MathLib::bigint value) const override + { + ValueFlow::Value result(value); + result.valueType = ValueFlow::Value::ValueType::INT; + result.setKnown(); + return result; + } + }; +} + +ValuePtr makeIntegralInferModel() +{ + return IntegralInferModel{}; +} + +ValueFlow::Value inferCondition(const std::string& op, const Token* varTok, MathLib::bigint val) +{ + if (!varTok) + return ValueFlow::Value{}; + if (varTok->hasKnownIntValue()) + return ValueFlow::Value{}; + std::vector r = infer(makeIntegralInferModel(), op, varTok->values(), val); + if (r.size() == 1 && r.front().isKnown()) + return r.front(); + return ValueFlow::Value{}; +} diff --git a/lib/infer.h b/lib/infer.h index 047dfe66b13..ed21d48a6c4 100644 --- a/lib/infer.h +++ b/lib/infer.h @@ -27,6 +27,7 @@ #include #include +class Token; template class ValuePtr; struct InferModel { @@ -56,4 +57,8 @@ std::vector infer(const ValuePtr& model, CPPCHECKLIB std::vector getMinValue(const ValuePtr& model, const std::list& values); std::vector getMaxValue(const ValuePtr& model, const std::list& values); +ValuePtr makeIntegralInferModel(); + +ValueFlow::Value inferCondition(const std::string& op, const Token* varTok, MathLib::bigint val); + #endif diff --git a/lib/lib.pri b/lib/lib.pri index 45ec38bad10..1331bd4735a 100644 --- a/lib/lib.pri +++ b/lib/lib.pri @@ -106,6 +106,7 @@ HEADERS += $${PWD}/addoninfo.h \ $${PWD}/vf_string.h \ $${PWD}/vf_switchvariable.h \ $${PWD}/vf_symbolicinfer.h \ + $${PWD}/vf_symbolicoperators.h \ $${PWD}/vf_unknownfunctionreturn.h \ $${PWD}/vfvalue.h \ $${PWD}/xml.h @@ -198,5 +199,6 @@ SOURCES += $${PWD}/valueflow.cpp \ $${PWD}/vf_string.cpp \ $${PWD}/vf_switchvariable.cpp \ $${PWD}/vf_symbolicinfer.cpp \ + $${PWD}/vf_symbolicoperators.cpp \ $${PWD}/vf_unknownfunctionreturn.cpp \ $${PWD}/vfvalue.cpp diff --git a/lib/programmemory.cpp b/lib/programmemory.cpp index 95fce1bb958..e5781ad2d03 100644 --- a/lib/programmemory.cpp +++ b/lib/programmemory.cpp @@ -1526,13 +1526,13 @@ namespace { if (expr->isComparisonOp() && (r.isUninitValue() || r.isImpossible())) { if (rhs.isIntValue() && !expr->astOperand1()->values().empty()) { std::vector result = - infer(ValueFlow::makeIntegralInferModel(), expr->str(), expr->astOperand1()->values(), {std::move(rhs)}); + infer(makeIntegralInferModel(), expr->str(), expr->astOperand1()->values(), {std::move(rhs)}); if (!result.empty() && result.front().isKnown()) return std::move(result.front()); } if (lhs.isIntValue() && !expr->astOperand2()->values().empty()) { std::vector result = - infer(ValueFlow::makeIntegralInferModel(), expr->str(), {std::move(lhs)}, expr->astOperand2()->values()); + infer(makeIntegralInferModel(), expr->str(), {std::move(lhs)}, expr->astOperand2()->values()); if (!result.empty() && result.front().isKnown()) return std::move(result.front()); } diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index b94c9e05c0e..6ae29a2a6ef 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -2487,22 +2487,6 @@ static bool isTruncated(const ValueType* src, const ValueType* dst, const Settin return false; } -static void setSymbolic(ValueFlow::Value& value, const Token* tok) -{ - assert(tok && tok->exprId() > 0 && "Missing expr id for symbolic value"); - value.valueType = ValueFlow::Value::ValueType::SYMBOLIC; - value.tokvalue = tok; -} - -static ValueFlow::Value makeSymbolic(const Token* tok, MathLib::bigint delta = 0) -{ - ValueFlow::Value value; - value.setKnown(); - setSymbolic(value, tok); - value.intvalue = delta; - return value; -} - static std::set getVarIds(const Token* tok) { std::set result; @@ -2562,13 +2546,13 @@ static void valueFlowSymbolic(const TokenList& tokenlist, const SymbolDatabase& Token* start = nextAfterAstRightmostLeaf(tok); const Token* end = ValueFlow::getEndOfExprScope(tok->astOperand1(), scope); - ValueFlow::Value rhs = makeSymbolic(tok->astOperand2()); + ValueFlow::Value rhs = ValueFlow::makeSymbolic(tok->astOperand2()); rhs.errorPath.emplace_back(tok, tok->astOperand1()->expressionString() + " is assigned '" + tok->astOperand2()->expressionString() + "' here."); valueFlowForward(start, end, tok->astOperand1(), std::move(rhs), tokenlist, errorLogger, settings); - ValueFlow::Value lhs = makeSymbolic(tok->astOperand1()); + ValueFlow::Value lhs = ValueFlow::makeSymbolic(tok->astOperand1()); lhs.errorPath.emplace_back(tok, tok->astOperand1()->expressionString() + " is assigned '" + tok->astOperand2()->expressionString() + "' here."); @@ -2577,130 +2561,6 @@ static void valueFlowSymbolic(const TokenList& tokenlist, const SymbolDatabase& } } -static const Token* isStrlenOf(const Token* tok, const Token* expr, int depth = 10) -{ - if (depth < 0) - return nullptr; - if (!tok) - return nullptr; - if (!expr) - return nullptr; - if (expr->exprId() == 0) - return nullptr; - if (Token::simpleMatch(tok->previous(), "strlen (")) { - if (tok->astOperand2()->exprId() == expr->exprId()) - return tok; - } else { - for (const ValueFlow::Value& v : tok->values()) { - if (!v.isSymbolicValue()) - continue; - if (!v.isKnown()) - continue; - if (v.intvalue != 0) - continue; - if (const Token* next = isStrlenOf(v.tokvalue, expr, depth - 1)) - return next; - } - } - return nullptr; -} - -static ValueFlow::Value inferCondition(const std::string& op, const Token* varTok, MathLib::bigint val); - -static void valueFlowSymbolicOperators(const SymbolDatabase& symboldatabase, const Settings& settings) -{ - for (const Scope* scope : symboldatabase.functionScopes) { - for (auto* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { - if (tok->hasKnownIntValue()) - continue; - - if (Token::Match(tok, "abs|labs|llabs|fabs|fabsf|fabsl (")) { - const Token* arg = tok->next()->astOperand2(); - if (!arg) - continue; - if (arg->exprId() == 0) - continue; - ValueFlow::Value c = inferCondition(">=", arg, 0); - if (!c.isKnown()) - continue; - - ValueFlow::Value v = makeSymbolic(arg); - v.errorPath = c.errorPath; - v.errorPath.emplace_back(tok, "Passed to " + tok->str()); - if (c.intvalue == 0) - v.setImpossible(); - else - v.setKnown(); - setTokenValue(tok->next(), std::move(v), settings); - } else if (Token::Match(tok, "*|/|<<|>>|^|+|-|%or%")) { - if (!tok->astOperand1()) - continue; - if (!tok->astOperand2()) - continue; - if (!astIsIntegral(tok->astOperand1(), false) && !astIsIntegral(tok->astOperand2(), false)) - continue; - const ValueFlow::Value* constant = nullptr; - const Token* vartok = nullptr; - if (tok->astOperand1()->hasKnownIntValue()) { - constant = &tok->astOperand1()->values().front(); - vartok = tok->astOperand2(); - } - if (tok->astOperand2()->hasKnownIntValue()) { - constant = &tok->astOperand2()->values().front(); - vartok = tok->astOperand1(); - } - if (!constant) - continue; - if (!vartok) - continue; - if (vartok->exprId() == 0) - continue; - if (Token::Match(tok, "<<|>>|/") && !astIsLHS(vartok)) - continue; - if (Token::Match(tok, "<<|>>|^|+|-|%or%") && constant->intvalue != 0) - continue; - if (Token::Match(tok, "*|/") && constant->intvalue != 1) - continue; - std::vector values = {makeSymbolic(vartok)}; - std::unordered_set ids = {vartok->exprId()}; - std::copy_if(vartok->values().cbegin(), - vartok->values().cend(), - std::back_inserter(values), - [&](const ValueFlow::Value& v) { - if (!v.isSymbolicValue()) - return false; - if (!v.tokvalue) - return false; - return ids.insert(v.tokvalue->exprId()).second; - }); - for (ValueFlow::Value& v : values) - setTokenValue(tok, std::move(v), settings); - } else if (Token::simpleMatch(tok, "[")) { - const Token* arrayTok = tok->astOperand1(); - const Token* indexTok = tok->astOperand2(); - if (!arrayTok) - continue; - if (!indexTok) - continue; - for (const ValueFlow::Value& value : indexTok->values()) { - if (!value.isSymbolicValue()) - continue; - if (value.intvalue != 0) - continue; - const Token* strlenTok = isStrlenOf(value.tokvalue, arrayTok); - if (!strlenTok) - continue; - ValueFlow::Value v = value; - v.bound = ValueFlow::Value::Bound::Point; - v.valueType = ValueFlow::Value::ValueType::INT; - v.errorPath.emplace_back(strlenTok, "Return index of first '\\0' character in string"); - setTokenValue(tok, std::move(v), settings); - } - } - } - } -} - template static void valueFlowForwardConst(Token* start, const Token* end, @@ -3887,35 +3747,6 @@ struct SimpleConditionHandler : ConditionHandler { } }; -struct IntegralInferModel : InferModel { - bool match(const ValueFlow::Value& value) const override { - return value.isIntValue(); - } - ValueFlow::Value yield(MathLib::bigint value) const override - { - ValueFlow::Value result(value); - result.valueType = ValueFlow::Value::ValueType::INT; - result.setKnown(); - return result; - } -}; - -ValuePtr ValueFlow::makeIntegralInferModel() { - return IntegralInferModel{}; -} - -static ValueFlow::Value inferCondition(const std::string& op, const Token* varTok, MathLib::bigint val) -{ - if (!varTok) - return ValueFlow::Value{}; - if (varTok->hasKnownIntValue()) - return ValueFlow::Value{}; - std::vector r = infer(ValueFlow::makeIntegralInferModel(), op, varTok->values(), val); - if (r.size() == 1 && r.front().isKnown()) - return r.front(); - return ValueFlow::Value{}; -} - struct SymbolicConditionHandler : SimpleConditionHandler { static bool isNegatedBool(const Token* tok) @@ -4950,7 +4781,7 @@ static void valueFlowSmartPointer(TokenList &tokenlist, ErrorLogger & errorLogge v.setKnown(); valueFlowForwardAssign(ftok, tok, std::move(vars), {std::move(v)}, false, tokenlist, errorLogger, settings); } else if (Token::simpleMatch(tok->astParent(), ". get ( )")) { - ValueFlow::Value v = makeSymbolic(tok); + ValueFlow::Value v = ValueFlow::makeSymbolic(tok); setTokenValue(tok->astParent()->tokAt(2), std::move(v), settings); } } else if (Token::Match(tok->previous(), "%name%|> (|{") && astIsSmartPointer(tok) && @@ -5839,7 +5670,7 @@ void ValueFlow::setValues(TokenList& tokenlist, runner.run({ VFA(analyzeImpossibleValues(tokenlist, settings)), - VFA(valueFlowSymbolicOperators(symboldatabase, settings)), + VFA(analyzeSymbolicOperators(symboldatabase, settings)), VFA(valueFlowCondition(SymbolicConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions)), VFA(valueFlowSymbolicInfer(symboldatabase, settings)), VFA(analyzeArrayBool(tokenlist, settings)), diff --git a/lib/valueflow.h b/lib/valueflow.h index ba88e12106e..924f29dbfca 100644 --- a/lib/valueflow.h +++ b/lib/valueflow.h @@ -34,7 +34,6 @@ #include class ErrorLogger; -struct InferModel; class Settings; class SymbolDatabase; class TimerResultsIntf; @@ -44,9 +43,6 @@ class ValueType; class Variable; class Scope; -template -class ValuePtr; - namespace ValueFlow { /// Constant folding of expression. This can be used before the full ValueFlow has been executed (ValueFlow::setValues). const Value * valueFlowConstantFoldAST(Token *expr, const Settings &settings); @@ -104,8 +100,6 @@ namespace ValueFlow { const Token *parseCompareInt(const Token *tok, Value &true_value, Value &false_value, const std::function(const Token*)>& evaluate); const Token *parseCompareInt(const Token *tok, Value &true_value, Value &false_value); - CPPCHECKLIB ValuePtr makeIntegralInferModel(); - const Token* solveExprValue(const Token* expr, const std::function(const Token*)>& eval, Value& value); diff --git a/lib/vf_analyze.h b/lib/vf_analyze.h index 19bf76d01c4..6a1263294bb 100644 --- a/lib/vf_analyze.h +++ b/lib/vf_analyze.h @@ -40,6 +40,7 @@ #include "vf_string.h" // IWYU pragma: export #include "vf_switchvariable.h" // IWYU pragma: export #include "vf_symbolicinfer.h" // IWYU pragma: export +#include "vf_symbolicoperators.h" // IWYU pragma: export #include "vf_unknownfunctionreturn.h" // IWYU pragma: export #endif // vfAnalyzeH diff --git a/lib/vf_common.cpp b/lib/vf_common.cpp index 93016be7d99..cfc2b981712 100644 --- a/lib/vf_common.cpp +++ b/lib/vf_common.cpp @@ -31,6 +31,7 @@ #include "vf_settokenvalue.h" +#include #include #include #include @@ -437,4 +438,20 @@ namespace ValueFlow scope = scope->nestedIn; return scope; } + + void setSymbolic(Value& value, const Token* tok) + { + assert(tok && tok->exprId() > 0 && "Missing expr id for symbolic value"); + value.valueType = Value::ValueType::SYMBOLIC; + value.tokvalue = tok; + } + + Value makeSymbolic(const Token* tok, MathLib::bigint delta) + { + Value value; + value.setKnown(); + setSymbolic(value, tok); + value.intvalue = delta; + return value; + } } diff --git a/lib/vf_common.h b/lib/vf_common.h index f542b51a527..6600e167a80 100644 --- a/lib/vf_common.h +++ b/lib/vf_common.h @@ -59,6 +59,10 @@ namespace ValueFlow bool isBreakOrContinueScope(const Token* endToken); const Scope* getLoopScope(const Token* tok); + + void setSymbolic(Value& value, const Token* tok); + + Value makeSymbolic(const Token* tok, MathLib::bigint delta = 0); } #endif // vfCommonH diff --git a/lib/vf_symbolicoperators.cpp b/lib/vf_symbolicoperators.cpp new file mode 100644 index 00000000000..54a6fce1ea8 --- /dev/null +++ b/lib/vf_symbolicoperators.cpp @@ -0,0 +1,162 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vf_symbolicoperators.h" + +#include "astutils.h" +#include "config.h" +#include "infer.h" +#include "symboldatabase.h" +#include "token.h" +#include "vfvalue.h" + +#include "vf_common.h" +#include "vf_settokenvalue.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace ValueFlow +{ + static const Token* isStrlenOf(const Token* tok, const Token* expr, int depth = 10) + { + if (depth < 0) + return nullptr; + if (!tok) + return nullptr; + if (!expr) + return nullptr; + if (expr->exprId() == 0) + return nullptr; + if (Token::simpleMatch(tok->previous(), "strlen (")) { + if (tok->astOperand2()->exprId() == expr->exprId()) + return tok; + } else { + for (const Value& v : tok->values()) { + if (!v.isSymbolicValue()) + continue; + if (!v.isKnown()) + continue; + if (v.intvalue != 0) + continue; + if (const Token* next = isStrlenOf(v.tokvalue, expr, depth - 1)) + return next; + } + } + return nullptr; + } + + void analyzeSymbolicOperators(const SymbolDatabase& symboldatabase, const Settings& settings) + { + for (const Scope* scope : symboldatabase.functionScopes) { + for (auto* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { + if (tok->hasKnownIntValue()) + continue; + + if (Token::Match(tok, "abs|labs|llabs|fabs|fabsf|fabsl (")) { + const Token* arg = tok->next()->astOperand2(); + if (!arg) + continue; + if (arg->exprId() == 0) + continue; + Value c = inferCondition(">=", arg, 0); + if (!c.isKnown()) + continue; + + Value v = makeSymbolic(arg); + v.errorPath = c.errorPath; + v.errorPath.emplace_back(tok, "Passed to " + tok->str()); + if (c.intvalue == 0) + v.setImpossible(); + else + v.setKnown(); + setTokenValue(tok->next(), std::move(v), settings); + } else if (Token::Match(tok, "*|/|<<|>>|^|+|-|%or%")) { + if (!tok->astOperand1()) + continue; + if (!tok->astOperand2()) + continue; + if (!astIsIntegral(tok->astOperand1(), false) && !astIsIntegral(tok->astOperand2(), false)) + continue; + const Value* constant = nullptr; + const Token* vartok = nullptr; + if (tok->astOperand1()->hasKnownIntValue()) { + constant = &tok->astOperand1()->values().front(); + vartok = tok->astOperand2(); + } + if (tok->astOperand2()->hasKnownIntValue()) { + constant = &tok->astOperand2()->values().front(); + vartok = tok->astOperand1(); + } + if (!constant) + continue; + if (!vartok) + continue; + if (vartok->exprId() == 0) + continue; + if (Token::Match(tok, "<<|>>|/") && !astIsLHS(vartok)) + continue; + if (Token::Match(tok, "<<|>>|^|+|-|%or%") && constant->intvalue != 0) + continue; + if (Token::Match(tok, "*|/") && constant->intvalue != 1) + continue; + std::vector values = {makeSymbolic(vartok)}; + std::unordered_set ids = {vartok->exprId()}; + std::copy_if(vartok->values().cbegin(), + vartok->values().cend(), + std::back_inserter(values), + [&](const Value& v) { + if (!v.isSymbolicValue()) + return false; + if (!v.tokvalue) + return false; + return ids.insert(v.tokvalue->exprId()).second; + }); + for (Value& v : values) + setTokenValue(tok, std::move(v), settings); + } else if (Token::simpleMatch(tok, "[")) { + const Token* arrayTok = tok->astOperand1(); + const Token* indexTok = tok->astOperand2(); + if (!arrayTok) + continue; + if (!indexTok) + continue; + for (const Value& value : indexTok->values()) { + if (!value.isSymbolicValue()) + continue; + if (value.intvalue != 0) + continue; + const Token* strlenTok = isStrlenOf(value.tokvalue, arrayTok); + if (!strlenTok) + continue; + Value v = value; + v.bound = Value::Bound::Point; + v.valueType = Value::ValueType::INT; + v.errorPath.emplace_back(strlenTok, "Return index of first '\\0' character in string"); + setTokenValue(tok, std::move(v), settings); + } + } + } + } + } +} diff --git a/lib/vf_symbolicoperators.h b/lib/vf_symbolicoperators.h new file mode 100644 index 00000000000..624f2d160da --- /dev/null +++ b/lib/vf_symbolicoperators.h @@ -0,0 +1,30 @@ +/* -*- C++ -*- + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef vfSymbolicOperatorsH +#define vfSymbolicOperatorsH + +class SymbolDatabase; +class Settings; + +namespace ValueFlow +{ + void analyzeSymbolicOperators(const SymbolDatabase& symboldatabase, const Settings& settings); +} + +#endif // vfSymbolicOperatorsH diff --git a/oss-fuzz/Makefile b/oss-fuzz/Makefile index 3f5add0ed72..a6c95704841 100644 --- a/oss-fuzz/Makefile +++ b/oss-fuzz/Makefile @@ -121,6 +121,7 @@ LIBOBJ = $(libcppdir)/valueflow.o \ $(libcppdir)/vf_string.o \ $(libcppdir)/vf_switchvariable.o \ $(libcppdir)/vf_symbolicinfer.o \ + $(libcppdir)/vf_symbolicoperators.o \ $(libcppdir)/vf_unknownfunctionreturn.o \ $(libcppdir)/vfvalue.o @@ -166,7 +167,7 @@ simplecpp.o: ../externals/simplecpp/simplecpp.cpp ../externals/simplecpp/simplec tinyxml2.o: ../externals/tinyxml2/tinyxml2.cpp ../externals/tinyxml2/tinyxml2.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -w -c -o $@ ../externals/tinyxml2/tinyxml2.cpp -$(libcppdir)/valueflow.o: ../lib/valueflow.cpp ../lib/addoninfo.h ../lib/analyzer.h ../lib/astutils.h ../lib/check.h ../lib/checkuninitvar.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/findtoken.h ../lib/forwardanalyzer.h ../lib/infer.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/programmemory.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/timer.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/valueptr.h ../lib/vf_analyze.h ../lib/vf_analyzers.h ../lib/vf_array.h ../lib/vf_arraybool.h ../lib/vf_arrayelement.h ../lib/vf_bailout.h ../lib/vf_bitand.h ../lib/vf_common.h ../lib/vf_conditionexpressions.h ../lib/vf_debug.h ../lib/vf_enumvalue.h ../lib/vf_functionreturn.h ../lib/vf_globalconstvar.h ../lib/vf_globalstaticvar.h ../lib/vf_impossiblevalues.h ../lib/vf_infercondition.h ../lib/vf_iteratorinfer.h ../lib/vf_iterators.h ../lib/vf_number.h ../lib/vf_pointeralias.h ../lib/vf_reverse.h ../lib/vf_rightshift.h ../lib/vf_sameexpressions.h ../lib/vf_settokenvalue.h ../lib/vf_string.h ../lib/vf_switchvariable.h ../lib/vf_symbolicinfer.h ../lib/vf_unknownfunctionreturn.h ../lib/vfvalue.h +$(libcppdir)/valueflow.o: ../lib/valueflow.cpp ../lib/addoninfo.h ../lib/analyzer.h ../lib/astutils.h ../lib/check.h ../lib/checkuninitvar.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/findtoken.h ../lib/forwardanalyzer.h ../lib/infer.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/programmemory.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/timer.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/valueptr.h ../lib/vf_analyze.h ../lib/vf_analyzers.h ../lib/vf_array.h ../lib/vf_arraybool.h ../lib/vf_arrayelement.h ../lib/vf_bailout.h ../lib/vf_bitand.h ../lib/vf_common.h ../lib/vf_conditionexpressions.h ../lib/vf_debug.h ../lib/vf_enumvalue.h ../lib/vf_functionreturn.h ../lib/vf_globalconstvar.h ../lib/vf_globalstaticvar.h ../lib/vf_impossiblevalues.h ../lib/vf_infercondition.h ../lib/vf_iteratorinfer.h ../lib/vf_iterators.h ../lib/vf_number.h ../lib/vf_pointeralias.h ../lib/vf_reverse.h ../lib/vf_rightshift.h ../lib/vf_sameexpressions.h ../lib/vf_settokenvalue.h ../lib/vf_string.h ../lib/vf_switchvariable.h ../lib/vf_symbolicinfer.h ../lib/vf_symbolicoperators.h ../lib/vf_unknownfunctionreturn.h ../lib/vfvalue.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/valueflow.cpp $(libcppdir)/tokenize.o: ../lib/tokenize.cpp ../externals/simplecpp/simplecpp.h ../lib/addoninfo.h ../lib/astutils.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/preprocessor.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/summaries.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/timer.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/vfvalue.h @@ -295,7 +296,7 @@ $(libcppdir)/fwdanalysis.o: ../lib/fwdanalysis.cpp ../lib/addoninfo.h ../lib/ast $(libcppdir)/importproject.o: ../lib/importproject.cpp ../externals/picojson/picojson.h ../externals/tinyxml2/tinyxml2.h ../lib/addoninfo.h ../lib/config.h ../lib/errortypes.h ../lib/filesettings.h ../lib/importproject.h ../lib/json.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/settings.h ../lib/standards.h ../lib/suppressions.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h ../lib/xml.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/importproject.cpp -$(libcppdir)/infer.o: ../lib/infer.cpp ../lib/calculate.h ../lib/config.h ../lib/errortypes.h ../lib/infer.h ../lib/mathlib.h ../lib/valueptr.h ../lib/vfvalue.h +$(libcppdir)/infer.o: ../lib/infer.cpp ../lib/calculate.h ../lib/config.h ../lib/errortypes.h ../lib/infer.h ../lib/mathlib.h ../lib/templatesimplifier.h ../lib/token.h ../lib/utils.h ../lib/valueptr.h ../lib/vfvalue.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/infer.cpp $(libcppdir)/keywords.o: ../lib/keywords.cpp ../lib/config.h ../lib/keywords.h ../lib/standards.h ../lib/utils.h @@ -430,6 +431,9 @@ $(libcppdir)/vf_switchvariable.o: ../lib/vf_switchvariable.cpp ../lib/addoninfo. $(libcppdir)/vf_symbolicinfer.o: ../lib/vf_symbolicinfer.cpp ../lib/astutils.h ../lib/config.h ../lib/errortypes.h ../lib/infer.h ../lib/library.h ../lib/mathlib.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/utils.h ../lib/valueptr.h ../lib/vf_settokenvalue.h ../lib/vf_symbolicinfer.h ../lib/vfvalue.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_symbolicinfer.cpp +$(libcppdir)/vf_symbolicoperators.o: ../lib/vf_symbolicoperators.cpp ../lib/astutils.h ../lib/config.h ../lib/errortypes.h ../lib/infer.h ../lib/library.h ../lib/mathlib.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/utils.h ../lib/vf_common.h ../lib/vf_settokenvalue.h ../lib/vf_symbolicoperators.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_symbolicoperators.cpp + $(libcppdir)/vf_unknownfunctionreturn.o: ../lib/vf_unknownfunctionreturn.cpp ../lib/addoninfo.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenlist.h ../lib/utils.h ../lib/vf_common.h ../lib/vf_settokenvalue.h ../lib/vf_unknownfunctionreturn.h ../lib/vfvalue.h $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vf_unknownfunctionreturn.cpp