Implement RE2 fallback to libpcre

RE2 doesn't support certain features, like negative lookaround,
so when a regular expression cannot be compiled with RE2, it's
compiled with libpcre instead.

This has some runtime cost, as this fallback is implemented
with an extra heap object and virtual function calls.

When RE2 is not enabled, however, everything works as it did before.
This commit is contained in:
WGH
2019-01-24 19:58:12 +03:00
committed by Felipe Zimmerle
parent 8c7e71b22a
commit 211c081c7d
7 changed files with 159 additions and 20 deletions

View File

@@ -0,0 +1,44 @@
/*
* ModSecurity, http://www.modsecurity.org/
* Copyright (c) 2019
*
* You may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* If any of the files related to licensing are missing or if you have any
* other questions related to licensing please contact Trustwave Holdings, Inc.
* directly using the email address security@modsecurity.org.
*
*/
#ifndef SRC_REGEX_BACKEND_BACKEND_H_
#define SRC_REGEX_BACKEND_BACKEND_H_
#include <list>
#include <string>
#include "src/regex/regex_match.h"
namespace modsecurity {
namespace regex {
namespace backend {
class Backend {
public:
virtual ~Backend() {}
virtual bool ok() const = 0;
virtual std::list<RegexMatch> searchAll(const std::string& s) const = 0;
virtual int search(const std::string &s, RegexMatch *m) const = 0;
virtual int search(const std::string &s) const = 0;
virtual const std::string& getPattern() const = 0;
};
} // namespace backend
} // namespace regex
} // namespace modsecurity
#endif // SRC_REGEX_BACKEND_BACKEND_H_

View File

@@ -22,6 +22,7 @@
#include <string>
#include <list>
#include "src/regex/backend/backend.h"
#include "src/regex/regex_match.h"
#ifndef SRC_REGEX_BACKEND_PCRE_H_
@@ -36,7 +37,7 @@ namespace backend {
#define OVECCOUNT 30
class Pcre {
class Pcre : public Backend {
public:
explicit Pcre(const std::string& pattern_);
~Pcre();
@@ -45,12 +46,20 @@ class Pcre {
Pcre(const Pcre&) = delete;
Pcre& operator=(const Pcre&) = delete;
std::list<RegexMatch> searchAll(const std::string& s) const;
int search(const std::string &s, RegexMatch *m) const;
int search(const std::string &s) const;
virtual bool ok() const override {
return m_pc != NULL;
}
const std::string pattern;
std::list<RegexMatch> searchAll(const std::string& s) const override;
int search(const std::string &s, RegexMatch *m) const override;
int search(const std::string &s) const override;
virtual const std::string& getPattern() const override {
return pattern;
};
private:
const std::string pattern;
pcre *m_pc = NULL;
pcre_extra *m_pce = NULL;
};

View File

@@ -29,15 +29,18 @@ namespace backend {
static RE2::Options get_re2_options() {
RE2::Options res;
// Re2 is usually used with fallback to libpcre,
// so disable unnecessary stderr noise
res.set_log_errors(false);
res.set_dot_nl(true);
return res;
}
Re2::Re2(const std::string& pattern_)
: pattern(pattern_.empty() ? ".*" : pattern_),
re(pattern, get_re2_options())
Re2::Re2(const std::string& pattern)
: re(pattern.empty() ? ".*" : pattern, get_re2_options())
{
}

View File

@@ -20,6 +20,7 @@
#include <string>
#include <list>
#include "src/regex/backend/backend.h"
#include "src/regex/regex_match.h"
#ifndef SRC_REGEX_BACKEND_RE2_H_
@@ -31,7 +32,7 @@ namespace backend {
#ifdef WITH_RE2
class Re2 {
class Re2 : public Backend {
public:
explicit Re2(const std::string& pattern_);
@@ -39,11 +40,17 @@ class Re2 {
Re2(const Re2&) = delete;
Re2& operator=(const Re2&) = delete;
std::list<RegexMatch> searchAll(const std::string& s) const;
int search(const std::string &s, RegexMatch *m) const;
int search(const std::string &s) const;
virtual bool ok() const override {
return re.ok();
}
const std::string pattern;
std::list<RegexMatch> searchAll(const std::string& s) const override;
int search(const std::string &s, RegexMatch *m) const override;
int search(const std::string &s) const override;
virtual const std::string& getPattern() const override {
return re.pattern();
};
private:
const RE2 re;
};

View File

@@ -0,0 +1,72 @@
/*
* ModSecurity, http://www.modsecurity.org/
* Copyright (c) 2019
*
* You may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* If any of the files related to licensing are missing or if you have any
* other questions related to licensing please contact Trustwave Holdings, Inc.
* directly using the email address security@modsecurity.org.
*
*/
#ifndef SRC_REGEX_BACKEND_FALLBACK_H_
#define SRC_REGEX_BACKEND_FALLBACK_H_
#include <memory>
#include "src/regex/backend/backend.h"
namespace modsecurity {
namespace regex {
template<typename T>
static backend::Backend* compile_regex_fallback(const std::string& pattern) {
return new T(pattern);
}
template<typename T, typename T2, typename... Args>
static backend::Backend* compile_regex_fallback(const std::string& pattern) {
T *regex = new T{pattern};
if (regex->ok()) {
return regex;
} else {
delete regex;
return compile_regex_fallback<T2, Args...>(pattern);
}
}
template<typename... Args>
class BackendFallback : public backend::Backend {
public:
BackendFallback(const std::string& pattern)
: backend(compile_regex_fallback<Args...>(pattern))
{}
virtual bool ok() const override {
return backend->ok();
}
std::list<RegexMatch> searchAll(const std::string& s) const override {
return backend->searchAll(s);
}
int search(const std::string &s, RegexMatch *m) const override {
return backend->search(s, m);
}
int search(const std::string &s) const override {
return backend->search(s);
}
const std::string& getPattern() const override {
return backend->getPattern();
}
private:
std::unique_ptr<backend::Backend> backend;
};
} // namespace regex
} // namespace modsecurity
#endif // SRC_REGEX_BACKEND_FALLBACK_H_

View File

@@ -20,9 +20,11 @@
#include <string>
#include <list>
#include "src/regex/backend/backend.h"
#include "src/regex/backend/pcre.h"
#include "src/regex/backend/re2.h"
#include "src/regex/regex_match.h"
#include "src/regex/backend_fallback.h"
#ifndef SRC_REGEX_REGEX_H_
#define SRC_REGEX_REGEX_H_
@@ -32,15 +34,17 @@ namespace modsecurity {
namespace regex {
#ifdef WITH_PCRE
using selectedBackend = backend::Pcre;
#elif WITH_RE2
using selectedBackend = backend::Re2;
# ifdef WITH_RE2
using selectedBackend = BackendFallback<
backend::Re2, backend::Pcre
>;
# else
using selectedBackend = backend::Pcre;
# endif
#else
#error "no regex backend selected"
# error "PCRE is not available"
#endif
using selectedBackend = backend::Pcre;
class Regex : public selectedBackend {
public:
explicit Regex(const std::string& pattern) :

View File

@@ -115,7 +115,7 @@ class KeyExclusion {
class KeyExclusionRegex : public KeyExclusion {
public:
explicit KeyExclusionRegex(regex::Regex re)
: m_re(re.pattern) { }
: m_re(re.getPattern()) { }
explicit KeyExclusionRegex(std::string re)
: m_re(re) { }