Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

C++: Add Windows command line and environment models #19563

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions 6 cpp/ql/lib/change-notes/2025-05-23-windows-sources.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
category: feature
---
* Added the `pCmdLine` arguments of `WinMain` and `wWinMain` as local flow sources.
* Added source models for `GetCommandLineA`, `GetCommandLineW`, `GetEnvironmentStringsA`, `GetEnvironmentStringsW`, `GetEnvironmentVariableA`, and `GetEnvironmentVariableW`.
* Added summary models for `CommandLineToArgvA` and `CommandLineToArgvW`.
20 changes: 20 additions & 0 deletions 20 cpp/ql/lib/ext/Windows.model.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# partial model of windows system calls
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# partial model of windows system calls
# partial model of windows system calls

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledging I saw this. Will fix this in a separate PR.

extensions:
- addsTo:
pack: codeql/cpp-all
extensible: sourceModel
data: # namespace, type, subtypes, name, signature, ext, output, kind, provenance
# processenv.h
- ["", "", False, "GetCommandLineA", "", "", "ReturnValue[*]", "local", "manual"]
- ["", "", False, "GetCommandLineW", "", "", "ReturnValue[*]", "local", "manual"]
- ["", "", False, "GetEnvironmentStringsA", "", "", "ReturnValue[*]", "local", "manual"]
- ["", "", False, "GetEnvironmentStringsW", "", "", "ReturnValue[*]", "local", "manual"]
- ["", "", False, "GetEnvironmentVariableA", "", "", "Argument[*1]", "local", "manual"]
- ["", "", False, "GetEnvironmentVariableW", "", "", "Argument[*1]", "local", "manual"]
- addsTo:
pack: codeql/cpp-all
extensible: summaryModel
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
# shellapi.h
- ["", "", False, "CommandLineToArgvA", "", "", "Argument[*0]", "ReturnValue[**]", "taint", "manual"]
- ["", "", False, "CommandLineToArgvW", "", "", "Argument[*0]", "ReturnValue[**]", "taint", "manual"]
17 changes: 16 additions & 1 deletion 17 cpp/ql/lib/semmle/code/cpp/security/FlowSources.qll
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ private class LocalModelSource extends LocalFlowSource {
}

