Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8db3e4a

Browse files
committed
Make IncorrectIntegerConversion use new API
1 parent 6c0c8d6 commit 8db3e4a

3 files changed

Lines changed: 118 additions & 8 deletions

File tree

go/ql/lib/semmle/go/security/IncorrectIntegerConversionLib.qll

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,13 @@ private predicate isIncorrectIntegerConversion(int sourceBitSize, int sinkBitSiz
5151
}
5252

5353
/**
54+
* DEPRECATED: use `Flow` instead.
55+
*
5456
* A taint-tracking configuration for reasoning about when an integer
5557
* obtained from parsing a string flows to a type conversion to a smaller
5658
* integer types, which could cause unexpected values.
5759
*/
58-
class ConversionWithoutBoundsCheckConfig extends TaintTracking::Configuration {
60+
deprecated class ConversionWithoutBoundsCheckConfig extends TaintTracking::Configuration {
5961
boolean sinkIsSigned;
6062
int sourceBitSize;
6163
int sinkBitSize;
@@ -148,6 +150,115 @@ class ConversionWithoutBoundsCheckConfig extends TaintTracking::Configuration {
148150
}
149151
}
150152

153+
/** Flow state for ConversionWithoutBoundsCheckConfig. */
154+
newtype MyFlowState =
155+
/** Keep track of info about the source and potential sinks. */
156+
TFlowstate(boolean sinkIsSigned, int sourceBitSize, int sinkBitSize) {
157+
sinkIsSigned in [true, false] and
158+
isIncorrectIntegerConversion(sourceBitSize, sinkBitSize)
159+
}
160+
161+
/** Gets the bit size of the source. */
162+
int getSourceBitSize(MyFlowState state) { state = TFlowstate(_, result, _) }
163+
164+
private module ConversionWithoutBoundsCheckConfig implements DataFlow::StateConfigSig {
165+
class FlowState = MyFlowState;
166+
167+
predicate isSource(DataFlow::Node source, FlowState state) {
168+
exists(
169+
DataFlow::CallNode c, IntegerParser::Range ip, int apparentBitSize, int effectiveBitSize
170+
|
171+
c.getTarget() = ip and source = c.getResult(0)
172+
|
173+
(
174+
apparentBitSize = ip.getTargetBitSize()
175+
or
176+
// If we are reading a variable, check if it is
177+
// `strconv.IntSize`, and use 0 if it is.
178+
exists(DataFlow::Node rawBitSize | rawBitSize = ip.getTargetBitSizeInput().getNode(c) |
179+
if rawBitSize = any(Strconv::IntSize intSize).getARead()
180+
then apparentBitSize = 0
181+
else apparentBitSize = rawBitSize.getIntValue()
182+
)
183+
) and
184+
(
185+
if apparentBitSize = 0
186+
then effectiveBitSize = getIntTypeBitSize(source.getFile())
187+
else effectiveBitSize = apparentBitSize
188+
) and
189+
// `effectiveBitSize` could be any value between 0 and 64, but we
190+
// can round it up to the nearest size of an integer type without
191+
// changing behavior.
192+
exists(int sourceBitSize |
193+
sourceBitSize = min(int b | b in [0, 8, 16, 32, 64] and b >= effectiveBitSize)
194+
|
195+
state = TFlowstate(_, sourceBitSize, _)
196+
)
197+
)
198+
}
199+
200+
/**
201+
* Holds if `sink` is a typecast to an integer type with size `bitSize` (where
202+
* 0 represents architecture-dependent) and the expression being typecast is
203+
* not also in a right-shift expression. We allow this case because it is
204+
* a common pattern to serialise `byte(v)`, `byte(v >> 8)`, and so on.
205+
*/
206+
additional predicate isSinkWithBitSize(
207+
DataFlow::TypeCastNode sink, boolean sinkIsSigned, int bitSize
208+
) {
209+
sink.asExpr() instanceof ConversionExpr and
210+
exists(IntegerType integerType | sink.getResultType().getUnderlyingType() = integerType |
211+
(
212+
bitSize = integerType.getSize()
213+
or
214+
not exists(integerType.getSize()) and
215+
bitSize = getIntTypeBitSize(sink.getFile())
216+
) and
217+
if integerType instanceof SignedIntegerType then sinkIsSigned = true else sinkIsSigned = false
218+
) and
219+
not exists(ShrExpr shrExpr |
220+
shrExpr.getLeftOperand().getGlobalValueNumber() =
221+
sink.getOperand().asExpr().getGlobalValueNumber() or
222+
shrExpr.getLeftOperand().(AndExpr).getAnOperand().getGlobalValueNumber() =
223+
sink.getOperand().asExpr().getGlobalValueNumber()
224+
)
225+
}
226+
227+
predicate isSink(DataFlow::Node sink, FlowState state) {
228+
// We use the argument of the type conversion as the configuration sink so that we
229+
// can sanitize the result of the conversion to prevent flow on to further sinks
230+
// without needing to use `isSanitizerOut`, which doesn't work with flow states
231+
// (and therefore the legacy `TaintTracking::Configuration` class).
232+
exists(boolean sinkIsSigned, int sinkBitSize |
233+
state = TFlowstate(sinkIsSigned, _, sinkBitSize)
234+
|
235+
isSinkWithBitSize(sink.getASuccessor(), sinkIsSigned, sinkBitSize)
236+
)
237+
}
238+
239+
predicate isBarrier(DataFlow::Node node, FlowState state) {
240+
exists(boolean sinkIsSigned, int sourceBitSize, int sinkBitSize |
241+
state = TFlowstate(sinkIsSigned, sourceBitSize, sinkBitSize)
242+
|
243+
// To catch flows that only happen on 32-bit architectures we
244+
// consider an architecture-dependent sink bit size to be 32.
245+
exists(UpperBoundCheckGuard g, int bitSize |
246+
if sinkBitSize != 0 then bitSize = sinkBitSize else bitSize = 32
247+
|
248+
node = DataFlow::BarrierGuard<upperBoundCheckGuard/3>::getABarrierNodeForGuard(g) and
249+
g.isBoundFor(bitSize, sinkIsSigned)
250+
)
251+
or
252+
exists(int bitSize |
253+
isIncorrectIntegerConversion(sourceBitSize, bitSize) and
254+
isSinkWithBitSize(node, sinkIsSigned, bitSize)
255+
)
256+
)
257+
}
258+
}
259+
260+
module Flow = TaintTracking::GlobalWithState<ConversionWithoutBoundsCheckConfig>;
261+
151262
private predicate upperBoundCheckGuard(DataFlow::Node g, Expr e, boolean branch) {
152263
g.(UpperBoundCheckGuard).checks(e, branch)
153264
}

go/ql/src/Security/CWE-681/IncorrectIntegerConversionQuery.ql

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,17 @@
1414
*/
1515

1616
import go
17-
import DataFlow::PathGraph
1817
import semmle.go.security.IncorrectIntegerConversionLib
18+
import Flow::PathGraph
1919

2020
from
21-
DataFlow::PathNode source, DataFlow::PathNode sink, ConversionWithoutBoundsCheckConfig cfg,
22-
DataFlow::CallNode call, DataFlow::Node sinkConverted
21+
Flow::PathNode source, Flow::PathNode sink, DataFlow::CallNode call, DataFlow::Node sinkConverted
2322
where
24-
cfg.hasFlowPath(source, sink) and
23+
Flow::flowPath(source, sink) and
2524
call.getResult(0) = source.getNode() and
2625
sinkConverted = sink.getNode().getASuccessor()
2726
select sinkConverted, source, sink,
2827
"Incorrect conversion of " +
29-
describeBitSize(cfg.getSourceBitSize(), getIntTypeBitSize(source.getNode().getFile())) +
30-
" from $@ to a lower bit size type " + sinkConverted.getType().getUnderlyingType().getName() +
28+
describeBitSize(getSourceBitSize(sink.getState()), getIntTypeBitSize(source.getNode().getFile()))
29+
+ " from $@ to a lower bit size type " + sinkConverted.getType().getUnderlyingType().getName() +
3130
" without an upper bound check.", source, call.getTarget().getQualifiedName()

go/ql/test/query-tests/Security/CWE-681/IncorrectIntegerConversion.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module TestIncorrectIntegerConversion implements TestSig {
88
predicate hasActualResult(Location location, string element, string tag, string value) {
99
tag = "hasValueFlow" and
1010
exists(DataFlow::Node sink, DataFlow::Node sinkConverted |
11-
any(ConversionWithoutBoundsCheckConfig config).hasFlowTo(sink) and
11+
Flow::flowTo(sink) and
1212
sinkConverted = sink.getASuccessor()
1313
|
1414
sinkConverted

0 commit comments

Comments
 (0)