Skip to content

Commit 67c7c3b

Browse files
WGH-zimmerle
authored andcommitted
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.
1 parent 0cfdd0e commit 67c7c3b

File tree

7 files changed

+159
-20
lines changed

7 files changed

+159
-20
lines changed

src/regex/backend/backend.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* ModSecurity, http://www.modsecurity.org/
3+
* Copyright (c) 2019
4+
*
5+
* You may not use this file except in compliance with
6+
* the License. You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* If any of the files related to licensing are missing or if you have any
11+
* other questions related to licensing please contact Trustwave Holdings, Inc.
12+
* directly using the email address security@modsecurity.org.
13+
*
14+
*/
15+
#ifndef SRC_REGEX_BACKEND_BACKEND_H_
16+
#define SRC_REGEX_BACKEND_BACKEND_H_
17+
18+
#include <list>
19+
#include <string>
20+
21+
#include "src/regex/regex_match.h"
22+
23+
namespace modsecurity {
24+
namespace regex {
25+
namespace backend {
26+
27+
class Backend {
28+
public:
29+
virtual ~Backend() {}
30+
31+
virtual bool ok() const = 0;
32+
33+
virtual std::list<RegexMatch> searchAll(const std::string& s) const = 0;
34+
virtual int search(const std::string &s, RegexMatch *m) const = 0;
35+
virtual int search(const std::string &s) const = 0;
36+
37+
virtual const std::string& getPattern() const = 0;
38+
};
39+
40+
} // namespace backend
41+
} // namespace regex
42+
} // namespace modsecurity
43+
44+
#endif // SRC_REGEX_BACKEND_BACKEND_H_

src/regex/backend/pcre.h

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <string>
2323
#include <list>
2424

25+
#include "src/regex/backend/backend.h"
2526
#include "src/regex/regex_match.h"
2627

