Skip to content

Commit 493e757

Browse files
authored
Merge pull request #18386 from MathiasVP/more-robust-param-name-matching
C++: Resolve `typedef`s when matching MaD parameters
2 parents 7248fb7 + 99ad184 commit 493e757

File tree

6 files changed

+313
-14
lines changed

6 files changed

+313
-14
lines changed

cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll

Lines changed: 87 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -479,15 +479,87 @@ private Function getFullyTemplatedFunction(Function f) {
479479
)
480480
}
481481

482+
/** Prefixes `const` to `s` if `t` is const, or returns `s` otherwise. */
483+
bindingset[s, t]
484+
private string withConst(string s, Type t) {
485+
if t.isConst() then result = "const " + s else result = s
486+
}
487+
488+
/** Prefixes `volatile` to `s` if `t` is const, or returns `s` otherwise. */
489+
bindingset[s, t]
490+
private string withVolatile(string s, Type t) {
491+
if t.isVolatile() then result = "volatile " + s else result = s
492+
}
493+
494+
/**
495+
* Returns `s` prefixed with appropriate specifiers from `t`, or `s` if `t` has
496+
* no relevant specifiers.
497+
*/
498+
bindingset[s, t]
499+
private string withSpecifiers(string s, Type t) {
500+
// An `int` that is both const and volatile will be printed as
501+
// `const volatile int` to match the behavior of `Type.getName` which
502+
// is generated by the extractor.
503+
result = withConst(withVolatile(s, t), t)
504+
}
505+
506+
/**
507+
* Gets the string version of `t`, after resolving typedefs. The boolean `needsSpace` is `true`
508+
* if a space should be appended before concatenating any additional symbols
509+
* (such as `*` or `&`) when recursively constructing the type name.
510+
*/
511+
private string getTypeName(Type t, boolean needsSpace) {
512+
// We don't care about template instantiations since we always base models
513+
// on the uninstantiated templates
514+
not t.isFromTemplateInstantiation(_) and
515+
(
516+
exists(DerivedType dt, string s, boolean needsSpace0 |
517+
dt = t and s = withSpecifiers(getTypeName(dt.getBaseType(), needsSpace0), dt)
518+
|
519+
dt instanceof ReferenceType and
520+
not dt instanceof RValueReferenceType and
521+
needsSpace = false and
522+
(if needsSpace0 = true then result = s + " &" else result = s + "&")
523+
or
524+
dt instanceof RValueReferenceType and
525+
needsSpace = false and
526+
(if needsSpace0 = true then result = s + " &&" else result = s + "&&")
527+
or
528+
dt instanceof PointerType and
529+
needsSpace = false and
530+
(if needsSpace0 = true then result = s + " *" else result = s + "*")
531+
or
532+
not dt instanceof ReferenceType and
533+
not dt instanceof PointerType and
534+
result = s and
535+
needsSpace = needsSpace0
536+
)
537+
or
538+
not t instanceof DerivedType and
539+
not t instanceof TypedefType and
540+
result = t.getName() and
541+
(if result.matches(["%*", "%&", "%]"]) then needsSpace = false else needsSpace = true)
542+
)
543+
or
544+
result = getTypeName(t.(TypedefType).getBaseType(), needsSpace)
545+
}
546+
482547
/**
483-
* Gets the type name of the `n`'th parameter of `f` without any template
484-
* arguments.
548+
* Gets a type name for the `n`'th parameter of `f` without any template
549+
* arguments. The result may be a string representing a type for which the
550+
* typedefs have been resolved.
485551
*/
486552
bindingset[f]
487553
pragma[inline_late]
488554
string getParameterTypeWithoutTemplateArguments(Function f, int n) {
489-
exists(string s, string base, string specifiers |
490-
s = f.getParameter(n).getType().getName() and
555+
exists(string s, string base, string specifiers, Type t |
556+
t = f.getParameter(n).getType() and
557+
// The name of the string can either be the possibly typedefed name
558+
// or an alternative name where typedefs has been resolved.
559+
// `getTypeName(t, _)` is almost equal to `t.resolveTypedefs().getName()`,
560+
// except that `t.resolveTypedefs()` doesn't have a result when the
561+
// resulting type doesn't appear in the database.
562+
s = [t.getName(), getTypeName(t, _)] and
491563
parseAngles(s, base, _, specifiers) and
492564
result = base + specifiers
493565
)
@@ -902,18 +974,19 @@ private Element interpretElement0(
902974
)
903975
}
904976

905-
/** Gets the source/sink/summary element corresponding to the supplied parameters. */
906-
Element interpretElement(
907-
string namespace, string type, boolean subtypes, string name, string signature, string ext
908-
) {
909-
elementSpec(namespace, type, subtypes, name, signature, ext) and
910-
exists(Element e | e = interpretElement0(namespace, type, subtypes, name, signature) |
911-
ext = "" and result = e
912-
)
913-
}
914-
915977
cached
916978
private module Cached {
979+
/** Gets the source/sink/summary element corresponding to the supplied parameters. */
980+
cached
981+
Element interpretElement(
982+
string namespace, string type, boolean subtypes, string name, string signature, string ext
983+
) {
984+
elementSpec(namespace, type, subtypes, name, signature, ext) and
985+
exists(Element e | e = interpretElement0(namespace, type, subtypes, name, signature) |
986+
ext = "" and result = e
987+
)
988+
}
989+
917990
/**
918991
* Holds if `node` is specified as a source with the given kind in a CSV flow
919992
* model.

cpp/ql/test/library-tests/dataflow/models-as-data/FlowSummaryNode.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,5 @@
7474
| tests.cpp:436:6:436:25 | [summary] to write: Argument[1] in madCallArg0WithValue | PostUpdateNode | madCallArg0WithValue | madCallArg0WithValue |
7575
| tests.cpp:437:5:437:36 | [summary param] 1 in madCallReturnValueIgnoreFunction | ParameterNode | madCallReturnValueIgnoreFunction | madCallReturnValueIgnoreFunction |
7676
| tests.cpp:437:5:437:36 | [summary] to write: ReturnValue in madCallReturnValueIgnoreFunction | ReturnNode | madCallReturnValueIgnoreFunction | madCallReturnValueIgnoreFunction |
77+
| tests.cpp:459:5:459:31 | [summary param] *0 in parameter_ref_to_return_ref | ParameterNode | parameter_ref_to_return_ref | parameter_ref_to_return_ref |
78+
| tests.cpp:459:5:459:31 | [summary] to write: ReturnValue[*] in parameter_ref_to_return_ref | ReturnNode | parameter_ref_to_return_ref | parameter_ref_to_return_ref |

cpp/ql/test/library-tests/dataflow/models-as-data/SummaryCall.expected

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ summarizedCallables
2929
| tests.cpp:435:9:435:38 | madCallArg0ReturnToReturnFirst |
3030
| tests.cpp:436:6:436:25 | madCallArg0WithValue |
3131
| tests.cpp:437:5:437:36 | madCallReturnValueIgnoreFunction |
32+
| tests.cpp:459:5:459:31 | parameter_ref_to_return_ref |
3233
sourceCallables
3334
| tests.cpp:3:5:3:10 | source |
3435
| tests.cpp:4:6:4:14 | sourcePtr |
@@ -218,3 +219,14 @@ sourceCallables
218219
| tests.cpp:441:6:441:17 | dontUseValue |
219220
| tests.cpp:441:23:441:23 | x |
220221
| tests.cpp:443:6:443:27 | test_function_pointers |
222+
| tests.cpp:456:19:456:19 | X |
223+
| tests.cpp:457:8:457:35 | StructWithTypedefInParameter<X> |
224+
| tests.cpp:457:8:457:35 | StructWithTypedefInParameter<int> |
225+
| tests.cpp:458:12:458:15 | Type |
226+
| tests.cpp:459:5:459:31 | parameter_ref_to_return_ref |
227+
| tests.cpp:459:45:459:45 | x |
228+
| tests.cpp:459:45:459:45 | x |
229+
| tests.cpp:462:6:462:37 | test_parameter_ref_to_return_ref |
230+
| tests.cpp:463:6:463:6 | x |
231+
| tests.cpp:464:36:464:36 | s |
232+
| tests.cpp:465:6:465:6 | y |

cpp/ql/test/library-tests/dataflow/models-as-data/testModels.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ private class TestSummaries extends SummaryModelCsv {
9898
";;false;madCallArg0ReturnToReturnFirst;;;Argument[0].ReturnValue;ReturnValue.Field[first];value",
9999
";;false;madCallArg0WithValue;;;Argument[1];Argument[0].Parameter[0];value",
100100
";;false;madCallReturnValueIgnoreFunction;;;Argument[1];ReturnValue;value",
101+
";StructWithTypedefInParameter<T>;true;parameter_ref_to_return_ref;(const T &);;Argument[*0];ReturnValue[*];value"
101102
]
102103
}
103104
}

cpp/ql/test/library-tests/dataflow/models-as-data/tests.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,3 +452,16 @@ void test_function_pointers() {
452452
madCallReturnValueIgnoreFunction(&sink, source());
453453
sink(madCallReturnValueIgnoreFunction(&dontUseValue, source())); // $ ir
454454
}
455+
456+
template<typename X>
457+
struct StructWithTypedefInParameter {
458+
typedef X Type;
459+
X& parameter_ref_to_return_ref(const Type& x); // $ interpretElement
460+
};
461+
462+
void test_parameter_ref_to_return_ref() {
463+
int x = source();
464+
StructWithTypedefInParameter<int> s;
465+
int y = s.parameter_ref_to_return_ref(x);
466+
sink(y); // $ ir
467+
}

0 commit comments

Comments
 (0)