Skip to content
This repository was archived by the owner on Jan 5, 2023. It is now read-only.

Commit bdcb1f2

Browse files
author
Max Schaefer
committed
Prevent misoptimisation in StringOps.
1 parent caf77e2 commit bdcb1f2

File tree

2 files changed

+40
-12
lines changed

2 files changed

+40
-12
lines changed

ql/src/semmle/go/StringOps.qll

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,25 @@ module StringOps {
7878
}
7979

8080
/**
81-
* An expression of form `strings.Index(A, B) === 0`.
81+
* Holds if `eq` is of the form `nd == 0` or `nd != 0`.
82+
*/
83+
pragma[noinline]
84+
private predicate comparesToZero(DataFlow::EqualityTestNode eq, DataFlow::Node nd) {
85+
exists(DataFlow::Node zero |
86+
eq.hasOperands(globalValueNumber(nd).getANode(), zero) and
87+
zero.getIntValue() = 0
88+
)
89+
}
90+
91+
/**
92+
* An expression of form `strings.Index(A, B) == 0`.
8293
*/
8394
private class HasPrefix_IndexOfEquals extends Range, DataFlow::EqualityTestNode {
8495
DataFlow::CallNode indexOf;
8596

8697
HasPrefix_IndexOfEquals() {
87-
indexOf.getTarget().hasQualifiedName("strings", "Index") and
88-
getAnOperand() = globalValueNumber(indexOf).getANode() and
89-
getAnOperand().getIntValue() = 0
98+
comparesToZero(this, indexOf) and
99+
indexOf.getTarget().hasQualifiedName("strings", "Index")
90100
}
91101

92102
override DataFlow::Node getBaseString() { result = indexOf.getArgument(0) }
@@ -97,19 +107,30 @@ module StringOps {
97107
}
98108

99109
/**
100-
* A comparison of form `x[0] === 'k'` for some rune literal `k`.
110+
* Holds if `eq` is of the form `str[0] == rhs` or `str[0] != rhs`.
111+
*/
112+
pragma[noinline]
113+
private predicate comparesFirstCharacter(
114+
DataFlow::EqualityTestNode eq, DataFlow::Node str, DataFlow::Node rhs
115+
) {
116+
exists(DataFlow::ElementReadNode read |
117+
eq.hasOperands(globalValueNumber(read).getANode(), rhs) and
118+
str = read.getBase() and
119+
str.getType().getUnderlyingType() instanceof StringType and
120+
read.getIndex().getIntValue() = 0
121+
)
122+
}
123+
124+
/**
125+
* A comparison of form `x[0] == 'k'` for some rune literal `k`.
101126
*/
102127
private class HasPrefix_FirstCharacter extends Range, DataFlow::EqualityTestNode {
103-
DataFlow::ElementReadNode read;
128+
DataFlow::Node base;
104129
DataFlow::Node runeLiteral;
105130

106-
HasPrefix_FirstCharacter() {
107-
read.getBase().getType().getUnderlyingType() instanceof StringType and
108-
read.getIndex().getIntValue() = 0 and
109-
eq(_, globalValueNumber(read).getANode(), runeLiteral)
110-
}
131+
HasPrefix_FirstCharacter() { comparesFirstCharacter(this, base, runeLiteral) }
111132

112-
override DataFlow::Node getBaseString() { result = read.getBase() }
133+
override DataFlow::Node getBaseString() { result = base }
113134

114135
override DataFlow::Node getSubstring() { result = runeLiteral }
115136

ql/src/semmle/go/dataflow/internal/DataFlowUtil.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,13 @@ class BinaryOperationNode extends Node {
669669

670670
/** Gets the operator of this operation. */
671671
string getOperator() { result = op }
672+
673+
/** Holds if `x` and `y` are the operands of this operation, in either order. */
674+
predicate hasOperands(Node x, Node y) {
675+
x = getAnOperand() and
676+
y = getAnOperand() and
677+
x != y
678+
}
672679
}
673680

674681
/**

0 commit comments

Comments
 (0)