Skip to content

Commit d17ae16

Browse files
Merge branch 'github:main' into main-1
2 parents 9639d08 + fcb4703 commit d17ae16

File tree

61 files changed

+3144
-1344
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+3144
-1344
lines changed

cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql

+16-2
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,17 @@ predicate interestingConcatenation(DataFlow::Node incoming, DataFlow::Node outgo
4949
call.getTarget() = op and
5050
op.hasQualifiedName("std", "operator+") and
5151
op.getType().(UserType).hasQualifiedName("std", "basic_string") and
52-
incoming.asIndirectArgument() = call.getArgument(1) and // left operand
52+
incoming.asIndirectArgument() = call.getArgument(1) and // right operand
5353
call = outgoing.asInstruction().getUnconvertedResultExpression()
5454
)
5555
}
5656

57+
/**
58+
* A state will represent the most recent concatenation that occurred in the data flow.
59+
* - `TConcatState` if the concetenation has not yet occurred.
60+
* - `TExecState(incoming, outgoing)`, representing the concatenation of data from `incoming`
61+
* into result `outgoing`.
62+
*/
5763
newtype TState =
5864
TConcatState() or
5965
TExecState(DataFlow::Node incoming, DataFlow::Node outgoing) {
@@ -74,7 +80,9 @@ class ExecState extends TExecState {
7480

7581
DataFlow::Node getOutgoingNode() { result = outgoing }
7682

77-
/** Holds if this is a possible `ExecState` for `sink`. */
83+
/**
84+
* Holds if this is a possible `ExecState` at `sink`, that is, if `outgoing` flows to `sink`.
85+
*/
7886
predicate isFeasibleForSink(DataFlow::Node sink) { ExecState::flow(outgoing, sink) }
7987

8088
string toString() { result = "ExecState" }
@@ -110,6 +118,12 @@ module ExecStateConfig implements DataFlow::ConfigSig {
110118

111119
module ExecState = TaintTracking::Global<ExecStateConfig>;
112120

121+
/**
122+
* A full `TaintTracking` configuration from source to concatenation to sink, using a flow
123+
* state to remember the concatenation. It's important that we track flow to the sink even though
124+
* as soon as we reach the concatenation we know it will get there (due to the check of
125+
* `isFeasibleForSink`), because this way we get a complete flow path.
126+
*/
113127
module ExecTaintConfig implements DataFlow::StateConfigSig {
114128
class FlowState = TState;
115129

cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/ExecTainted.expected

+205-153
Large diffs are not rendered by default.

cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/test.cpp

+46-7
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ extern void encodeShellString(char *shellStr, int maxChars, const char* cStr);
1414

1515
int main(int argc, char** argv) {
1616
char *userName = argv[2];
17-
17+
1818
{
1919
// BAD: a string from the user is injected directly into
2020
// a command.
@@ -23,10 +23,10 @@ int main(int argc, char** argv) {
2323
system(command1);
2424
}
2525

26-
{
26+
{
2727
// GOOD: the user string is encoded by a library routine.
2828
char userNameQuoted[1000] = {0};
29-
encodeShellString(userNameQuoted, 1000, userName);
29+
encodeShellString(userNameQuoted, 1000, userName);
3030
char command2[1000] = {0};
3131
sprintf(command2, "userinfo -v %s", userNameQuoted);
3232
system(command2);
@@ -36,16 +36,16 @@ int main(int argc, char** argv) {
3636
void test2(char* arg2) {
3737
// GOOD?: the user string is the *first* part of the command, like $CC in many environments
3838
char *envCC = getenv("CC");
39-
39+
4040
char command[1000];
41-
sprintf("%s %s", envCC, arg2);
41+
sprintf(command, "%s %s", envCC, arg2);
4242
system(command);
4343
}
4444

4545
void test3(char* arg1) {
4646
// GOOD?: the user string is a `$CFLAGS` environment variable
4747
char *envCflags = getenv("CFLAGS");
48-
48+
4949
char command[1000];
5050
sprintf(command, "%s %s", arg1, envCflags);
5151
system(command);
@@ -54,6 +54,7 @@ void test3(char* arg1) {
5454
typedef unsigned long size_t;
5555
typedef void FILE;
5656
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
57+
char *strncpy(char *s1, const char *s2, size_t n);
5758
char *strncat(char *s1, const char *s2, size_t n);
5859

5960
void test4(FILE *f) {
@@ -160,7 +161,7 @@ void test15(FILE *f) {
160161
fread(temp, 1, 10, f);
161162

162163
int x = atoi(temp);
163-
164+
164165
char temp2[10];
165166
sprintf(temp2, "%d", x);
166167
sprintf(command, "tail -n %s foo.log", temp2);
@@ -222,4 +223,42 @@ void test19(FILE *f) {
222223
execl("/bin/sh", "sh", "-c", command);
223224
}
224225

226+
void test20() {
227+
// BAD: the user strings `var_b`, `var_c` are injected directly into a command
228+
char buffer[1024 * 4];
229+
230+
strncpy(buffer, getenv("var_a"), 1024);
231+
strncat(buffer, getenv("var_b"), 1024);
232+
strncat(buffer, getenv("var_c"), 1024);
233+
strncat(buffer, " ", 1024);
234+
system(buffer);
235+
}
236+
237+
void test21() {
238+
// BAD: the user strings `var_b`, `var_c` are injected directly into a command
239+
char buffer1[1024];
240+
char buffer2[1024];
241+
242+
sprintf(buffer1, "%s %s",
243+
getenv("var_a"),
244+
getenv("var_b"));
245+
sprintf(buffer2, "%s %s %s",
246+
" ",
247+
buffer1,
248+
getenv("var_c"));
249+
system(buffer2);
250+
}
251+
252+
void test22() {
253+
// BAD: the user strings `var_a` are injected directly into a command
254+
char buffer[1024 * 11];
255+
int i;
256+
257+
strncpy(buffer, "command ", 1024);
258+
for (i = 0; i < 10; i++) {
259+
strncat(buffer, getenv("var_a"), 1024);
260+
}
261+
system(buffer);
262+
}
263+
225264
// open question: do we want to report certain sources even when they're the start of the string?

csharp/ql/lib/CHANGELOG.md

-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@
6868

6969
### Minor Analysis Improvements
7070

71-
* .NET 9 is now required to build the C# extractor.
7271
* The Models as Data models for .NET 8 Runtime now include generated models for higher order methods.
7372

7473
## 3.1.0

csharp/ql/lib/change-notes/released/3.1.1.md

-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,4 @@
22

33
### Minor Analysis Improvements
44

5-
* .NET 9 is now required to build the C# extractor.
65
* The Models as Data models for .NET 8 Runtime now include generated models for higher order methods.

csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll

+11-32
Original file line numberDiff line numberDiff line change
@@ -734,15 +734,15 @@ private predicate variableReadPseudo(ControlFlow::BasicBlock bb, int i, Ssa::Sou
734734
}
735735

736736
pragma[noinline]
737-
private predicate adjacentDefRead(
737+
deprecated private predicate adjacentDefRead(
738738
Definition def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2,
739739
SsaInput::SourceVariable v
740740
) {
741741
Impl::adjacentDefRead(def, bb1, i1, bb2, i2) and
742742
v = def.getSourceVariable()
743743
}
744744

745-
private predicate adjacentDefReachesRead(
745+
deprecated private predicate adjacentDefReachesRead(
746746
Definition def, SsaInput::SourceVariable v, SsaInput::BasicBlock bb1, int i1,
747747
SsaInput::BasicBlock bb2, int i2
748748
) {
@@ -760,18 +760,7 @@ private predicate adjacentDefReachesRead(
760760
)
761761
}
762762

763-
/** Same as `adjacentDefRead`, but skips uncertain reads. */
764-
pragma[nomagic]
765-
private predicate adjacentDefSkipUncertainReads(
766-
Definition def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2
767-
) {
768-
exists(SsaInput::SourceVariable v |
769-
adjacentDefReachesRead(def, v, bb1, i1, bb2, i2) and
770-
SsaInput::variableRead(bb2, i2, v, true)
771-
)
772-
}
773-
774-
private predicate adjacentDefReachesUncertainRead(
763+
deprecated private predicate adjacentDefReachesUncertainRead(
775764
Definition def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2
776765
) {
777766
exists(SsaInput::SourceVariable v |
@@ -933,10 +922,8 @@ private module Cached {
933922
*/
934923
cached
935924
predicate firstReadSameVar(Definition def, ControlFlow::Node cfn) {
936-
exists(ControlFlow::BasicBlock bb1, int i1, ControlFlow::BasicBlock bb2, int i2 |
937-
def.definesAt(_, bb1, i1) and
938-
adjacentDefSkipUncertainReads(def, bb1, i1, bb2, i2) and
939-
cfn = bb2.getNode(i2)
925+
exists(ControlFlow::BasicBlock bb, int i |
926+
Impl::firstUse(def, bb, i, true) and cfn = bb.getNode(i)
940927
)
941928
}
942929

@@ -947,25 +934,17 @@ private module Cached {
947934
*/
948935
cached
949936
predicate adjacentReadPairSameVar(Definition def, ControlFlow::Node cfn1, ControlFlow::Node cfn2) {
950-
exists(ControlFlow::BasicBlock bb1, int i1, ControlFlow::BasicBlock bb2, int i2 |
937+
exists(
938+
ControlFlow::BasicBlock bb1, int i1, ControlFlow::BasicBlock bb2, int i2,
939+
Ssa::SourceVariable v
940+
|
941+
Impl::ssaDefReachesRead(v, def, bb1, i1) and
942+
Impl::adjacentUseUse(bb1, i1, bb2, i2, v, true) and
951943
cfn1 = bb1.getNode(i1) and
952-
variableReadActual(bb1, i1, _) and
953-
adjacentDefSkipUncertainReads(def, bb1, i1, bb2, i2) and
954944
cfn2 = bb2.getNode(i2)
955945
)
956946
}
957947

958-
cached
959-
predicate lastRefBeforeRedef(Definition def, ControlFlow::BasicBlock bb, int i, Definition next) {
960-
Impl::lastRefRedef(def, bb, i, next) and
961-
not SsaInput::variableRead(bb, i, def.getSourceVariable(), false)
962-
or
963-
exists(SsaInput::BasicBlock bb0, int i0 |
964-
Impl::lastRefRedef(def, bb0, i0, next) and
965-
adjacentDefReachesUncertainRead(def, bb, i, bb0, i0)
966-
)
967-
}
968-
969948
cached
970949
Definition uncertainWriteDefinitionInput(UncertainWriteDefinition def) {
971950
Impl::uncertainWriteDefinitionInput(def, result)

0 commit comments

Comments
 (0)