Skip to content

Commit 1ff9f2a

Browse files
authored
Merge pull request #3421 from airween/v3/secreqbodylimit
fix: integer type limits, 2nd try
2 parents 690355b + 9fb75d2 commit 1ff9f2a

File tree

3 files changed

+1084
-952
lines changed

3 files changed

+1084
-952
lines changed

headers/modsecurity/rules_set_properties.h

Lines changed: 130 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@
1313
*
1414
*/
1515

16+
#ifdef WIN32
17+
#ifdef max
18+
#undef max
19+
#endif
20+
#ifdef min
21+
#undef min
22+
#endif
23+
#endif
1624

1725
#ifdef __cplusplus
1826
#include <ctime>
@@ -22,6 +30,8 @@
2230
#include <list>
2331
#include <set>
2432
#include <cstring>
33+
#include <limits>
34+
#include <cstdint>
2535
#endif
2636

2737

@@ -68,46 +78,132 @@ class Driver;
6878
using modsecurity::debug_log::DebugLog;
6979
using modsecurity::audit_log::AuditLog;
7080

71-
/** @ingroup ModSecurity_CPP_API */
72-
class ConfigInt {
73-
public:
74-
ConfigInt() : m_set(false), m_value(0) { }
75-
bool m_set;
76-
int m_value;
81+
// template for different numeric int types
82+
template <typename T>
83+
class ConfigValue {
84+
public:
85+
bool m_set = false;
86+
T m_value = 0;
7787

78-
void merge(const ConfigInt *from) {
79-
if (m_set == true || from->m_set == false) {
88+
ConfigValue() = default;
89+
90+
void merge(const ConfigValue<T>* from) {
91+
if (m_set || !from->m_set) {
8092
return;
8193
}
8294
m_set = true;
8395
m_value = from->m_value;
84-
return;
8596
}
86-
};
8797

98+
// default parser
99+
bool parse(const std::string& a, std::string* errmsg = nullptr) {
88100

89-
class ConfigDouble {
90-
public:
91-
ConfigDouble() : m_set(false), m_value(0) { }
92-
bool m_set;
93-
double m_value;
101+
// use an alias type because the template can convert both signed and unsigned int
102+
using LimitSigned = std::conditional_t<std::is_signed_v<T>, std::int64_t, std::uint64_t>;
103+
LimitSigned val;
94104

95-
void merge(const ConfigDouble *from) {
96-
if (m_set == true || from->m_set == false) {
97-
return;
105+
// clear errno variable, wee need that later
106+
errno = 0;
107+
108+
try {
109+
if constexpr (std::is_signed_v<T>) {
110+
val = static_cast<std::int64_t>(std::stoll(a));
111+
} else {
112+
val = static_cast<std::uint64_t>(std::stoull(a));
113+
}
114+
}
115+
catch (const std::invalid_argument&) {
116+
// probably can't occur, but we handle it anyway
117+
set_error(errmsg, "Invalid number format (not numeric)");
118+
return false;
119+
}
120+
catch (const std::out_of_range&) {
121+
// the value is out of range, we can not handle it
122+
set_error(errmsg, "Number out of range");
123+
return false;
124+
}
125+
catch (...) { // NOSONAR
126+
// we don't need to handle all exceptions, the engine's BISON parser
127+
// does not allow other symbols than numbers
128+
set_error(errmsg, "An unknown error occurred while parsing number.");
129+
return false;
130+
}
131+
132+
if (
133+
// The first condition will be true when the value is bigger than int64/uint64 maximum value.
134+
// The second condition checks whether the value fits into int64/uint64, but not
135+
// into the designed type, e.g., uint32; in that case the errno will be 0, but
136+
// we must check the value is not bigger than the defined maximum of the class.
137+
(errno == ERANGE && val == std::numeric_limits<LimitSigned>::max())
138+
||
139+
(val > static_cast<LimitSigned>(maxValue()))
140+
) {
141+
set_error(errmsg, "Value is too big.");
142+
return false;
143+
}
144+
145+
if (
146+
// same as above
147+
(errno == ERANGE && val == std::numeric_limits<LimitSigned>::min())
148+
||
149+
(val < static_cast<LimitSigned>(minValue()))
150+
) {
151+
set_error(errmsg, "Value is too small.");
152+
return false;
98153
}
154+
155+
m_value = static_cast<T>(val);
99156
m_set = true;
100-
m_value = from->m_value;
101-
return;
157+
return true;
158+
159+
}
160+
161+
protected:
162+
// derived classes must implement the maxValue
163+
virtual T maxValue() const = 0;
164+
// minValue is optional
165+
virtual T minValue() const { return 0; }
166+
167+
private:
168+
static inline void set_error(std::string* err, const char* msg) {
169+
if (err) {
170+
*err = msg;
171+
}
172+
}
173+
};
174+
175+
/** @ingroup ModSecurity_CPP_API */
176+
177+
class ConfigInt : public ConfigValue<int32_t> {
178+
protected:
179+
int32_t minValue() const override {
180+
return std::numeric_limits<int32_t>::min();
181+
}
182+
int32_t maxValue() const override {
183+
return std::numeric_limits<int32_t>::max();
184+
}
185+
};
186+
187+
class ConfigUnsignedInt : public ConfigValue<uint32_t> {
188+
protected:
189+
uint32_t maxValue() const override {
190+
return std::numeric_limits<uint32_t>::max();
191+
}
192+
};
193+
194+
class ConfigUnsignedLong : public ConfigValue<uint64_t> {
195+
protected:
196+
uint64_t maxValue() const override {
197+
return std::numeric_limits<uint64_t>::max();
102198
}
103199
};
104200

105201

106202
class ConfigString {
107203
public:
108-
ConfigString() : m_set(false), m_value("") { }
109-
bool m_set;
110-
std::string m_value;
204+
bool m_set = false;
205+
std::string m_value = "";
206+
ConfigString() = default;
111207

112208
void merge(const ConfigString *from) {
113209
if (m_set == true || from->m_set == false) {
@@ -122,10 +218,10 @@ class ConfigString {
122218

123219
class ConfigSet {
124220
public:
125-
ConfigSet() : m_set(false), m_clear(false) { }
126-
bool m_set;
127-
bool m_clear;
221+
bool m_set = false;
222+
bool m_clear = false;
128223
std::set<std::string> m_value;
224+
ConfigSet() = default;
129225
};
130226

131227

@@ -504,14 +600,14 @@ class RulesSetProperties {
504600
ConfigXMLParseXmlIntoArgs m_secXMLParseXmlIntoArgs;
505601
ConfigBoolean m_tmpSaveUploadedFiles;
506602
ConfigBoolean m_uploadKeepFiles;
507-
ConfigDouble m_argumentsLimit;
508-
ConfigDouble m_requestBodyJsonDepthLimit;
509-
ConfigDouble m_requestBodyLimit;
510-
ConfigDouble m_requestBodyNoFilesLimit;
511-
ConfigDouble m_responseBodyLimit;
512-
ConfigInt m_pcreMatchLimit;
513-
ConfigInt m_uploadFileLimit;
514-
ConfigInt m_uploadFileMode;
603+
ConfigUnsignedInt m_argumentsLimit;
604+
ConfigUnsignedInt m_requestBodyJsonDepthLimit;
605+
ConfigUnsignedLong m_requestBodyLimit;
606+
ConfigUnsignedLong m_requestBodyNoFilesLimit;
607+
ConfigUnsignedLong m_responseBodyLimit;
608+
ConfigUnsignedInt m_pcreMatchLimit;
609+
ConfigUnsignedInt m_uploadFileLimit;
610+
ConfigUnsignedInt m_uploadFileMode;
515611
DebugLog *m_debugLog;
516612
OnFailedRemoteRulesAction m_remoteRulesActionOnFailed;
517613
RuleEngine m_secRuleEngine;

0 commit comments

Comments
 (0)