Skip to content

Commit b421be9

Browse files
committed
Fix review comments
1 parent 9c933a0 commit b421be9

5 files changed

Lines changed: 282 additions & 21 deletions

File tree

‎lib/programmemory.cpp‎

Lines changed: 126 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,20 @@ static bool isBounded(const ValueFlow::Value& value)
853853
return value.bound != ValueFlow::Value::Bound::Point;
854854
}
855855

856+
static bool addOverflows(MathLib::bigint x, MathLib::bigint y)
857+
{
858+
if (y > 0)
859+
return x > std::numeric_limits<MathLib::bigint>::max() - y;
860+
return x < std::numeric_limits<MathLib::bigint>::min() - y;
861+
}
862+
863+
static bool subtractOverflows(MathLib::bigint x, MathLib::bigint y)
864+
{
865+
if (y < 0)
866+
return x > std::numeric_limits<MathLib::bigint>::max() + y;
867+
return x < std::numeric_limits<MathLib::bigint>::min() + y;
868+
}
869+
856870
static bool multiplyOverflows(MathLib::bigint x, MathLib::bigint y)
857871
{
858872
if (x == 0 || y == 0)
@@ -862,6 +876,69 @@ static bool multiplyOverflows(MathLib::bigint x, MathLib::bigint y)
862876
return std::abs(x) > std::numeric_limits<MathLib::bigint>::max() / std::abs(y);
863877
}
864878

