Skip to content

Commit 8d2bf22

Browse files
authored
Merge pull request #7914 from hvitved/ruby/generalize-element-content
Ruby: Generalize `ArrayElementContent` to `ElementContent`
2 parents ec31675 + f766981 commit 8d2bf22

File tree

15 files changed

+5562
-5551
lines changed

15 files changed

+5562
-5551
lines changed

ruby/ql/lib/codeql/ruby/ast/Constant.qll

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,38 @@ class ConstantValue extends TConstantValue {
105105

106106
/** Holds if this is the `nil` value. */
107107
predicate isNil() { this = TNil() }
108+
109+
/** Gets a (unique) serialized version of this value. */
110+
final string serialize() {
111+
result = this.getInt().toString()
112+
or
113+
exists(string res | res = this.getFloat().toString() |
114+
if exists(res.indexOf(".")) then result = res else result = res + ".0"
115+
)
116+
or
117+
exists(int numerator, int denominator |
118+
this.isRational(numerator, denominator) and
119+
result = numerator + "/" + denominator
120+
)
121+
or
122+
exists(float real, float imaginary |
123+
this.isComplex(real, imaginary) and
124+
result = real + "+" + imaginary + "i"
125+
)
126+
or
127+
result = "\"" + this.getString().replaceAll("\"", "\\\"") + "\""
128+
or
129+
result = ":" + this.getSymbol()
130+
or
131+
exists(string s, string flags |
132+
this.isRegExpWithFlags(s, flags) and
133+
result = "/" + s + "/" + flags
134+
)
135+
or
136+
result = this.getBoolean().toString()
137+
or
138+
this.isNil() and result = "nil"
139+
}
108140
}
109141