/**
* A local data flow source that the `argv` parameter to `main` or `wmain`.
* A local data flow source that is the `argv` parameter to `main` or `wmain`.
*/
private class ArgvSource extends LocalFlowSource {
ArgvSource() {
Expand All @@ -69,6 +69,21 @@ private class ArgvSource extends LocalFlowSource {
override string getSourceType() { result = "a command-line argument" }
}

/**
* A local data flow source that is the `pCmdLine` parameter to `WinMain` or `wWinMain`.
*/
private class CmdLineSource extends LocalFlowSource {
CmdLineSource() {
exists(Function main, Parameter pCmdLine |
main.hasGlobalName(["WinMain", "wWinMain"]) and
main.getParameter(2) = pCmdLine and
this.asParameter(1) = pCmdLine
jketema marked this conversation as resolved.
Show resolved Hide resolved
)
}

override string getSourceType() { result = "a command-line" }
}

/**
* A remote data flow source that is defined through 'models as data'.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,11 @@ module IRTest {

/** Common data flow configuration to be used by tests. */
module IRTestAllocationConfig implements DataFlow::ConfigSig {
private import semmle.code.cpp.security.FlowSources

predicate isSource(DataFlow::Node source) {
source instanceof FlowSource
or
source.asExpr().(FunctionCall).getTarget().getName() = "source"
or
source.asIndirectExpr(1).(FunctionCall).getTarget().getName() = "indirect_source"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,3 +337,4 @@ irFlow
| true_upon_entry.cpp:70:11:70:16 | call to source | true_upon_entry.cpp:78:8:78:8 | x |
| true_upon_entry.cpp:83:11:83:16 | call to source | true_upon_entry.cpp:86:8:86:8 | x |
| true_upon_entry.cpp:98:11:98:16 | call to source | true_upon_entry.cpp:105:8:105:8 | x |
| winmain.cpp:4:57:4:64 | *pCmdLine | winmain.cpp:6:8:6:16 | * ... |
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
void sink(char);
void sink(char*);

int WinMain(void *hInstance, void *hPrevInstance, char *pCmdLine, int nCmdShow) { // $ ast-def=hInstance ast-def=hPrevInstance ast-def=pCmdLine ir-def=*hInstance ir-def=*hPrevInstance ir-def=*pCmdLine
sink(pCmdLine);
sink(*pCmdLine); // $ ir

return 0;
}
50 changes: 38 additions & 12 deletions 50 cpp/ql/test/library-tests/dataflow/external-models/flow.expected
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,44 @@ edges
| asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:103:29:103:39 | *send_buffer | provenance | Sink:MaD:6 |
| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:56:18:56:23 | [summary param] *0 in buffer | provenance | |
| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | MaD:10 |
| test.cpp:4:5:4:17 | [summary param] 0 in ymlStepManual | test.cpp:4:5:4:17 | [summary] to write: ReturnValue in ymlStepManual | provenance | MaD:23489 |
| test.cpp:5:5:5:20 | [summary param] 0 in ymlStepGenerated | test.cpp:5:5:5:20 | [summary] to write: ReturnValue in ymlStepGenerated | provenance | MaD:23490 |
| test.cpp:6:5:6:27 | [summary param] 0 in ymlStepManual_with_body | test.cpp:6:5:6:27 | [summary] to write: ReturnValue in ymlStepManual_with_body | provenance | MaD:23491 |
| test.cpp:4:5:4:17 | [summary param] 0 in ymlStepManual | test.cpp:4:5:4:17 | [summary] to write: ReturnValue in ymlStepManual | provenance | MaD:23497 |
| test.cpp:5:5:5:20 | [summary param] 0 in ymlStepGenerated | test.cpp:5:5:5:20 | [summary] to write: ReturnValue in ymlStepGenerated | provenance | MaD:23498 |
| test.cpp:6:5:6:27 | [summary param] 0 in ymlStepManual_with_body | test.cpp:6:5:6:27 | [summary] to write: ReturnValue in ymlStepManual_with_body | provenance | MaD:23499 |
| test.cpp:7:47:7:52 | value2 | test.cpp:7:64:7:69 | value2 | provenance | |
| test.cpp:7:64:7:69 | value2 | test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | provenance | |
| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:10:10:10:18 | call to ymlSource | provenance | Src:MaD:23487 |
| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:14:10:14:10 | x | provenance | Sink:MaD:23488 |
| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:10:10:10:18 | call to ymlSource | provenance | Src:MaD:23495 |
| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:14:10:14:10 | x | provenance | Sink:MaD:23496 |
| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:17:24:17:24 | x | provenance | |
| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:21:27:21:27 | x | provenance | |
| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:25:35:25:35 | x | provenance | |
| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:32:41:32:41 | x | provenance | |
| test.cpp:17:10:17:22 | call to ymlStepManual | test.cpp:17:10:17:22 | call to ymlStepManual | provenance | |
| test.cpp:17:10:17:22 | call to ymlStepManual | test.cpp:18:10:18:10 | y | provenance | Sink:MaD:23488 |
| test.cpp:17:10:17:22 | call to ymlStepManual | test.cpp:18:10:18:10 | y | provenance | Sink:MaD:23496 |
| test.cpp:17:24:17:24 | x | test.cpp:4:5:4:17 | [summary param] 0 in ymlStepManual | provenance | |
| test.cpp:17:24:17:24 | x | test.cpp:17:10:17:22 | call to ymlStepManual | provenance | MaD:23489 |
| test.cpp:17:24:17:24 | x | test.cpp:17:10:17:22 | call to ymlStepManual | provenance | MaD:23497 |
| test.cpp:21:10:21:25 | call to ymlStepGenerated | test.cpp:21:10:21:25 | call to ymlStepGenerated | provenance | |
| test.cpp:21:10:21:25 | call to ymlStepGenerated | test.cpp:22:10:22:10 | z | provenance | Sink:MaD:23488 |
| test.cpp:21:10:21:25 | call to ymlStepGenerated | test.cpp:22:10:22:10 | z | provenance | Sink:MaD:23496 |
| test.cpp:21:27:21:27 | x | test.cpp:5:5:5:20 | [summary param] 0 in ymlStepGenerated | provenance | |
| test.cpp:21:27:21:27 | x | test.cpp:21:10:21:25 | call to ymlStepGenerated | provenance | MaD:23490 |
| test.cpp:21:27:21:27 | x | test.cpp:21:10:21:25 | call to ymlStepGenerated | provenance | MaD:23498 |
| test.cpp:25:11:25:33 | call to ymlStepManual_with_body | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | provenance | |
| test.cpp:25:11:25:33 | call to ymlStepManual_with_body | test.cpp:26:10:26:11 | y2 | provenance | Sink:MaD:23488 |
| test.cpp:25:11:25:33 | call to ymlStepManual_with_body | test.cpp:26:10:26:11 | y2 | provenance | Sink:MaD:23496 |
| test.cpp:25:35:25:35 | x | test.cpp:6:5:6:27 | [summary param] 0 in ymlStepManual_with_body | provenance | |
| test.cpp:25:35:25:35 | x | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | provenance | MaD:23491 |
| test.cpp:25:35:25:35 | x | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | provenance | MaD:23499 |
| test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | provenance | |
| test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | test.cpp:33:10:33:11 | z2 | provenance | Sink:MaD:23488 |
| test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | test.cpp:33:10:33:11 | z2 | provenance | Sink:MaD:23496 |
| test.cpp:32:41:32:41 | x | test.cpp:7:47:7:52 | value2 | provenance | |
| test.cpp:32:41:32:41 | x | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | provenance | |
| windows.cpp:6:8:6:25 | [summary param] *0 in CommandLineToArgvA | windows.cpp:6:8:6:25 | [summary] to write: ReturnValue[**] in CommandLineToArgvA | provenance | MaD:331 |
| windows.cpp:11:15:11:29 | *call to GetCommandLineA | windows.cpp:11:15:11:29 | *call to GetCommandLineA | provenance | Src:MaD:325 |
| windows.cpp:11:15:11:29 | *call to GetCommandLineA | windows.cpp:13:8:13:11 | * ... | provenance | |
| windows.cpp:11:15:11:29 | *call to GetCommandLineA | windows.cpp:16:36:16:38 | *cmd | provenance | |
| windows.cpp:16:17:16:34 | **call to CommandLineToArgvA | windows.cpp:16:17:16:34 | **call to CommandLineToArgvA | provenance | |
| windows.cpp:16:17:16:34 | **call to CommandLineToArgvA | windows.cpp:19:8:19:15 | * ... | provenance | |
| windows.cpp:16:36:16:38 | *cmd | windows.cpp:6:8:6:25 | [summary param] *0 in CommandLineToArgvA | provenance | |
| windows.cpp:16:36:16:38 | *cmd | windows.cpp:16:17:16:34 | **call to CommandLineToArgvA | provenance | MaD:331 |
| windows.cpp:23:17:23:38 | *call to GetEnvironmentStringsA | windows.cpp:23:17:23:38 | *call to GetEnvironmentStringsA | provenance | Src:MaD:327 |
| windows.cpp:23:17:23:38 | *call to GetEnvironmentStringsA | windows.cpp:25:10:25:13 | * ... | provenance | |
| windows.cpp:28:36:28:38 | GetEnvironmentVariableA output argument | windows.cpp:30:10:30:13 | * ... | provenance | Src:MaD:329 |
nodes
| asio_streams.cpp:56:18:56:23 | [summary param] *0 in buffer | semmle.label | [summary param] *0 in buffer |
| asio_streams.cpp:56:18:56:23 | [summary] to write: ReturnValue in buffer | semmle.label | [summary] to write: ReturnValue in buffer |
Expand Down Expand Up @@ -78,9 +89,24 @@ nodes
| test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | semmle.label | call to ymlStepGenerated_with_body |
| test.cpp:32:41:32:41 | x | semmle.label | x |
| test.cpp:33:10:33:11 | z2 | semmle.label | z2 |
| windows.cpp:6:8:6:25 | [summary param] *0 in CommandLineToArgvA | semmle.label | [summary param] *0 in CommandLineToArgvA |
| windows.cpp:6:8:6:25 | [summary] to write: ReturnValue[**] in CommandLineToArgvA | semmle.label | [summary] to write: ReturnValue[**] in CommandLineToArgvA |
| windows.cpp:11:15:11:29 | *call to GetCommandLineA | semmle.label | *call to GetCommandLineA |
| windows.cpp:11:15:11:29 | *call to GetCommandLineA | semmle.label | *call to GetCommandLineA |
| windows.cpp:13:8:13:11 | * ... | semmle.label | * ... |
| windows.cpp:16:17:16:34 | **call to CommandLineToArgvA | semmle.label | **call to CommandLineToArgvA |
| windows.cpp:16:17:16:34 | **call to CommandLineToArgvA | semmle.label | **call to CommandLineToArgvA |
| windows.cpp:16:36:16:38 | *cmd | semmle.label | *cmd |
| windows.cpp:19:8:19:15 | * ... | semmle.label | * ... |
| windows.cpp:23:17:23:38 | *call to GetEnvironmentStringsA | semmle.label | *call to GetEnvironmentStringsA |
| windows.cpp:23:17:23:38 | *call to GetEnvironmentStringsA | semmle.label | *call to GetEnvironmentStringsA |
| windows.cpp:25:10:25:13 | * ... | semmle.label | * ... |
| windows.cpp:28:36:28:38 | GetEnvironmentVariableA output argument | semmle.label | GetEnvironmentVariableA output argument |
| windows.cpp:30:10:30:13 | * ... | semmle.label | * ... |
subpaths
| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:56:18:56:23 | [summary param] *0 in buffer | asio_streams.cpp:56:18:56:23 | [summary] to write: ReturnValue in buffer | asio_streams.cpp:100:44:100:62 | call to buffer |
| test.cpp:17:24:17:24 | x | test.cpp:4:5:4:17 | [summary param] 0 in ymlStepManual | test.cpp:4:5:4:17 | [summary] to write: ReturnValue in ymlStepManual | test.cpp:17:10:17:22 | call to ymlStepManual |
| test.cpp:21:27:21:27 | x | test.cpp:5:5:5:20 | [summary param] 0 in ymlStepGenerated | test.cpp:5:5:5:20 | [summary] to write: ReturnValue in ymlStepGenerated | test.cpp:21:10:21:25 | call to ymlStepGenerated |
| test.cpp:25:35:25:35 | x | test.cpp:6:5:6:27 | [summary param] 0 in ymlStepManual_with_body | test.cpp:6:5:6:27 | [summary] to write: ReturnValue in ymlStepManual_with_body | test.cpp:25:11:25:33 | call to ymlStepManual_with_body |
| test.cpp:32:41:32:41 | x | test.cpp:7:47:7:52 | value2 | test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body |
| windows.cpp:16:36:16:38 | *cmd | windows.cpp:6:8:6:25 | [summary param] *0 in CommandLineToArgvA | windows.cpp:6:8:6:25 | [summary] to write: ReturnValue[**] in CommandLineToArgvA | windows.cpp:16:17:16:34 | **call to CommandLineToArgvA |
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
| asio_streams.cpp:87:34:87:44 | read_until output argument | remote |
| test.cpp:10:10:10:18 | call to ymlSource | local |
| windows.cpp:11:15:11:29 | *call to GetCommandLineA | local |
| windows.cpp:23:17:23:38 | *call to GetEnvironmentStringsA | local |
| windows.cpp:28:36:28:38 | GetEnvironmentVariableA output argument | local |
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
| test.cpp:28:35:28:35 | 0 | test.cpp:28:11:28:33 | call to ymlStepManual_with_body |
| test.cpp:32:38:32:38 | 0 | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body |
| test.cpp:35:38:35:38 | x | test.cpp:35:11:35:36 | call to ymlStepGenerated_with_body |
| windows.cpp:16:36:16:38 | *cmd | windows.cpp:16:17:16:34 | **call to CommandLineToArgvA |
31 changes: 31 additions & 0 deletions 31 cpp/ql/test/library-tests/dataflow/external-models/windows.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
void sink(char);
void sink(char*);
void sink(char**);

char* GetCommandLineA();
char** CommandLineToArgvA(char*, int*);
char* GetEnvironmentStringsA();
int GetEnvironmentVariableA(const char*, char*, int);

void getCommandLine() {
char* cmd = GetCommandLineA();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe these A and W variants are often called via a macro with no letter (in this case it would be GetCommandLine). It's probably fine that we test on the specific implementation functions and I suspect many users do in fact call them directly as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct.

sink(cmd);
sink(*cmd); // $ ir

int argc;
char** argv = CommandLineToArgvA(cmd, &argc);
sink(argv);
sink(argv[1]);
sink(*argv[1]); // $ ir
}

void getEnvironment() {
char* env = GetEnvironmentStringsA();
sink(env);
sink(*env); // $ ir

char buf[1024];
GetEnvironmentVariableA("FOO", buf, sizeof(buf));
sink(buf);
sink(*buf); // $ ir
}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.