Skip to content

Commit bf1699d

Browse files
committed
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 70a6833 commit bf1699d

File tree

7 files changed

+167
-22
lines changed

7 files changed

+167
-22
lines changed

Diff for: src/regex/backend/backend.h

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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 [email protected].
13+
*
14+
*/
15+
#ifndef SRC_REGEX_BACKEND_BACKEND_H_
16+
#define SRC_REGEX_BACKEND_BACKEND_H_
17+
18+
#include <list>
19+
#include <vector>
20+
#include <string>
21+
22+
#include "src/regex/regex_match.h"
23+
24+
namespace modsecurity {
25+
namespace regex {
26+
namespace backend {
27+
28+
class Backend {
29+
public:
30+
virtual ~Backend() {}
31+
32+
virtual bool ok() const = 0;
33+
34+
virtual std::list<RegexMatch> searchAll(const std::string& s) const = 0;
35+
virtual bool searchOneMatch(const std::string& s, std::vector<RegexMatchCapture>& captures) const = 0;
36+
virtual int search(const std::string &s, RegexMatch *m) const = 0;
37+
virtual int search(const std::string &s) const = 0;
38+
39+
virtual const std::string& getPattern() const = 0;
40+
};
41+
42+
} // namespace backend
43+
} // namespace regex
44+
} // namespace modsecurity
45+
46+
#endif // SRC_REGEX_BACKEND_BACKEND_H_

Diff for: src/regex/backend/pcre.h

+15-6
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <list>
2424
#include <vector>
2525

26+
#include "src/regex/backend/backend.h"
2627
#include "src/regex/regex_match.h"
2728

2829
#ifndef SRC_REGEX_BACKEND_PCRE_H_
@@ -37,7 +38,7 @@ namespace backend {
3738
#define OVECCOUNT 900
3839

3940

40-
class Pcre {
41+
class Pcre : public Backend {
4142
public:
4243
explicit Pcre(const std::string& pattern_);
4344
~Pcre();
@@ -46,13 +47,21 @@ class Pcre {
4647
Pcre(const Pcre&) = delete;
4748
Pcre& operator=(const Pcre&) = delete;
4849

49-
std::list<RegexMatch> searchAll(const std::string& s) const;
50-
bool searchOneMatch(const std::string& s, std::vector<RegexMatchCapture>& captures) const;
51-
int search(const std::string &s, RegexMatch *m) const;
52-
int search(const std::string &s) const;
50+
std::list<RegexMatch> searchAll(const std::string& s) const override;
51+
bool searchOneMatch(const std::string& s, std::vector<RegexMatchCapture>& captures) const override;
52+
int search(const std::string &s, RegexMatch *m) const override;
53+
int search(const std::string &s) const override;
5354

54-
const std::string pattern;
55+
virtual bool ok() const override {
56+
return m_pc != NULL;
57+
}
58+
59+
virtual const std::string& getPattern() const override {
60+
return pattern;
61+
};
5562
private:
63+
const std::string pattern;
64+
5665
pcre *m_pc = NULL;
5766
pcre_extra *m_pce = NULL;
5867
};

Diff for: src/regex/backend/re2.cc

+6-3
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

Diff for: src/regex/backend/re2.h

+12-6
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <vector>
2222
#include <list>
2323

24+
#include "src/regex/backend/backend.h"
2425
#include "src/regex/regex_match.h"
2526

2627
#ifndef SRC_REGEX_BACKEND_RE2_H_
@@ -32,20 +33,25 @@ namespace backend {
3233

3334
#ifdef WITH_RE2
3435

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

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

43-
std::list<RegexMatch> searchAll(const std::string& s) const;
44-
bool searchOneMatch(const std::string& s, std::vector<RegexMatchCapture>& captures) const;
45-
int search(const std::string &s, RegexMatch *m) const;
46-
int search(const std::string &s) const;
44+
std::list<RegexMatch> searchAll(const std::string& s) const override;
45+
bool searchOneMatch(const std::string& s, std::vector<RegexMatchCapture>& captures) const override;
46+
int search(const std::string &s, RegexMatch *m) const override;
47+
int search(const std::string &s) const override;
48+
virtual bool ok() const override {
49+
return re.ok();
50+
}
4751

48-
const std::string pattern;
52+
virtual const std::string& getPattern() const override {
53+
return re.pattern();
54+
};
4955
private:
5056
const RE2 re;
5157
};

Diff for: src/regex/backend_fallback.h

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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 [email protected].
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+
bool searchOneMatch(const std::string& s, std::vector<RegexMatchCapture>& captures) const override {
56+
return backend->searchOneMatch(s, captures);
57+
}
58+
int search(const std::string &s, RegexMatch *m) const override {
59+
return backend->search(s, m);
60+
}
61+
int search(const std::string &s) const override {
62+
return backend->search(s);
63+
}
64+
65+
const std::string& getPattern() const override {
66+
return backend->getPattern();
67+
}
68+
private:
69+
std::unique_ptr<backend::Backend> backend;
70+
};
71+
72+
} // namespace regex
73+
} // namespace modsecurity
74+
75+
#endif // SRC_REGEX_BACKEND_FALLBACK_H_

Diff for: src/regex/regex.h

+10-4
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121
#include <list>
2222
#include <vector>
2323

24+
#include "src/regex/backend/backend.h"
2425
#include "src/regex/backend/pcre.h"
2526
#include "src/regex/backend/re2.h"
2627
#include "src/regex/regex_match.h"
28+
#include "src/regex/backend_fallback.h"
2729

2830
#ifndef SRC_REGEX_REGEX_H_
2931
#define SRC_REGEX_REGEX_H_
@@ -33,11 +35,15 @@ namespace modsecurity {
3335
namespace regex {
3436

3537
#ifdef WITH_PCRE
36-
using selectedBackend = backend::Pcre;
37-
#elif WITH_RE2
38-
using selectedBackend = backend::Re2;
38+
# ifdef WITH_RE2
39+
using selectedBackend = BackendFallback<
40+
backend::Re2, backend::Pcre
41+
>;
42+
# else
43+
using selectedBackend = backend::Pcre;
44+
# endif
3945
#else
40-
#error "no regex backend selected"
46+
# error "PCRE is not available"
4147
#endif
4248

4349
class Regex : public selectedBackend {

Diff for: src/variables/variable.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,9 @@ class KeyExclusion {
115115
// FIXME: use pre built regex.
116116
class KeyExclusionRegex : public KeyExclusion {
117117
public:
118-
explicit KeyExclusionRegex(const regex::Regex &re)
119-
: m_re(re.pattern) { }
120-
explicit KeyExclusionRegex(const std::string &re)
118+
explicit KeyExclusionRegex(regex::Regex re)
119+
: m_re(re.getPattern()) { }
120+
explicit KeyExclusionRegex(std::string re)
121121
: m_re(re) { }
122122

123123
~KeyExclusionRegex() override { }

0 commit comments

Comments
 (0)