110142
/** Provides different sub classes of `ConstantValue`. */

ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ cached
404404
private module Cached {
405405
cached
406406
newtype TConstantValue =
407-
TInt(int i) { isInt(_, i) or isIntExpr(_, i) } or
407+
TInt(int i) { isInt(_, i) or isIntExpr(_, i) or i in [0 .. 100] } or
408408
TFloat(float f) { isFloat(_, f) or isFloatExpr(_, f) } or
409409
TRational(int numerator, int denominator) {
410410
isRational(_, numerator, denominator) or

ruby/ql/lib/codeql/ruby/dataflow/FlowSummary.qll

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,33 +31,27 @@ module SummaryComponent {
3131
/** Gets a summary component that represents a block argument. */
3232
SummaryComponent block() { result = argument(any(ParameterPosition pos | pos.isBlock())) }
3333

34-
/** Gets a summary component that represents an element in an array at an unknown index. */
35-
SummaryComponent arrayElementUnknown() {
36-
result = SC::content(TSingletonContent(TUnknownArrayElementContent()))
34+
/** Gets a summary component that represents an element in a collection at an unknown index. */
35+
SummaryComponent elementUnknown() {
36+
result = SC::content(TSingletonContent(TUnknownElementContent()))
3737
}
3838

39-
/**
40-
* Gets a summary component that represents an element in an array at a known index.
41-
*
42-
* Has no result for negative indices. Wrap-around interpretation of negative indices should be
43-
* handled by the caller, if modeling a function that has such behavior.
44-
*/
45-
bindingset[i]
46-
SummaryComponent arrayElementKnown(int i) {
47-
result = SC::content(TSingletonContent(TKnownArrayElementContent(i)))
48-
or
49-
// `i` may be out of range
50-
i >= 0 and
51-
not exists(TKnownArrayElementContent(i)) and
52-
result = arrayElementUnknown()
39+
/** Gets a summary component that represents an element in a collection at a known index. */
40+
SummaryComponent elementKnown(ConstantValue cv) {
41+
result = SC::content(TSingletonContent(DataFlow::Content::getElementContent(cv)))
5342
}
5443

5544
/**
56-
* Gets a summary component that represents an element in an array at either an unknown
57-
* index or known index. This predicate should never be used in the output specification
58-
* of a flow summary; use `arrayElementUnknown()` instead.
45+
* Gets a summary component that represents an element in a collection at either an unknown
46+
* index or known index. This has the same semantics as
47+
*
48+
* ```ql
49+
* elementKnown() or elementUnknown(_)
50+
* ```
51+
*
52+
* but is more efficient, because it is represented by a single value.
5953
*/
60-
SummaryComponent arrayElementAny() { result = SC::content(TAnyArrayElementContent()) }
54+
SummaryComponent elementAny() { result = SC::content(TAnyElementContent()) }
6155

6256
/** Gets a summary component that represents the return value of a call. */
6357
SummaryComponent return() { result = SC::return(any(NormalReturnKind rk)) }

ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -319,12 +319,15 @@ private module Cached {
319319
cached
320320
newtype TContentSet =
321321
TSingletonContent(Content c) or
322-
TAnyArrayElementContent()
322+
TAnyElementContent()
323323

324324
cached
325325
newtype TContent =
326-
TKnownArrayElementContent(int i) { i in [0 .. 10] } or
327-
TUnknownArrayElementContent()
326+
TKnownElementContent(ConstantValue cv) {
327+
not cv.isInt(_) or
328+
cv.getInt() in [0 .. 10]
329+
} or
330+
TUnknownElementContent()
328331

329332
/**
330333
* Holds if `e` is an `ExprNode` that may be returned by a call to `c`.
@@ -341,7 +344,7 @@ private module Cached {
341344
}
342345
}
343346

344-
class TArrayElementContent = TKnownArrayElementContent or TUnknownArrayElementContent;
347+
class TElementContent = TKnownElementContent or TUnknownElementContent;
345348

346349
import Cached
347350

@@ -862,7 +865,7 @@ int accessPathLimit() { result = 5 }
862865
* Holds if access paths with `c` at their head always should be tracked at high
863866
* precision. This disables adaptive access path precision for such access paths.
864867
*/
865-
predicate forceHighPrecision(Content c) { c instanceof Content::ArrayElementContent }
868+
predicate forceHighPrecision(Content c) { c instanceof Content::ElementContent }
866869

867870
/** The unit type. */
868871
private newtype TUnit = TMkUnit()

ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -203,24 +203,32 @@ class Content extends TContent {
203203

204204
/** Provides different sub classes of `Content`. */
205205
module Content {
206-
/** An element in an array. */
207-
class ArrayElementContent extends Content, TArrayElementContent { }
206+
/** An element in a collection, for example an element in an array or in a hash. */
207+
class ElementContent extends Content, TElementContent { }
208208

209-
/** An element in an array at a known index. */
210-
class KnownArrayElementContent extends ArrayElementContent, TKnownArrayElementContent {
211-
private int i;
209+
/** An element in a collection at a known index. */
210+
class KnownElementContent extends ElementContent, TKnownElementContent {
211+
private ConstantValue cv;
212212

213-
KnownArrayElementContent() { this = TKnownArrayElementContent(i) }
213+
KnownElementContent() { this = TKnownElementContent(cv) }
214214

215-
/** Gets the index in the array. */
216-
int getIndex() { result = i }
215+
/** Gets the index in the collection. */
216+
ConstantValue getIndex() { result = cv }
217217

218-
override string toString() { result = "array element " + i }
218+
override string toString() { result = "element " + cv }
219219
}
220220

221-
/** An element in an array at an unknown index. */
222-
class UnknownArrayElementContent extends ArrayElementContent, TUnknownArrayElementContent {
223-
override string toString() { result = "array element" }
221+
/** An element in a collection at an unknown index. */
222+
class UnknownElementContent extends ElementContent, TUnknownElementContent {
223+
override string toString() { result = "element" }
224+
}
225+
226+
/** Gets the element content corresponding to constant value `cv`. */
227+
ElementContent getElementContent(ConstantValue cv) {
228+
result = TKnownElementContent(cv)
229+
or
230+
not exists(TKnownElementContent(cv)) and
231+
result = TUnknownElementContent()
224232
}
225233
}
226234

@@ -234,8 +242,8 @@ class ContentSet extends TContentSet {
234242
/** Holds if this content set is the singleton `{c}`. */
235243
predicate isSingleton(Content c) { this = TSingletonContent(c) }
236244

237-
/** Holds if this content set represent all `ArrayElementContent`s. */
238-
predicate isAnyArrayElement() { this = TAnyArrayElementContent() }
245+
/** Holds if this content set represents all `ElementContent`s. */
246+
predicate isAnyElement() { this = TAnyElementContent() }
239247

240248
/** Gets a textual representation of this content set. */
241249
string toString() {
@@ -244,24 +252,24 @@ class ContentSet extends TContentSet {
244252
result = c.toString()
245253
)
246254
or
247-
this.isAnyArrayElement() and
255+
this.isAnyElement() and
248256
result = "any array element"
249257
}
250258

251259
/** Gets a content that may be stored into when storing into this set. */
252260
Content getAStoreContent() {
253261
this.isSingleton(result)
254262
or
255-
this.isAnyArrayElement() and
256-
result = TUnknownArrayElementContent()
263+
this.isAnyElement() and
264+
result = TUnknownElementContent()
257265
}
258266

259267
/** Gets a content that may be read from when reading from this set. */
260268
Content getAReadContent() {
261269
this.isSingleton(result)
262270
or
263-
this.isAnyArrayElement() and
264-
result instanceof Content::ArrayElementContent
271+
this.isAnyElement() and
272+
result instanceof Content::ElementContent
265273
}
266274
}
267275

ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImplSpecific.qll

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -73,19 +73,19 @@ SummaryComponent interpretComponentSpecific(AccessPathToken c) {
7373
ppos.isPositionalLowerBound(AccessPath::parseLowerBound(arg))
7474
)
7575
or
76-
c.getName() = "ArrayElement" and
77-
(
78-
c.getNumArgument() = 0 and
79-
result = FlowSummary::SummaryComponent::arrayElementAny()
76+
c.getName() = "Element" and
77+
exists(string arg | arg = c.getAnArgument() |
78+
arg = "?" and
79+
result = FlowSummary::SummaryComponent::elementUnknown()
8080
or
81-
exists(string arg | arg = c.getAnArgument() |
82-
arg = "?" and
83-
result = FlowSummary::SummaryComponent::arrayElementUnknown()
81+
arg = "any" and
82+
result = FlowSummary::SummaryComponent::elementAny()
83+
or
84+
exists(ConstantValue cv | result = FlowSummary::SummaryComponent::elementKnown(cv) |
85+
cv.isInt(AccessPath::parseInt(arg))
8486
or
85-
exists(int i |
86-
i = AccessPath::parseInt(c.getAnArgument()) and
87-
result = FlowSummary::SummaryComponent::arrayElementKnown(i)
88-
)
87+
not exists(AccessPath::parseInt(arg)) and
88+
cv.serialize() = c.getAnArgument()
8989
)
9090
)
9191
}

ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,9 @@ private module Cached {
103103
// allow flow out of a _tainted_ collection. This is needed in order to support taint-
104104
// tracking configurations where the source is a collection.
105105
exists(DataFlow::ContentSet c | readStep(nodeFrom, c, nodeTo) |
106-
c.isSingleton(any(DataFlow::Content::ArrayElementContent aec))
106+
c.isSingleton(any(DataFlow::Content::ElementContent ec))
107107
or
108-
c.isAnyArrayElement()
108+
c.isAnyElement()
109109
)
110110
}
111111

ruby/ql/lib/codeql/ruby/frameworks/Core.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ private class SplatSummary extends SummarizedCallable {
6262
(
6363
// *1 = [1]
6464
input = "Argument[self]" and
65-
output = "ReturnValue.ArrayElement[0]"
65+
output = "ReturnValue.Element[0]"
6666
or
6767
// *[1] = [1]
6868
input = "Argument[self]" and

0 commit comments

Comments
 (0)