diff --git a/cpp/ql/lib/change-notes/2025-05-23-windows-sources.md b/cpp/ql/lib/change-notes/2025-05-23-windows-sources.md new file mode 100644 index 000000000000..e07dcbe85988 --- /dev/null +++ b/cpp/ql/lib/change-notes/2025-05-23-windows-sources.md @@ -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`. diff --git a/cpp/ql/lib/ext/Windows.model.yml b/cpp/ql/lib/ext/Windows.model.yml new file mode 100644 index 000000000000..acac5f5fbf87 --- /dev/null +++ b/cpp/ql/lib/ext/Windows.model.yml @@ -0,0 +1,20 @@ + # partial model of windows system calls +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"] diff --git a/cpp/ql/lib/semmle/code/cpp/security/FlowSources.qll b/cpp/ql/lib/semmle/code/cpp/security/FlowSources.qll index b5e94d4c0463..eba6f9339ffe 100644 --- a/cpp/ql/lib/semmle/code/cpp/security/FlowSources.qll +++ b/cpp/ql/lib/semmle/code/cpp/security/FlowSources.qll @@ -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() { @@ -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 + ) + } + + override string getSourceType() { result = "a command-line" } +} + /** * A remote data flow source that is defined through 'models as data'. */ diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/TestBase.qll b/cpp/ql/test/library-tests/dataflow/dataflow-tests/TestBase.qll index b9213a71549f..e28d19133c75 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/TestBase.qll +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/TestBase.qll @@ -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" diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test-source-sink.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test-source-sink.expected index fa141b614ea6..323ce2a43121 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test-source-sink.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test-source-sink.expected @@ -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 | * ... | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/winmain.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/winmain.cpp new file mode 100644 index 000000000000..3f267e1e81b2 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/winmain.cpp @@ -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; +} diff --git a/cpp/ql/test/library-tests/dataflow/external-models/flow.expected b/cpp/ql/test/library-tests/dataflow/external-models/flow.expected index 2eb0844862d6..9992ca5a7213 100644 --- a/cpp/ql/test/library-tests/dataflow/external-models/flow.expected +++ b/cpp/ql/test/library-tests/dataflow/external-models/flow.expected @@ -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 | @@ -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 | diff --git a/cpp/ql/test/library-tests/dataflow/external-models/sources.expected b/cpp/ql/test/library-tests/dataflow/external-models/sources.expected index 3e71025e7ff1..1c21bf851219 100644 --- a/cpp/ql/test/library-tests/dataflow/external-models/sources.expected +++ b/cpp/ql/test/library-tests/dataflow/external-models/sources.expected @@ -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 | diff --git a/cpp/ql/test/library-tests/dataflow/external-models/steps.expected b/cpp/ql/test/library-tests/dataflow/external-models/steps.expected index aab2691cda1e..ccdec4aefcb2 100644 --- a/cpp/ql/test/library-tests/dataflow/external-models/steps.expected +++ b/cpp/ql/test/library-tests/dataflow/external-models/steps.expected @@ -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 | diff --git a/cpp/ql/test/library-tests/dataflow/external-models/windows.cpp b/cpp/ql/test/library-tests/dataflow/external-models/windows.cpp new file mode 100644 index 000000000000..dfa055fa1e88 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/external-models/windows.cpp @@ -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(); + 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 +}