Skip to content

Commit 5d7fd19

Browse files
committed
Add range checks for narrowing integer casts in xml::Reader
1 parent 25179bc commit 5d7fd19

2 files changed

Lines changed: 57 additions & 2 deletions

File tree

include/rfl/xml/Reader.hpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define RFL_XML_READER_HPP_
33

44
#include <exception>
5+
#include <limits>
56
#include <optional>
67
#include <pugixml.hpp>
78
#include <string>
@@ -100,10 +101,26 @@ struct Reader {
100101
const auto str = std::visit(get_value, _var.node_or_attribute_);
101102
try {
102103
if constexpr (std::is_unsigned<std::remove_cvref_t<T>>()) {
103-
return static_cast<T>(std::stoull(str));
104+
if (!str.empty() && str[0] == '-') {
105+
return error("Value '" + std::string(str) +
106+
"' is negative, cannot convert to unsigned integer.");
107+
}
108+
const auto val = std::stoull(str);
109+
if (val > static_cast<unsigned long long>(
110+
std::numeric_limits<T>::max())) {
111+
return error("Value '" + std::string(str) +
112+
"' is out of range for the target type.");
113+
}
114+
return static_cast<T>(val);
104115
}
105116
else {
106-
return static_cast<T>(std::stoll(str));
117+
const auto val = std::stoll(str);
118+
if (val < static_cast<long long>(std::numeric_limits<T>::min()) ||
119+
val > static_cast<long long>(std::numeric_limits<T>::max())) {
120+
return error("Value '" + std::string(str) +
121+
"' is out of range for the target type.");
122+
}
123+
return static_cast<T>(val);
107124
}
108125
} catch (std::exception& e) {
109126
return error("Could not cast '" + std::string(str) + "' to integer.");

tests/xml/test_regression_bugs.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,41 @@ TEST(xml_regression, read_uint64_large_value) {
6868
}
6969

7070
} // namespace test_xml_reader_int64
71+
72+
// xml::Reader narrowing: stoull/stoll + static_cast<T> silently truncates
73+
// for types narrower than 64-bit (e.g. int16_t, uint16_t).
74+
namespace test_xml_reader_narrowing {
75+
76+
struct WithInt16 {
77+
int16_t value;
78+
};
79+
80+
struct WithUint16 {
81+
uint16_t value;
82+
};
83+
84+
TEST(xml_regression, read_rejects_out_of_range_for_int16) {
85+
const auto xml = std::string(
86+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
87+
"<WithInt16><value>99999</value></WithInt16>");
88+
const auto res = rfl::xml::read<WithInt16>(xml);
89+
EXPECT_FALSE(res) << "99999 should be rejected for int16_t field";
90+
}
91+
92+
TEST(xml_regression, read_rejects_negative_for_uint16) {
93+
const auto xml = std::string(
94+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
95+
"<WithUint16><value>-1</value></WithUint16>");
96+
const auto res = rfl::xml::read<WithUint16>(xml);
97+
EXPECT_FALSE(res) << "-1 should be rejected for uint16_t field";
98+
}
99+
100+
TEST(xml_regression, read_rejects_large_negative_for_int16) {
101+
const auto xml = std::string(
102+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
103+
"<WithInt16><value>-99999</value></WithInt16>");
104+
const auto res = rfl::xml::read<WithInt16>(xml);
105+
EXPECT_FALSE(res) << "-99999 should be rejected for int16_t field";
106+
}
107+
108+
} // namespace test_xml_reader_narrowing

0 commit comments

Comments
 (0)