Fix invalid memory uses in libdnf::ConfigParser::substitute_expression()

Resolves: RHEL-36777
This commit is contained in:
Petr Písař 2024-07-16 12:25:17 +02:00
parent b20b2dbef5
commit 4d9a633ed1
3 changed files with 176 additions and 1 deletions

View File

@ -0,0 +1,128 @@
From 1f6c2479260e2d26a861b871c443a5b960523a71 Mon Sep 17 00:00:00 2001
From: Evan Goode <mail@evangoo.de>
Date: Tue, 7 May 2024 16:33:03 +0000
Subject: [PATCH] ConfigParser: fix use-out-of-scope leaks
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Petr Písař <ppisar@redhat.com>
---
libdnf/conf/ConfigParser.cpp | 48 ++++++++++++++++++++++++------------
1 file changed, 32 insertions(+), 16 deletions(-)
diff --git a/libdnf/conf/ConfigParser.cpp b/libdnf/conf/ConfigParser.cpp
index 76e7f9cc..977da757 100644
--- a/libdnf/conf/ConfigParser.cpp
+++ b/libdnf/conf/ConfigParser.cpp
@@ -95,7 +95,9 @@ std::pair<std::string, size_t> ConfigParser::substitute_expression(const std::st
const auto & variable_key = res.substr(pos_variable, pos_after_variable - pos_variable);
const auto variable_mapping = substitutions.find(variable_key);
- const std::string * variable_value = nullptr;
+ // No std::optional here.
+ bool variable_value_has_value{false};
+ std::string variable_value{""};
if (variable_mapping == substitutions.end()) {
if (variable_key == "releasever_major" || variable_key == "releasever_minor") {
@@ -103,17 +105,22 @@ std::pair<std::string, size_t> ConfigParser::substitute_expression(const std::st
if (releasever_mapping != substitutions.end()) {
const auto & releasever_split = ConfigParser::split_releasever(releasever_mapping->second);
if (variable_key == "releasever_major") {
- variable_value = &std::get<0>(releasever_split);
+ variable_value = std::get<0>(releasever_split);
+ variable_value_has_value = true;
} else {
- variable_value = &std::get<1>(releasever_split);
+ variable_value = std::get<1>(releasever_split);
+ variable_value_has_value = true;
}
}
}
} else {
- variable_value = &variable_mapping->second;
+ variable_value = variable_mapping->second;
+ variable_value_has_value = true;
}
- const std::string * subst_str = nullptr;
+ // No std::optional here
+ std::string subst_str{""};
+ bool subst_str_has_value{false};
size_t pos_after_variable_expression;
@@ -153,20 +160,23 @@ std::pair<std::string, size_t> ConfigParser::substitute_expression(const std::st
// If variable is unset or empty, the expansion of word is
// substituted. Otherwise, the value of variable is
// substituted.
- if (variable_value == nullptr || variable_value->empty()) {
- subst_str = &expanded_word;
+ if (!variable_value_has_value || variable_value.empty()) {
+ subst_str = expanded_word;
+ subst_str_has_value = true;
} else {
subst_str = variable_value;
+ subst_str_has_value = true;
}
} else if (expansion_mode == '+') {
// ${variable:+word} (alternate value)
// If variable is unset or empty nothing is substituted.
// Otherwise, the expansion of word is substituted.
- if (variable_value == nullptr || variable_value->empty()) {
- const std::string empty{};
- subst_str = &empty;
+ if (!variable_value_has_value || variable_value.empty()) {
+ subst_str = "";
+ subst_str_has_value = true;
} else {
- subst_str = &expanded_word;
+ subst_str = expanded_word;
+ subst_str_has_value = true;
}
} else {
// Unknown expansion mode, continue after the ':'
@@ -176,7 +186,10 @@ std::pair<std::string, size_t> ConfigParser::substitute_expression(const std::st
pos_after_variable_expression = pos_after_word + 1;
} else if (res[pos_after_variable] == '}') {
// ${variable}
- subst_str = variable_value;
+ if (variable_value_has_value) {
+ subst_str = variable_value;
+ subst_str_has_value = true;
+ }
// Move past the closing '}'
pos_after_variable_expression = pos_after_variable + 1;
} else {
@@ -186,20 +199,23 @@ std::pair<std::string, size_t> ConfigParser::substitute_expression(const std::st
}
} else {
// No braces, we have a $variable
- subst_str = variable_value;
+ if (variable_value_has_value) {
+ subst_str = variable_value;
+ subst_str_has_value = true;
+ }
pos_after_variable_expression = pos_after_variable;
}
// If there is no substitution to make, move past the variable expression and continue.
- if (subst_str == nullptr) {
+ if (!subst_str_has_value) {
total_scanned += pos_after_variable_expression - pos;
pos = pos_after_variable_expression;
continue;
}
- res.replace(pos, pos_after_variable_expression - pos, *subst_str);
+ res.replace(pos, pos_after_variable_expression - pos, subst_str);
total_scanned += pos_after_variable_expression - pos;
- pos += subst_str->length();
+ pos += subst_str.length();
} else {
total_scanned += 1;
pos += 1;
--
2.45.2

View File

@ -0,0 +1,42 @@
From 18f49a49be14f80233613710e84dda47c5958252 Mon Sep 17 00:00:00 2001
From: Evan Goode <mail@evangoo.de>
Date: Tue, 7 May 2024 16:28:59 +0000
Subject: [PATCH] Add tests for shell-style variable expansion
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Petr Písař <ppisar@redhat.com>
---
tests/libdnf/conf/ConfigParserTest.cpp | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/tests/libdnf/conf/ConfigParserTest.cpp b/tests/libdnf/conf/ConfigParserTest.cpp
index 70278196..1448d8d3 100644
--- a/tests/libdnf/conf/ConfigParserTest.cpp
+++ b/tests/libdnf/conf/ConfigParserTest.cpp
@@ -30,4 +30,21 @@ void ConfigParserTest::testConfigParserReleasever()
libdnf::ConfigParser::substitute(text, substitutions);
CPPUNIT_ASSERT(text == "major: , minor: ");
}
+ {
+ std::map<std::string, std::string> substitutions = {
+ {"var1", "value123"},
+ {"var2", "456"},
+ };
+ std::string text = "foo$var1-bar";
+ libdnf::ConfigParser::substitute(text, substitutions);
+ CPPUNIT_ASSERT(text == "foovalue123-bar");
+
+ text = "${var1:+alternate}-${unset:-default}-${nn:+n${nn:-${nnn:}";
+ libdnf::ConfigParser::substitute(text, substitutions);
+ CPPUNIT_ASSERT(text == "alternate-default-${nn:+n${nn:-${nnn:}");
+
+ text = "${unset:-${var1:+${var2:+$var2}}}";
+ libdnf::ConfigParser::substitute(text, substitutions);
+ CPPUNIT_ASSERT(text == "456");
+ }
}
--
2.45.2

View File

@ -56,7 +56,7 @@
Name: libdnf
Version: %{libdnf_major_version}.%{libdnf_minor_version}.%{libdnf_micro_version}
Release: 4%{?dist}
Release: 5%{?dist}
Summary: Library providing simplified C and Python API to libsolv
License: LGPL-2.1-or-later
URL: https://github.com/rpm-software-management/libdnf
@ -65,6 +65,8 @@ Patch1: 0001-context-use-rpmtsAddReinstallElement-when-doing-a-re.patch
Patch2: 0002-Since-we-use-rpmtsAddReinstallElement-rpm-also-unins.patch
Patch3: 0003-MergedTransaction-Fix-invalid-memory-access-when-dro.patch
Patch4: 0004-Set-pool-flag-to-fix-pool_addfileprovides_queue-with.patch
Patch5: 0005-ConfigParser-fix-use-out-of-scope-leaks.patch
Patch6: 0006-Add-tests-for-shell-style-variable-expansion.patch
BuildRequires: cmake
BuildRequires: gcc
@ -308,6 +310,9 @@ popd
%endif
%changelog
* Tue Jul 16 2024 Petr Pisar <ppisar@redhat.com> - 0.73.1-5
- Fix invalid memory uses in libdnf::ConfigParser::substitute_expression() (RHEL-36777)
* Tue Jul 02 2024 Petr Pisar <ppisar@redhat.com> - 0.73.1-4
- Fix sack initialization to enable postponed addition of filelists (RHEL-12355)