winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 9c2c6351db480d0be383168cc9cf9c908b9809e7
parent b57977b9fb74b61f4b5a26847732af23fe6f2268
Author: yao-msft <50888816+yao-msft@users.noreply.github.com>
Date:   Wed,  5 Mar 2025 14:06:21 -0800

Add max nest level limit to yaml parser (#5275)

This fixes an oom issue found in fuzzing run.

Diffstat:
Msrc/AppInstallerCLITests/AppInstallerCLITests.vcxproj | 3+++
Msrc/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters | 3+++
Asrc/AppInstallerCLITests/TestData/ContainsTooManyNestedLayers.yaml | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerCLITests/Yaml.cpp | 5+++++
Msrc/AppInstallerSharedLib/YamlWrapper.cpp | 19+++++++++++++++++++
5 files changed, 131 insertions(+), 0 deletions(-)

diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -1040,6 +1040,9 @@ <CopyFileToFolders Include="TestData\ContainsEscapeControlCode.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ContainsTooManyNestedLayers.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> <CopyFileToFolders Include="TestData\ManifestV1_10-Bad-SchemaHeaderInvalid.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -1053,6 +1053,9 @@ <CopyFileToFolders Include="TestData\ContainsEscapeControlCode.yaml"> <Filter>TestData</Filter> </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ContainsTooManyNestedLayers.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> <CopyFileToFolders Include="TestData\ManifestV1_10-Bad-SchemaHeaderInvalid.yaml"> <Filter>TestData</Filter> </CopyFileToFolders> diff --git a/src/AppInstallerCLITests/TestData/ContainsTooManyNestedLayers.yaml b/src/AppInstallerCLITests/TestData/ContainsTooManyNestedLayers.yaml @@ -0,0 +1,101 @@ +- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - value diff --git a/src/AppInstallerCLITests/Yaml.cpp b/src/AppInstallerCLITests/Yaml.cpp @@ -173,3 +173,8 @@ TEST_CASE("YamlContainsEscapeControlCode", "[YAML]") { REQUIRE_THROWS_HR(Load(TestDataFile("ContainsEscapeControlCode.yaml")), APPINSTALLER_CLI_ERROR_LIBYAML_ERROR); } + +TEST_CASE("YamlContainsTooManyNestedLayers", "[YAML]") +{ + REQUIRE_THROWS_HR(Load(TestDataFile("ContainsTooManyNestedLayers.yaml")), APPINSTALLER_CLI_ERROR_YAML_DOC_BUILD_FAILED); +} diff --git a/src/AppInstallerSharedLib/YamlWrapper.cpp b/src/AppInstallerSharedLib/YamlWrapper.cpp @@ -151,6 +151,9 @@ namespace AppInstaller::YAML::Wrapper size_t childOffset = 0; }; + static int YAML_DOCUMENT_NEST_LEVEL_LIMIT = 100; + int nestLevel = 0; + std::stack<StackItem> resultStack; resultStack.emplace(root, &result); @@ -173,6 +176,12 @@ namespace AppInstaller::YAML::Wrapper break; case YAML_SEQUENCE_NODE: { + if (stackItem.childOffset == 0) + { + // We've entered the sequence. + nestLevel++; + } + yaml_node_item_t* child = stackItem.yamlNode->data.sequence.items.start + stackItem.childOffset++; if (child < stackItem.yamlNode->data.sequence.items.top) { @@ -184,11 +193,18 @@ namespace AppInstaller::YAML::Wrapper { // We've reached the end of the sequence pop = true; + nestLevel--; } break; } case YAML_MAPPING_NODE: { + if (stackItem.childOffset == 0) + { + // We've entered the mapping. + nestLevel++; + } + yaml_node_pair_t* child = stackItem.yamlNode->data.mapping.pairs.start + stackItem.childOffset++; if (child < stackItem.yamlNode->data.mapping.pairs.top) { @@ -207,6 +223,7 @@ namespace AppInstaller::YAML::Wrapper { // We've reached the end of the mapping pop = true; + nestLevel--; } break; } @@ -216,6 +233,8 @@ namespace AppInstaller::YAML::Wrapper { resultStack.pop(); } + + THROW_HR_IF_MSG(APPINSTALLER_CLI_ERROR_YAML_DOC_BUILD_FAILED, nestLevel > YAML_DOCUMENT_NEST_LEVEL_LIMIT, "Too many layers of nested nodes."); } return result;