2728
#ifndef SRC_REGEX_BACKEND_PCRE_H_
@@ -36,7 +37,7 @@ namespace backend {
3637
#define OVECCOUNT 30
3738

3839

39-
class Pcre {
40+
class Pcre : public Backend {
4041
public:
4142
explicit Pcre(const std::string& pattern_);
4243
~Pcre();
@@ -45,12 +46,20 @@ class Pcre {
4546
Pcre(const Pcre&) = delete;
4647
Pcre& operator=(const Pcre&) = delete;
4748

48-
std::list<RegexMatch> searchAll(const std::string& s) const;
49-
int search(const std::string &s, RegexMatch *m) const;
50-
int search(const std::string &s) const;
49+
virtual bool ok() const override {
50+
return m_pc != NULL;
51+
}
5152

52-
const std::string pattern;
53+
std::list<RegexMatch> searchAll(const std::string& s) const override;
54+
int search(const std::string &s, RegexMatch *m) const override;
55+
int search(const std::string &s) const override;
56+
57+
virtual const std::string& getPattern() const override {
58+
return pattern;
59+
};
5360
private:
61+
const std::string pattern;
62+
5463
pcre *m_pc = NULL;
5564
pcre_extra *m_pce = NULL;
5665
};

src/regex/backend/re2.cc

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,18 @@ namespace backend {
2929
static RE2::Options get_re2_options() {
3030
RE2::Options res;
3131

32+
// Re2 is usually used with fallback to libpcre,
33+
// so disable unnecessary stderr noise
34+
res.set_log_errors(false);
35+
3236
res.set_dot_nl(true);
3337

3438
return res;
3539
}
3640

3741

38-
Re2::Re2(const std::string& pattern_)
39-
: pattern(pattern_.empty() ? ".*" : pattern_),
40-
re(pattern, get_re2_options())
42+
Re2::Re2(const std::string& pattern)
43+
: re(pattern.empty() ? ".*" : pattern, get_re2_options())
4144
{
4245
}
4346

src/regex/backend/re2.h

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <string>
2121
#include <list>
2222

23+
#include "src/regex/backend/backend.h"
2324
#include "src/regex/regex_match.h"
2425

2526
#ifndef SRC_REGEX_BACKEND_RE2_H_
@@ -31,19 +32,25 @@ namespace backend {
3132

3233
#ifdef WITH_RE2
3334

34-
class Re2 {
35+
class Re2 : public Backend {
3536
public:
3637
explicit Re2(const std::string& pattern_);
3738

3839
// RE2 class is not copyable, so neither is this
3940
Re2(const Re2&) = delete;
4041
Re2& operator=(const Re2&) = delete;
4142

42-
std::list<RegexMatch> searchAll(const std::string& s) const;
43-
int search(const std::string &s, RegexMatch *m) const;
44-
int search(const std::string &s) const;
43+
virtual bool ok() const override {
44+
return re.ok();
45+
}
4546

46-
const std::string pattern;
47+
std::list<RegexMatch> searchAll(const std::string& s) const override;
48+
int search(const std::string &s, RegexMatch *m) const override;
49+
int search(const std::string &s) const override;
50+
51+
virtual const std::string& getPattern() const override {
52+
return re.pattern();
53+
};
4754
private:
4855
const RE2 re;
4956
};

src/regex/backend_fallback.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* ModSecurity, http://www.modsecurity.org/
3+
* Copyright (c) 2019
4+
*
5+
* You may not use this file except in compliance with
6+
* the License. You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* If any of the files related to licensing are missing or if you have any
11+
* other questions related to licensing please contact Trustwave Holdings, Inc.
12+
* directly using the email address security@modsecurity.org.
13+
*
14+
*/
15+
#ifndef SRC_REGEX_BACKEND_FALLBACK_H_
16+
#define SRC_REGEX_BACKEND_FALLBACK_H_
17+
18+
#include <memory>
19+
20+
#include "src/regex/backend/backend.h"
21+
22+
namespace modsecurity {
23+
namespace regex {
24+
25+
template<typename T>
26+
static backend::Backend* compile_regex_fallback(const std::string& pattern) {
27+
return new T(pattern);
28+
}
29+
30+
template<typename T, typename T2, typename... Args>
31+
static backend::Backend* compile_regex_fallback(const std::string& pattern) {
32+
T *regex = new T{pattern};
33+
if (regex->ok()) {
34+
return regex;
35+
} else {
36+
delete regex;
37+
return compile_regex_fallback<T2, Args...>(pattern);
38+
}
39+
}
40+
41+
template<typename... Args>
42+
class BackendFallback : public backend::Backend {
43+
public:
44+
BackendFallback(const std::string& pattern)
45+
: backend(compile_regex_fallback<Args...>(pattern))
46+
{}
47+
48+
virtual bool ok() const override {
49+
return backend->ok();
50+
}
51+
52+
std::list<RegexMatch> searchAll(const std::string& s) const override {
53+
return backend->searchAll(s);
54+
}
55+
int search(const std::string &s, RegexMatch *m) const override {
56+
return backend->search(s, m);
57+
}
58+
int search(const std::string &s) const override {
59+
return backend->search(s);
60+
}
61+
62+
const std::string& getPattern() const override {
63+
return backend->getPattern();
64+
}
65+
private:
66+
std::unique_ptr<backend::Backend> backend;
67+
};
68+
69+
} // namespace regex
70+
} // namespace modsecurity
71+
72+
#endif // SRC_REGEX_BACKEND_FALLBACK_H_

src/regex/regex.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
#include <string>
2121
#include <list>
2222

23+
#include "src/regex/backend/backend.h"
2324
#include "src/regex/backend/pcre.h"
2425
#include "src/regex/backend/re2.h"
2526
#include "src/regex/regex_match.h"
27+
#include "src/regex/backend_fallback.h"
2628

2729
#ifndef SRC_REGEX_REGEX_H_
2830
#define SRC_REGEX_REGEX_H_
@@ -32,15 +34,17 @@ namespace modsecurity {
3234
namespace regex {
3335

3436
#ifdef WITH_PCRE
35-
using selectedBackend = backend::Pcre;
36-
#elif WITH_RE2
37-
using selectedBackend = backend::Re2;
37+
# ifdef WITH_RE2
38+
using selectedBackend = BackendFallback<
39+
backend::Re2, backend::Pcre
40+
>;
41+
# else
42+
using selectedBackend = backend::Pcre;
43+
# endif
3844
#else
39-
#error "no regex backend selected"
45+
# error "PCRE is not available"
4046
#endif
4147

42-
using selectedBackend = backend::Pcre;
43-
4448
class Regex : public selectedBackend {
4549
public:
4650
explicit Regex(const std::string& pattern) :

src/variables/variable.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ class KeyExclusion {
115115
class KeyExclusionRegex : public KeyExclusion {
116116
public:
117117
explicit KeyExclusionRegex(regex::Regex re)
118-
: m_re(re.pattern) { }
118+
: m_re(re.getPattern()) { }
119119
explicit KeyExclusionRegex(std::string re)
120120
: m_re(re) { }
121121

0 commit comments

Comments
 (0)