Data-Flow Steps are Complex Steps that represent flows of data.
We will look at each one using our sample program X42
:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
if (argc > 1 && strcmp(argv[1], "42") == 0) {
fprintf(stderr, "It depends!\n");
exit(42);
}
printf("What is the meaning of life?\n");
exit(0);
}
reachableBy #
reachableBy
is a Data-Flow Step that returns sources for flows of data from sinks to sources.
joern> def source = cpg.method.name("main").parameter
joern> def sink = cpg.call.name("strcmp").argument
joern> sink.reachableBy(source).l
val res4: List[io.shiftleft.codepropertygraph.generated.nodes.MethodParameterIn] = List(
MethodParameterIn(
id = 15L,
closureBindingId = None,
code = "char *argv[]",
columnNumber = Some(value = 20),
dynamicTypeHintFullName = ArraySeq(),
evaluationStrategy = "BY_VALUE",
index = 2,
isVariadic = false,
lineNumber = Some(value = 6),
name = "argv",
order = 2,
possibleTypes = ArraySeq(),
typeFullName = "char[]*"
)
)
reachableByFlows #
reachableByFlows
is a Data-Flow Step that returns paths for flows of data from sinks to sources.
joern> def source = cpg.method.name("main").parameter
joern> def sink = cpg.call.name("strcmp").argument
joern> sink.reachableByFlows(source).p
val res0: List[String] = List(
"""
┌─────────────────┬────────────────────────────┬──────────┬──────┬─────┐
│nodeType │tracked │lineNumber│method│file │
├─────────────────┼────────────────────────────┼──────────┼──────┼─────┤
│MethodParameterIn│main(int argc, char *argv[])│5 │main │X42.c│
│Call │strcmp(argv[1], "42") │6 │main │X42.c│
└─────────────────┴────────────────────────────┴──────────┴──────┴─────┘
""")