Skip to content

fix: integer type limits, 2nd try #3421

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion headers/modsecurity/rule_with_operator.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ class RuleWithOperator : public RuleWithActions {

static void updateMatchedVars(Transaction *trasn, const std::string &key,
const std::string &value);
static void cleanMatchedVars(Transaction *trasn);


const std::string& getOperatorName() const;
Expand Down
2 changes: 2 additions & 0 deletions headers/modsecurity/rules_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ class RulesSet : public RulesSetProperties {
void debug(int level, const std::string &id, const std::string &uri,
const std::string &msg);

static void cleanMatchedVars(Transaction *trans);

RulesSetPhases m_rulesSetPhases;
private:
#ifndef NO_LOGS
Expand Down
164 changes: 130 additions & 34 deletions headers/modsecurity/rules_set_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@
*
*/

#ifdef WIN32
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
#endif

#ifdef __cplusplus
#include <ctime>
Expand All @@ -22,6 +30,8 @@
#include <list>
#include <set>
#include <cstring>
#include <limits>
#include <cstdint>
#endif


Expand Down Expand Up @@ -68,46 +78,132 @@ class Driver;
using modsecurity::debug_log::DebugLog;
using modsecurity::audit_log::AuditLog;

/** @ingroup ModSecurity_CPP_API */
class ConfigInt {
public:
ConfigInt() : m_set(false), m_value(0) { }
bool m_set;
int m_value;
// template for different numeric int types
template <typename T>
class ConfigValue {
public:
bool m_set = false;
T m_value = 0;

void merge(const ConfigInt *from) {
if (m_set == true || from->m_set == false) {
ConfigValue() = default;

void merge(const ConfigValue<T>* from) {
if (m_set || !from->m_set) {
return;
}
m_set = true;
m_value = from->m_value;
return;
}
};

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

class ConfigDouble {
public:
ConfigDouble() : m_set(false), m_value(0) { }
bool m_set;
double m_value;
// use an alias type because the template can convert both signed and unsigned int
using LimitSigned = std::conditional_t<std::is_signed_v<T>, std::int64_t, std::uint64_t>;
LimitSigned val;

void merge(const ConfigDouble *from) {
if (m_set == true || from->m_set == false) {
return;
// clear errno variable, wee need that later
errno = 0;

try {
if constexpr (std::is_signed_v<T>) {
val = static_cast<std::int64_t>(std::stoll(a));
} else {
val = static_cast<std::uint64_t>(std::stoull(a));
}
}
catch (const std::invalid_argument&) {
// probably can't occur, but we handle it anyway
set_error(errmsg, "Invalid number format (not numeric)");
return false;
}
catch (const std::out_of_range&) {
// the value is out of range, we can not handle it
set_error(errmsg, "Number out of range");
return false;
}
catch (...) { // NOSONAR
// we don't need to handle all exceptions, the engine's BISON parser
// does not allow other symbols than numbers
set_error(errmsg, "An unknown error occurred while parsing number.");
return false;
}

if (
// The first condition will be true when the value is bigger than int64/uint64 maximum value.
// The second condition checks whether the value fits into int64/uint64, but not
// into the designed type, e.g., uint32; in that case the errno will be 0, but
// we must check the value is not bigger than the defined maximum of the class.
(errno == ERANGE && val == std::numeric_limits<LimitSigned>::max())
||
(val > static_cast<LimitSigned>(maxValue()))
) {
set_error(errmsg, "Value is too big.");
return false;
}

if (
// same as above
(errno == ERANGE && val == std::numeric_limits<LimitSigned>::min())
||
(val < static_cast<LimitSigned>(minValue()))
) {
set_error(errmsg, "Value is too small.");
return false;
}

m_value = static_cast<T>(val);
m_set = true;
m_value = from->m_value;
return;
return true;

}

protected:
// derived classes must implement the maxValue
virtual T maxValue() const = 0;
// minValue is optional
virtual T minValue() const { return 0; }

private:
static inline void set_error(std::string* err, const char* msg) {
if (err) {
*err = msg;
}
}
};

/** @ingroup ModSecurity_CPP_API */

class ConfigInt : public ConfigValue<int32_t> {
protected:
int32_t minValue() const override {
return std::numeric_limits<int32_t>::min();
}
int32_t maxValue() const override {
return std::numeric_limits<int32_t>::max();
}
};

class ConfigUnsignedInt : public ConfigValue<uint32_t> {
protected:
uint32_t maxValue() const override {
return std::numeric_limits<uint32_t>::max();
}
};

class ConfigUnsignedLong : public ConfigValue<uint64_t> {
protected:
uint64_t maxValue() const override {
return std::numeric_limits<uint64_t>::max();
}
};


class ConfigString {
public:
ConfigString() : m_set(false), m_value("") { }
bool m_set;
std::string m_value;
bool m_set = false;
std::string m_value = "";
ConfigString() = default;

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

class ConfigSet {
public:
ConfigSet() : m_set(false), m_clear(false) { }
bool m_set;
bool m_clear;
bool m_set = false;
bool m_clear = false;
std::set<std::string> m_value;
ConfigSet() = default;
};


Expand Down Expand Up @@ -504,14 +600,14 @@ class RulesSetProperties {
ConfigXMLParseXmlIntoArgs m_secXMLParseXmlIntoArgs;
ConfigBoolean m_tmpSaveUploadedFiles;
ConfigBoolean m_uploadKeepFiles;
ConfigDouble m_argumentsLimit;
ConfigDouble m_requestBodyJsonDepthLimit;
ConfigDouble m_requestBodyLimit;
ConfigDouble m_requestBodyNoFilesLimit;
ConfigDouble m_responseBodyLimit;
ConfigInt m_pcreMatchLimit;
ConfigInt m_uploadFileLimit;
ConfigInt m_uploadFileMode;
ConfigUnsignedInt m_argumentsLimit;
ConfigUnsignedInt m_requestBodyJsonDepthLimit;
ConfigUnsignedLong m_requestBodyLimit;
ConfigUnsignedLong m_requestBodyNoFilesLimit;
ConfigUnsignedLong m_responseBodyLimit;
ConfigUnsignedInt m_pcreMatchLimit;
ConfigUnsignedInt m_uploadFileLimit;
ConfigUnsignedInt m_uploadFileMode;
DebugLog *m_debugLog;
OnFailedRemoteRulesAction m_remoteRulesActionOnFailed;
RuleEngine m_secRuleEngine;
Expand Down
Loading
Loading