879+
// Does the left shift of a non-negative value overflow? The shift must be valid.
880+
static bool shiftOverflows(MathLib::bigint x, MathLib::bigint shift)
881+
{
882+
const MathLib::biguint limit = static_cast<MathLib::biguint>(std::numeric_limits<MathLib::bigint>::max()) >>
883+
static_cast<unsigned>(shift);
884+
return static_cast<MathLib::biguint>(x) > limit;
885+
}
886+
887+
// The largest value of the unsigned type of the expression, or -1 when the type is not known
888+
static MathLib::bigint unsignedMax(const Token* expr, const Settings& settings)
889+
{
890+
const ValueType* vt = expr->valueType();
891+
if (!vt)
892+
return -1;
893+
const std::size_t size = vt->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointer);
894+
if (size == 0)
895+
return -1;
896+
if (size >= sizeof(MathLib::bigint))
897+
return std::numeric_limits<MathLib::bigint>::max();
898+
return static_cast<MathLib::bigint>((MathLib::biguint(1) << (8 * size)) - 1);
899+
}
900+
901+
// Does "x <op> k" (or "k <op> x", when the range is the right operand) wrap around in the unsigned
902+
// type of the expression for some value x that the values allow? The values are either one value or
903+
// constraints. A signed expression does not wrap: its overflow is undefined.
904+
static bool mayWrap(const ProgramMemory::Values& values,
905+
const Token* expr,
906+
const std::string& op,
907+
MathLib::bigint k,
908+
bool rangeIsLhs,
909+
const Settings& settings)
910+
{
911+
if (!astIsUnsigned(expr))
912+
return false;
913+
const MathLib::bigint max = unsignedMax(expr, settings);
914+
if (max < 0 || ValueFlow::isSaturated(k))
915+
return true;
916+
// The smallest and the largest value that the values allow
917+
MathLib::bigint low = 0;
918+
MathLib::bigint high = max;
919+
for (const ValueFlow::Value& v : values) {
920+
if (!v.isImpossible())
921+
low = high = v.intvalue;
922+
else if (isLowerBound(v))
923+
low = std::max(low, v.rangeEdge());
924+
else if (isUpperBound(v))
925+
high = std::min(high, v.rangeEdge());
926+
}
927+
if (op == "+")
928+
return k >= 0 ? high > max - k : low < -k;
929+
if (op == "-") {
930+
if (!rangeIsLhs)
931+
return k < 0 || high > k;
932+
return k >= 0 ? low<k : high> max + k;
933+
}
934+
if (op == "*")
935+
return k < 0 || multiplyOverflows(high, k) || high * k > max;
936+
if (op == "<<")
937+
return k < 0 || k >= 63 || shiftOverflows(high, k) ||
938+
static_cast<MathLib::bigint>(static_cast<MathLib::biguint>(high) << static_cast<unsigned>(k)) > max;
939+
return false;
940+
}
941+
865942
// The operations that keep the order of the values of a range
866943
static bool isMonotone(const std::string& op)
867944
{
@@ -873,15 +950,20 @@ static bool isMonotone(const std::string& op)
873950
// values turns the range around.
874951
static ValueFlow::Value applyToRange(const std::string& op, const ValueFlow::Value& range, MathLib::bigint k, bool rangeIsLhs)
875952
{
876-
const MathLib::bigint edge = range.rangeEdge();
877-
if (ValueFlow::isSaturated(edge) || ValueFlow::isSaturated(k))
953+
// A bound at the limit of the type has no edge beyond it
954+
if (ValueFlow::isSaturated(range.intvalue) || ValueFlow::isSaturated(k))
878955
return ValueFlow::Value::unknown();
956+
const MathLib::bigint edge = range.rangeEdge();
879957
bool increasing = true;
880958
MathLib::bigint result = 0;
881959
if (op == "+") {
960+
if (addOverflows(edge, k))
961+
return ValueFlow::Value::unknown();
882962
result = edge + k;
883963
} else if (op == "-") {
884964
increasing = rangeIsLhs;
965+
if (rangeIsLhs ? subtractOverflows(edge, k) : subtractOverflows(k, edge))
966+
return ValueFlow::Value::unknown();
885967
result = rangeIsLhs ? edge - k : k - edge;
886968
} else if (op == "*") {
887969
if (k == 0 || multiplyOverflows(edge, k))
@@ -895,12 +977,18 @@ static ValueFlow::Value applyToRange(const std::string& op, const ValueFlow::Val
895977
increasing = k > 0;
896978
result = edge / k;
897979
} else {
898-
// Shifts: calculate() rejects a negative or too large shift and a negative value
980+
// Shifts: calculate() rejects a negative or too large shift and a negative value, but it does
981+
// not check that a left shift fits
982+
if (!rangeIsLhs || (op == "<<" && (edge < 0 || k < 0 || k >= 63 || shiftOverflows(edge, k))))
983+
return ValueFlow::Value::unknown();
899984
bool error = false;
900985
result = calculate(op, edge, k, &error);
901-
if (!rangeIsLhs || error || (op == "<<" && (result >> k) != edge))
986+
if (error)
902987
return ValueFlow::Value::unknown();
903988
}
989+
// The bound is one off the edge of the result
990+
if (ValueFlow::isSaturated(result))
991+
return ValueFlow::Value::unknown();
904992
ValueFlow::Value scaled = range;
905993
scaled.setRangeEdge(result, range.isLowerEdge() == increasing);
906994
return scaled;
@@ -1658,7 +1746,7 @@ namespace {
16581746
// known value and does not depend on a tracked value
16591747
const Values* getStoredValues(const Token* expr) const
16601748
{
1661-
if (expr->exprId() == 0)
1749+
if (!expr || expr->exprId() == 0)
16621750
return nullptr;
16631751
const Values* stored = pm->getValues(expr->exprId());
16641752
if (!stored || expr->hasKnownIntValue() || dependsOnTrackedValue(expr))
@@ -1701,6 +1789,8 @@ namespace {
17011789

17021790
ValueFlow::Value executeMultiCondition(bool b, const Token* expr)
17031791
{
1792+
if (!expr)
1793+
return unknown();
17041794
if (const ValueFlow::Value* v = pm->getValue(expr->exprId(), /*impossible*/ true)) {
17051795
if (v->isIntValue())
17061796
return *v;
@@ -1806,11 +1896,14 @@ namespace {
18061896
continue;
18071897
if (value.tokvalue->exprId() == 0)
18081898
continue;
1809-
const ValueFlow::Value* sizeValue = pm->getValue(value.tokvalue->exprId());
1810-
if (sizeValue && sizeValue->isContainerSizeValue()) {
1811-
sizes.push_back(*sizeValue);
1899+
const Values* aliasValues = pm->getValues(value.tokvalue->exprId());
1900+
if (!aliasValues)
1901+
continue;
1902+
std::copy_if(aliasValues->cbegin(), aliasValues->cend(), std::back_inserter(sizes), [](const ValueFlow::Value& v) {
1903+
return v.isContainerSizeValue();
1904+
});
1905+
if (!sizes.empty())
18121906
break;
1813-
}
18141907
}
18151908
return sizes;
18161909
}
@@ -1865,6 +1958,12 @@ namespace {
18651958
// Constraints of the right hand side cannot be combined with the values
18661959
const ValueFlow::Value& delta = rhs.front();
18671960
Values& lhs = pm->at(expr->astOperand1()->exprId());
1961+
// An unsigned value that may wrap around is lost
1962+
const std::string op = expr->str().substr(0, expr->str().size() - 1);
1963+
if (delta.isIntValue() && mayWrap(lhs, expr->astOperand1(), op, delta.intvalue, true, settings)) {
1964+
lhs.assign(1, unknown());
1965+
return {};
1966+
}
18681967
for (ValueFlow::Value& v : lhs) {
18691968
const ValueFlow::Value r = evaluate(expr, v, delta, /*removeAssign*/ true);
18701969
if (r.isUninitValue()) {
@@ -1898,12 +1997,14 @@ namespace {
18981997
// The values of an expression all have the same type
18991998
if (!lhs.front().isIntValue())
19001999
return {};
1901-
// An unsigned value wraps around when it is decremented and may be zero
1902-
if (expr->str() == "--" && astIsUnsigned(expr->astOperand1()) && !isTrue(lhs)) {
2000+
// An unsigned value that may wrap around is lost, as is a value at the limit of the type
2001+
if (mayWrap(lhs, expr->astOperand1(), expr->str() == "++" ? "+" : "-", 1, true, settings) ||
2002+
std::any_of(lhs.cbegin(), lhs.cend(), [](const ValueFlow::Value& v) {
2003+
return ValueFlow::isSaturated(v.intvalue);
2004+
})) {
19032005
lhs.assign(1, unknown());
19042006
return {};
19052007
}
1906-
19072008
// Shift every value of the variable; bounds and impossible values move along
19082009
for (ValueFlow::Value& v : lhs) {
19092010
if (expr->str() == "++")
@@ -1949,6 +2050,9 @@ namespace {
19492050
// Apply the operation to each constraint; a constraint it cannot transform is dropped
19502051
const Values& constraints = lhsConstraints ? lhsValues : rhsValues;
19512052
const ValueFlow::Value& other = lhsConstraints ? rhsValues.front() : lhsValues.front();
2053+
// The range of an unsigned expression that may wrap around is lost
2054+
if (other.isIntValue() && mayWrap(constraints, expr, expr->str(), other.intvalue, lhsConstraints, settings))
2055+
return {};
19522056
Values result;
19532057
for (const ValueFlow::Value& constraint : constraints) {
19542058
ValueFlow::Value r = lhsConstraints ? evaluate(expr, constraint, other) : evaluate(expr, other, constraint);
@@ -2179,9 +2283,16 @@ namespace {
21792283
const Values* stored = pm->getValues(value.tokvalue->exprId());
21802284
if (!stored || (!stored->front().isIntValue() && value.intvalue != 0))
21812285
continue;
2182-
ValueFlow::Value v2 = stored->front();
2183-
v2.intvalue += value.intvalue;
2184-
return single(std::move(v2));
2286+
// The values of the expression this one is symbolically equal to, moved by the offset
2287+
Values shifted;
2288+
for (const ValueFlow::Value& v : *stored) {
2289+
if (addOverflows(v.intvalue, value.intvalue))
2290+
continue;
2291+
shifted.push_back(v);
2292+
shifted.back().intvalue += value.intvalue;
2293+
}
2294+
if (!shifted.empty())
2295+
return shifted;
21852296
}
21862297
if (!values.empty() && values.front().isIntValue())
21872298
return values;

‎lib/token.cpp‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include <functional>
4040
#include <iostream>
4141
#include <iterator>
42+
#include <limits>
4243
#include <map>
4344
#include <set>
4445
#include <sstream>
@@ -2021,7 +2022,9 @@ static bool isAdjacent(const ValueFlow::Value& x, const ValueFlow::Value& y)
20212022
return true;
20222023
if (x.valueType == ValueFlow::Value::ValueType::FLOAT)
20232024
return false;
2024-
return std::abs(x.intvalue - y.intvalue) == 1;
2025+
// Compared without a difference, which could overflow
2026+
const MathLib::bigint max = std::numeric_limits<MathLib::bigint>::max();
2027+
return (y.intvalue != max && x.intvalue == y.intvalue + 1) || (x.intvalue != max && y.intvalue == x.intvalue + 1);
20252028
}
20262029

20272030
static bool removePointValue(std::list<ValueFlow::Value>& values, std::list<ValueFlow::Value>::iterator& x)

‎lib/valueflow.cpp‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6313,7 +6313,8 @@ const Token* ValueFlow::solveExprValue(const Token* expr,
63136313
return ValueFlow::solveExprValue(binaryTok, eval, value);
63146314
}
63156315
case '*': {
6316-
if (intval == 0 || ValueFlow::isSaturated(value.intvalue))
6316+
// A bound at the limit of the type cannot be solved, nor can its edge be divided safely
6317+
if (intval == 0 || ValueFlow::isSaturated(value.intvalue) || ValueFlow::isSaturated(value.rangeEdge()))
63176318
break;
63186319
if (value.bound == ValueFlow::Value::Bound::Point) {
63196320
// x * k is v only for a v that k divides

‎lib/vfvalue.h‎

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <cmath>
3030
#include <cstdint>
3131
#include <functional>
32+
#include <limits>
3233
#include <string>
3334
#include <type_traits>
3435
#include <utility>
@@ -202,18 +203,29 @@ namespace ValueFlow
202203
return (bound == Bound::Lower) != isImpossible();
203204
}
204205

205-
/** The first value inside the range of a value with a bound */
206+
/**
207+
* The first value inside the range of a value with a bound. A bound at the limit of the type
208+
* stays there, as there is no value beyond it.
209+
*/
206210
MathLib::bigint rangeEdge() const {
207211
if (!isImpossible())
208212
return intvalue;
209-
return isLowerEdge() ? intvalue + 1 : intvalue - 1;
213+
if (isLowerEdge())
214+
return intvalue == std::numeric_limits<MathLib::bigint>::max() ? intvalue : intvalue + 1;
215+
return intvalue == std::numeric_limits<MathLib::bigint>::min() ? intvalue : intvalue - 1;
210216
}
211217

212-
/** Let the range start (lower edge) or end at the given value, keeping the kind of the value */
218+
/**
219+
* Let the range start (lower edge) or end at the given value, keeping the kind of the value.
220+
* An edge at the limit of the type stays there.
221+
*/
213222
void setRangeEdge(MathLib::bigint edge, bool lowerEdge) {
214223
if (isImpossible()) {
215224
bound = lowerEdge ? Bound::Upper : Bound::Lower;
216-
intvalue = lowerEdge ? edge - 1 : edge + 1;
225+
if (lowerEdge)
226+
intvalue = edge == std::numeric_limits<MathLib::bigint>::min() ? edge : edge - 1;
227+
else
228+
intvalue = edge == std::numeric_limits<MathLib::bigint>::max() ? edge : edge + 1;
217229
} else {
218230
bound = lowerEdge ? Bound::Lower : Bound::Upper;
219231
intvalue = edge;

0 commit comments

Comments
 (0)