Merge pull request #13949 from owen-mc/go/change-flowstate-for-incorrect-integer-conversion

Go: Improve incorrect integer conversion
This commit is contained in:
Owen Mansel-Chan
2023-10-05 09:59:36 +01:00
committed by GitHub
11 changed files with 410 additions and 139 deletions

View File

@@ -117,6 +117,9 @@ module IntegerParser {
* input is 0 then it means the bit size of `int` and `uint`.
*/
FunctionInput getTargetBitSizeInput() { none() }
/** Gets whether the function is for parsing signed or unsigned integers. */
boolean isSigned() { none() }
}
}

View File

@@ -11,6 +11,8 @@ module Strconv {
Atoi() { this.hasQualifiedName("strconv", "Atoi") }
override int getTargetBitSize() { result = 0 }
override boolean isSigned() { result = true }
}
/** The `ParseInt` function. */
@@ -18,6 +20,8 @@ module Strconv {
ParseInt() { this.hasQualifiedName("strconv", "ParseInt") }
override FunctionInput getTargetBitSizeInput() { result.isParameter(2) }
override boolean isSigned() { result = true }
}
/** The `ParseUint` function. */
@@ -25,6 +29,8 @@ module Strconv {
ParseUint() { this.hasQualifiedName("strconv", "ParseUint") }
override FunctionInput getTargetBitSizeInput() { result.isParameter(2) }
override boolean isSigned() { result = false }
}
/**

View File

@@ -14,14 +14,15 @@ float getMaxIntValue(int bitSize, boolean isSigned) {
}
/**
* Get the size of `int` or `uint` in `file`, or 0 if it is
* architecture-specific.
* Get the size of `int` or `uint` in `file`, or
* `architectureSpecificBitSize` if it is architecture-specific.
*/
int getIntTypeBitSize(File file) {
bindingset[architectureSpecificBitSize]
int getIntTypeBitSize(File file, int architectureSpecificBitSize) {
file.constrainsIntBitSize(result)
or
not file.constrainsIntBitSize(_) and
result = 0
result = architectureSpecificBitSize
}
/**
@@ -90,7 +91,7 @@ deprecated class ConversionWithoutBoundsCheckConfig extends TaintTracking::Confi
) and
(
if apparentBitSize = 0
then effectiveBitSize = getIntTypeBitSize(source.getFile())
then effectiveBitSize = getIntTypeBitSize(source.getFile(), 0)
else effectiveBitSize = apparentBitSize
) and
// `effectiveBitSize` could be any value between 0 and 64, but we
@@ -113,7 +114,7 @@ deprecated class ConversionWithoutBoundsCheckConfig extends TaintTracking::Confi
bitSize = integerType.getSize()
or
not exists(integerType.getSize()) and
bitSize = getIntTypeBitSize(sink.getFile())
bitSize = getIntTypeBitSize(sink.getFile(), 0)
) and
if integerType instanceof SignedIntegerType then sinkIsSigned = true else sinkIsSigned = false
) and
@@ -140,7 +141,7 @@ deprecated class ConversionWithoutBoundsCheckConfig extends TaintTracking::Confi
if sinkBitSize != 0 then bitSize = sinkBitSize else bitSize = 32
|
node = DataFlow::BarrierGuard<upperBoundCheckGuard/3>::getABarrierNodeForGuard(g) and
g.isBoundFor(bitSize, sinkIsSigned)
if sinkIsSigned = true then g.isBoundFor(bitSize, 32) else g.isBoundFor(bitSize - 1, 32)
)
or
exists(int bitSize |
@@ -150,49 +151,261 @@ deprecated class ConversionWithoutBoundsCheckConfig extends TaintTracking::Confi
}
}
/** Flow state for ConversionWithoutBoundsCheckConfig. */
newtype IntegerConversionFlowState =
/** Keep track of info about the source and potential sinks. */
TFlowstate(boolean sinkIsSigned, int sourceBitSize, int sinkBitSize) {
sinkIsSigned in [true, false] and
isIncorrectIntegerConversion(sourceBitSize, sinkBitSize)
private int validBitSize() { result = [7, 8, 15, 16, 31, 32, 63, 64] }
private newtype TArchitectureBitSize =
TMk32Bit() or
TMk64Bit() or
TMkUnknown()
private class ArchitectureBitSize extends TArchitectureBitSize {
/** Gets an integer for the architecture bit size, if known. */
int toInt() {
this = TMk32Bit() and result = 32
or
this = TMk64Bit() and result = 64
}
/** Gets the bit size of the source. */
int getSourceBitSize(IntegerConversionFlowState state) { state = TFlowstate(_, result, _) }
/** Holds if the architecture bit size is unknown. */
predicate isUnknown() { this = TMkUnknown() }
/** Gets a textual representation of this element. */
string toString() {
result = this.toInt() + "-bit"
or
this.isUnknown() and result = "unknown"
}
}
private newtype TMaxValueState =
TMkMaxValueState(int bitSize, ArchitectureBitSize architectureBitSize) {
bitSize = validBitSize()
}
/** Flow state for ConversionWithoutBoundsCheckConfig. */
private class MaxValueState extends TMaxValueState {
/**
* Gets the smallest bitsize where the maximum value that could get to this
* point fits into an integer type whose maximum value is 2^(result) - 1.
*
* For example, if we know `1 << 12` can get to a particular program point,
* then the result would be 15, since a 16-bit signed integer can represent
* that value and that type has maximum value 2^15 -1. An unsigned 8-bit
* integer cannot represent that value as its maximum value is 2^8 - 1.
*/
int getBitSize() { this = TMkMaxValueState(result, _) }
/** Gets whether the architecture is 32 bit or 64 bit, or if it is unknown. */
ArchitectureBitSize getArchitectureBitSize() { this = TMkMaxValueState(_, result) }
/**
* Gets the bitsize we should use for a sink.
*
* If the architecture bit size is known, then we should use that. Otherwise,
* we should use 32 bits, because that will find results that only exist on
* 32-bit architectures.
*/
bindingset[default]
int getSinkBitSize(int default) {
if this = TMkMaxValueState(_, TMk64Bit()) then result = 64 else result = default
}
/** Gets a textual representation of this element. */
string toString() {
exists(string suffix |
suffix = " (on " + this.getArchitectureBitSize().toInt() + "-bit architecture)"
or
this.getArchitectureBitSize().isUnknown() and suffix = ""
|
result = "MaxValueState(max value <= 2^(" + this.getBitSize() + ")-1" + suffix
)
}
}
/**
* A node that blocks some flow states and transforms some others as they flow
* through it.
*/
abstract class BarrierFlowStateTransformer extends DataFlow::Node {
/**
* Holds if this should be a barrier for `flowstate`.
*
* This includes flow states which are transformed into other flow states.
*/
abstract predicate barrierFor(MaxValueState flowstate);
/**
* Gets the flow state that `flowstate` is transformed into.
*
* Due to limitations of the implementation the transformation defined by this
* predicate must be idempotent, that is, for any input `x` it must be that:
* ```
* transform(transform(x)) = transform(x)
* ```
*/
abstract MaxValueState transform(MaxValueState flowstate);
}
private predicate upperBoundCheckGuard(DataFlow::Node g, Expr e, boolean branch) {
g.(UpperBoundCheckGuard).checks(e, branch)
}
/** An upper bound check that compares a variable to a constant value. */
class UpperBoundCheckGuard extends DataFlow::RelationalComparisonNode {
UpperBoundCheckGuard() {
count(expr.getAnOperand().getExactValue()) = 1 and
expr.getAnOperand().getType().getUnderlyingType() instanceof IntegerType
}
/**
* Holds if this upper bound check ensures the non-constant operand is less
* than or equal to `2^(bitsize) - 1`. In this case, the upper bound check
* is a barrier guard. `architectureBitSize` is used if the constant operand
* is `math.MaxInt` or `math.MaxUint`.
*
* Note that we have to use floats here because integers in CodeQL are
* represented by 32-bit signed integers, which cannot represent some of the
* integer values which we will encounter.
*/
predicate isBoundFor(int bitSize, int architectureBitSize) {
bitSize = validBitSize() and
architectureBitSize = [32, 64] and
exists(float bound, float strictnessOffset |
// For `x < c` the bound is `c-1`. For `x >= c` we will be an upper bound
// on the `branch` argument of `checks` is false, which is equivalent to
// `x < c`.
if expr instanceof LssExpr or expr instanceof GeqExpr
then strictnessOffset = 1
else strictnessOffset = 0
|
exists(DeclaredConstant maxint, DeclaredConstant maxuint |
maxint.hasQualifiedName("math", "MaxInt") and maxuint.hasQualifiedName("math", "MaxUint")
|
if expr.getAnOperand() = maxint.getAReference()
then bound = getMaxIntValue(architectureBitSize, true)
else
if expr.getAnOperand() = maxuint.getAReference()
then bound = getMaxIntValue(architectureBitSize, false)
else bound = expr.getAnOperand().getExactValue().toFloat()
) and
bound - strictnessOffset < 2.pow(bitSize) - 1
)
}
/** Holds if this guard validates `e` upon evaluating to `branch`. */
predicate checks(Expr e, boolean branch) {
this.leq(branch, DataFlow::exprNode(e), _, _) and
not e.isConst()
}
}
/**
* A node that is safely guarded by an `UpperBoundCheckGuard`.
*
* When this guarantees that a variable in the non-constant operand is less
* than some value this may be a barrier guard which should block some flow
* states and transform some others as they flow through.
*
* For example, in the following code:
* ```go
* if parsed <= math.MaxInt16 {
* _ = uint16(parsed)
* }
* ```
* `parsed < math.MaxInt16` is an `UpperBoundCheckGuard` and `uint16(parsed)`
* is an `UpperBoundCheck` that would be a barrier for flow states with bit
* size greater than 15 and would transform them to a flow state with bit size
* 15 and the same architecture bit size.
*
* However, in the following code:
* ```go
* parsed, _ := strconv.ParseUint(input, 10, 32)
* if parsed < 5 {
* _ = uint16(parsed)
* }
* ```
* `parsed < 5` is an `UpperBoundCheckGuard` and `uint16(parsed)` is a barrier
* for all flow states and would not transform any flow states, thus
* effectively blocking them.
*/
class UpperBoundCheck extends BarrierFlowStateTransformer {
UpperBoundCheckGuard g;
UpperBoundCheck() {
this = DataFlow::BarrierGuard<upperBoundCheckGuard/3>::getABarrierNodeForGuard(g)
}
override predicate barrierFor(MaxValueState flowstate) {
// Use a default value of 32 for `MaxValueState.getSinkBitSize` because
// this will find results that only exist on 32-bit architectures.
g.isBoundFor(flowstate.getBitSize(), flowstate.getSinkBitSize(32))
}
override MaxValueState transform(MaxValueState state) {
this.barrierFor(state) and
result.getBitSize() =
max(int bitsize |
bitsize = validBitSize() and
bitsize < state.getBitSize() and
// Use a default value of 32 for `MaxValueState.getSinkBitSize` because
// this will find results that only exist on 32-bit architectures.
not g.isBoundFor(bitsize, state.getSinkBitSize(32))
) and
result.getArchitectureBitSize() = state.getArchitectureBitSize()
}
}
/**
* Holds if `source` is the result of a call to `strconv.Atoi`,
* `strconv.ParseInt`, or `strconv.ParseUint`, `bitSize` is the `bitSize`
* argument to that call (or 0 for `strconv.Atoi`) and hence must be between 0
* and 64, and `isSigned` is true for `strconv.Atoi`, true for
* `strconv.ParseInt` and false for `strconv.ParseUint`.
*/
predicate isSourceWithBitSize(DataFlow::Node source, int bitSize, boolean isSigned) {
exists(DataFlow::CallNode c, IntegerParser::Range ip, int apparentBitSize |
c = ip.getACall() and
source = c.getResult(0) and
(
apparentBitSize = ip.getTargetBitSize()
or
// If we are reading a variable, check if it is
// `strconv.IntSize`, and use 0 if it is.
exists(DataFlow::Node rawBitSize |
rawBitSize = ip.getTargetBitSizeInput().getNode(c) and
if rawBitSize = any(Strconv::IntSize intSize).getARead()
then apparentBitSize = 0
else apparentBitSize = rawBitSize.getIntValue()
)
) and
// Note that `bitSize` is not necessarily the bit-size of an integer type.
// It can be any integer between 0 and 64.
bitSize = replaceZeroWith(apparentBitSize, getIntTypeBitSize(source.getFile(), 0)) and
isSigned = ip.isSigned()
)
}
private module ConversionWithoutBoundsCheckConfig implements DataFlow::StateConfigSig {
class FlowState = IntegerConversionFlowState;
class FlowState = MaxValueState;
predicate isSource(DataFlow::Node source, FlowState state) {
exists(
DataFlow::CallNode c, IntegerParser::Range ip, int apparentBitSize, int effectiveBitSize
|
c.getTarget() = ip and source = c.getResult(0)
|
(
apparentBitSize = ip.getTargetBitSize()
or
// If we are reading a variable, check if it is
// `strconv.IntSize`, and use 0 if it is.
exists(DataFlow::Node rawBitSize | rawBitSize = ip.getTargetBitSizeInput().getNode(c) |
if rawBitSize = any(Strconv::IntSize intSize).getARead()
then apparentBitSize = 0
else apparentBitSize = rawBitSize.getIntValue()
exists(int effectiveBitSize, boolean sourceIsSigned |
isSourceWithBitSize(source, effectiveBitSize, sourceIsSigned) and
if effectiveBitSize = 0
then
exists(int b | b = [32, 64] |
state.getBitSize() = adjustBitSize(0, sourceIsSigned, b) and
state.getArchitectureBitSize().toInt() = b
)
) and
(
if apparentBitSize = 0
then effectiveBitSize = getIntTypeBitSize(source.getFile())
else effectiveBitSize = apparentBitSize
) and
// `effectiveBitSize` could be any value between 0 and 64, but we
// can round it up to the nearest size of an integer type without
// changing behavior.
exists(int sourceBitSize |
sourceBitSize = min(int b | b in [0, 8, 16, 32, 64] and b >= effectiveBitSize)
|
state = TFlowstate(_, sourceBitSize, _)
else (
state.getArchitectureBitSize().isUnknown() and
state.getBitSize() =
min(int bitsize |
bitsize = validBitSize() and
// The `bitSizeForZero` argument will not be used because on this
// branch `effectiveBitSize != 0`.
adjustBitSize(effectiveBitSize, sourceIsSigned, 64) <= bitsize
)
)
)
}
@@ -203,18 +416,26 @@ private module ConversionWithoutBoundsCheckConfig implements DataFlow::StateConf
* not also in a right-shift expression. We allow this case because it is
* a common pattern to serialise `byte(v)`, `byte(v >> 8)`, and so on.
*/
additional predicate isSinkWithBitSize(
DataFlow::TypeCastNode sink, boolean sinkIsSigned, int bitSize
) {
additional predicate isSink2(DataFlow::TypeCastNode sink, FlowState state) {
sink.asExpr() instanceof ConversionExpr and
exists(IntegerType integerType | sink.getResultType().getUnderlyingType() = integerType |
exists(int architectureBitSize, IntegerType integerType, int sinkBitsize, boolean sinkIsSigned |
// Use a default value of 32 for `MaxValueState.getSinkBitSize` because
// this will find results that only exist on 32-bit architectures.
architectureBitSize = getIntTypeBitSize(sink.getFile(), state.getSinkBitSize(32)) and
not (state.getArchitectureBitSize().toInt() = 32 and architectureBitSize = 64) and
sink.getResultType().getUnderlyingType() = integerType and
(
bitSize = integerType.getSize()
sinkBitsize = integerType.getSize()
or
not exists(integerType.getSize()) and
bitSize = getIntTypeBitSize(sink.getFile())
sinkBitsize = 0
) and
if integerType instanceof SignedIntegerType then sinkIsSigned = true else sinkIsSigned = false
(
if integerType instanceof SignedIntegerType
then sinkIsSigned = true
else sinkIsSigned = false
) and
adjustBitSize(sinkBitsize, sinkIsSigned, architectureBitSize) < state.getBitSize()
) and
not exists(ShrExpr shrExpr |
shrExpr.getLeftOperand().getGlobalValueNumber() =
@@ -229,31 +450,24 @@ private module ConversionWithoutBoundsCheckConfig implements DataFlow::StateConf
// can sanitize the result of the conversion to prevent flow on to further sinks
// without needing to use `isSanitizerOut`, which doesn't work with flow states
// (and therefore the legacy `TaintTracking::Configuration` class).
exists(boolean sinkIsSigned, int sinkBitSize |
state = TFlowstate(sinkIsSigned, _, sinkBitSize)
|
isSinkWithBitSize(sink.getASuccessor(), sinkIsSigned, sinkBitSize)
)
isSink2(sink.getASuccessor(), state)
}
predicate isBarrier(DataFlow::Node node, FlowState state) {
exists(boolean sinkIsSigned, int sourceBitSize, int sinkBitSize |
state = TFlowstate(sinkIsSigned, sourceBitSize, sinkBitSize)
|
// To catch flows that only happen on 32-bit architectures we
// consider an architecture-dependent sink bit size to be 32.
exists(UpperBoundCheckGuard g, int bitSize |
if sinkBitSize != 0 then bitSize = sinkBitSize else bitSize = 32
|
node = DataFlow::BarrierGuard<upperBoundCheckGuard/3>::getABarrierNodeForGuard(g) and
g.isBoundFor(bitSize, sinkIsSigned)
)
or
exists(int bitSize |
isIncorrectIntegerConversion(sourceBitSize, bitSize) and
isSinkWithBitSize(node, sinkIsSigned, bitSize)
)
)
// Safely guarded by a barrier guard.
exists(BarrierFlowStateTransformer bfst | node = bfst and bfst.barrierFor(state))
or
// When there is a flow from a source to a sink, do not allow the flow to
// continue to a further sink.
isSink2(node, state)
}
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
// Create additional flow steps for `BarrierFlowStateTransformer`s
state2 = node2.(BarrierFlowStateTransformer).transform(state1) and
DataFlow::simpleLocalFlowStep(node1, node2)
}
}
@@ -263,57 +477,8 @@ private module ConversionWithoutBoundsCheckConfig implements DataFlow::StateConf
*/
module Flow = TaintTracking::GlobalWithState<ConversionWithoutBoundsCheckConfig>;
private predicate upperBoundCheckGuard(DataFlow::Node g, Expr e, boolean branch) {
g.(UpperBoundCheckGuard).checks(e, branch)
}
/** An upper bound check that compares a variable to a constant value. */
class UpperBoundCheckGuard extends DataFlow::RelationalComparisonNode {
UpperBoundCheckGuard() {
count(expr.getAnOperand().getExactValue()) = 1 and
expr.getAnOperand().getType().getUnderlyingType() instanceof IntegerType
}
/**
* Gets the constant value which this upper bound check ensures the
* other value is less than or equal to.
*/
predicate isBoundFor(int bitSize, boolean isSigned) {
bitSize = [8, 16, 32] and
exists(float bound, float strictnessOffset |
// For `x < c` the bound is `c-1`. For `x >= c` we will be an upper bound
// on the `branch` argument of `checks` is false, which is equivalent to
// `x < c`.
if expr instanceof LssExpr or expr instanceof GeqExpr
then strictnessOffset = 1
else strictnessOffset = 0
|
(
bound = expr.getAnOperand().getExactValue().toFloat()
or
exists(DeclaredConstant maxint | maxint.hasQualifiedName("math", "MaxInt") |
expr.getAnOperand() = maxint.getAReference() and
bound = getMaxIntValue(32, true)
)
or
exists(DeclaredConstant maxuint | maxuint.hasQualifiedName("math", "MaxUint") |
expr.getAnOperand() = maxuint.getAReference() and
bound = getMaxIntValue(32, false)
)
) and
bound - strictnessOffset <= getMaxIntValue(bitSize, isSigned)
)
}
/** Holds if this guard validates `e` upon evaluating to `branch`. */
predicate checks(Expr e, boolean branch) {
this.leq(branch, DataFlow::exprNode(e), _, _) and
not e.isConst()
}
}
/** Gets a string describing the size of the integer parsed. */
string describeBitSize(int bitSize, int intTypeBitSize) {
deprecated string describeBitSize(int bitSize, int intTypeBitSize) {
intTypeBitSize in [0, 32, 64] and
if bitSize != 0
then bitSize in [8, 16, 32, 64] and result = "a " + bitSize + "-bit integer"
@@ -325,3 +490,40 @@ string describeBitSize(int bitSize, int intTypeBitSize) {
"a number with architecture-dependent bit-width, which is constrained to be " +
intTypeBitSize + "-bit by build constraints,"
}
/** Gets a string describing the size of the integer parsed. */
string describeBitSize2(DataFlow::Node source) {
exists(int sourceBitSize, int intTypeBitSize, boolean isSigned, string signedString |
isSourceWithBitSize(source, sourceBitSize, isSigned) and
intTypeBitSize = getIntTypeBitSize(source.getFile(), 0)
|
(if isSigned = true then signedString = "a signed " else signedString = "an unsigned ") and
if sourceBitSize != 0
then result = signedString + sourceBitSize + "-bit integer"
else
if intTypeBitSize = 0
then result = "an integer with architecture-dependent bit size"
else
result =
"a number with architecture-dependent bit-width, which is constrained to be " +
intTypeBitSize + "-bit by build constraints,"
)
}
/**
* The integer type with bit size `bitSize` and signedness `isSigned` has
* maximum value `2^result - 1`.
*/
bindingset[bitSize, bitSizeForZero]
private int adjustBitSize(int bitSize, boolean isSigned, int bitSizeForZero) {
exists(int effectiveBitSize | effectiveBitSize = replaceZeroWith(bitSize, bitSizeForZero) |
isSigned = true and result = effectiveBitSize - 1
or
isSigned = false and result = effectiveBitSize
)
}
bindingset[inputBitSize, replacementForZero]
private int replaceZeroWith(int inputBitSize, int replacementForZero) {
if inputBitSize = 0 then result = replacementForZero else result = inputBitSize
}

View File

@@ -24,7 +24,6 @@ where
call.getResult(0) = source.getNode() and
sinkConverted = sink.getNode().getASuccessor()
select sinkConverted, source, sink,
"Incorrect conversion of " +
describeBitSize(getSourceBitSize(sink.getState()), getIntTypeBitSize(source.getNode().getFile()))
+ " from $@ to a lower bit size type " + sinkConverted.getType().getUnderlyingType().getName() +
"Incorrect conversion of " + describeBitSize2(source.getNode()) +
" from $@ to a lower bit size type " + sinkConverted.getType().getUnderlyingType().getName() +
" without an upper bound check.", source, call.getTarget().getQualifiedName()

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The query "Incorrect conversion between integer types" (`go/incorrect-integer-conversion`) has been improved. It can now detect parsing an unsigned integer type (like `uint32`) and converting it to the signed integer type of the same size (like `int32`), which may lead to more results. It also treats `int` and `uint` more carefully, which may lead to more results or fewer incorrect results.

View File

@@ -134,7 +134,7 @@ func testParseUint() {
if err != nil {
panic(err)
}
_ = int8(parsed)
_ = int8(parsed) // $ hasValueFlow="type conversion"
_ = uint8(parsed)
_ = int16(parsed)
_ = uint16(parsed)
@@ -152,7 +152,7 @@ func testParseUint() {
}
_ = int8(parsed) // $ hasValueFlow="type conversion"
_ = uint8(parsed) // $ hasValueFlow="type conversion"
_ = int16(parsed)
_ = int16(parsed) // $ hasValueFlow="type conversion"
_ = uint16(parsed)
_ = int32(parsed)
_ = uint32(parsed)
@@ -170,11 +170,11 @@ func testParseUint() {
_ = uint8(parsed) // $ hasValueFlow="type conversion"
_ = int16(parsed) // $ hasValueFlow="type conversion"
_ = uint16(parsed) // $ hasValueFlow="type conversion"
_ = int32(parsed)
_ = int32(parsed) // $ hasValueFlow="type conversion"
_ = uint32(parsed)
_ = int64(parsed)
_ = uint64(parsed)
_ = int(parsed)
_ = int(parsed) // $ hasValueFlow="type conversion"
_ = uint(parsed)
}
{
@@ -188,7 +188,7 @@ func testParseUint() {
_ = uint16(parsed) // $ hasValueFlow="type conversion"
_ = int32(parsed) // $ hasValueFlow="type conversion"
_ = uint32(parsed) // $ hasValueFlow="type conversion"
_ = int64(parsed)
_ = int64(parsed) // $ hasValueFlow="type conversion"
_ = uint64(parsed)
_ = int(parsed) // $ hasValueFlow="type conversion"
_ = uint(parsed) // $ hasValueFlow="type conversion"
@@ -204,9 +204,9 @@ func testParseUint() {
_ = uint16(parsed) // $ hasValueFlow="type conversion"
_ = int32(parsed) // $ hasValueFlow="type conversion"
_ = uint32(parsed) // $ hasValueFlow="type conversion"
_ = int64(parsed)
_ = int64(parsed) // $ hasValueFlow="type conversion"
_ = uint64(parsed)
_ = int(parsed)
_ = int(parsed) // $ hasValueFlow="type conversion"
_ = uint(parsed)
}
}
@@ -266,6 +266,62 @@ func testBoundsChecking(input string) {
}
if parsed <= math.MaxUint16 {
_ = uint16(parsed)
_ = uint(parsed)
_ = int32(parsed)
}
}
{
parsed, err := strconv.ParseUint(input, 10, 0)
if err != nil {
panic(err)
}
if parsed <= math.MaxUint64 {
_ = int8(parsed) // $ hasValueFlow="type conversion"
_ = uint8(parsed) // $ hasValueFlow="type conversion"
_ = int16(parsed) // $ hasValueFlow="type conversion"
_ = uint16(parsed) // $ hasValueFlow="type conversion"
_ = int32(parsed) // $ hasValueFlow="type conversion"
_ = uint32(parsed) // $ hasValueFlow="type conversion"
_ = int64(parsed) // $ hasValueFlow="type conversion"
_ = uint64(parsed)
_ = int(parsed) // $ hasValueFlow="type conversion"
_ = uint(parsed)
}
if parsed <= math.MaxInt64 {
_ = int8(parsed) // $ hasValueFlow="type conversion"
_ = uint8(parsed) // $ hasValueFlow="type conversion"
_ = int16(parsed) // $ hasValueFlow="type conversion"
_ = uint16(parsed) // $ hasValueFlow="type conversion"
_ = int32(parsed) // $ hasValueFlow="type conversion"
_ = uint32(parsed) // $ hasValueFlow="type conversion"
_ = int64(parsed)
_ = uint64(parsed)
_ = int(parsed) // $ hasValueFlow="type conversion"
_ = uint(parsed)
}
if parsed <= math.MaxUint32 {
_ = int8(parsed) // $ hasValueFlow="type conversion"
_ = uint8(parsed) // $ hasValueFlow="type conversion"
_ = int16(parsed) // $ hasValueFlow="type conversion"
_ = uint16(parsed) // $ hasValueFlow="type conversion"
_ = int32(parsed) // $ hasValueFlow="type conversion"
_ = uint32(parsed)
_ = int64(parsed)
_ = uint64(parsed)
_ = int(parsed) // $ hasValueFlow="type conversion"
_ = uint(parsed)
}
if parsed <= math.MaxInt32 {
_ = int8(parsed) // $ hasValueFlow="type conversion"
_ = uint8(parsed) // $ hasValueFlow="type conversion"
_ = int16(parsed) // $ hasValueFlow="type conversion"
_ = uint16(parsed) // $ hasValueFlow="type conversion"
_ = int32(parsed)
_ = uint32(parsed)
_ = int64(parsed)
_ = uint64(parsed)
_ = int(parsed)
_ = uint(parsed)
}
}
{
@@ -273,8 +329,9 @@ func testBoundsChecking(input string) {
if err != nil {
panic(err)
}
if parsed <= math.MaxUint8 {
_ = uint8(parsed)
if parsed <= math.MaxUint16 {
_ = uint16(parsed)
_ = int16(parsed) // $ hasValueFlow="type conversion"
}
if parsed < 5 {
_ = uint16(parsed)

View File

@@ -20,7 +20,7 @@ func testIntSource386() {
if err != nil {
panic(err)
}
_ = int32(parsed)
_ = int32(parsed) // $ hasValueFlow="type conversion"
_ = uint32(parsed)
}
{

View File

@@ -23,7 +23,7 @@ func testIntSource32() {
if err != nil {
panic(err)
}
_ = int32(parsed)
_ = int32(parsed) // $ hasValueFlow="type conversion"
_ = uint32(parsed)
}
{

View File

@@ -20,7 +20,7 @@ func testIntSinkAmd64() {
if err != nil {
panic(err)
}
_ = int(parsed)
_ = int(parsed) // $ hasValueFlow="type conversion"
_ = uint(parsed)
}
}

View File

@@ -23,7 +23,7 @@ func testIntSink64() {
if err != nil {
panic(err)
}
_ = int(parsed)
_ = int(parsed) // $ hasValueFlow="type conversion"
_ = uint(parsed)
}
}

View File

@@ -24,7 +24,7 @@ func oldTestIntSink64() {
if err != nil {
panic(err)
}
_ = int(parsed)
_ = int(parsed) // $ hasValueFlow="type conversion"
_ = uint(parsed)
}
}