Patch for CVE-2011-1951: syslog-ng-3.1.4-pcre-dos.patch (#709088)
Enabled the test suite
This commit is contained in:
parent
8108ce0d9d
commit
8629f464db
19
syslog-ng-3.1.4-disable-sql-and-ssl-tests.patch
Normal file
19
syslog-ng-3.1.4-disable-sql-and-ssl-tests.patch
Normal file
@ -0,0 +1,19 @@
|
||||
diff -ruN syslog-ng-3.1.4/tests/functional/func_test.py syslog-ng-3.1.4-modified/tests/functional/func_test.py
|
||||
--- syslog-ng-3.1.4/tests/functional/func_test.py 2009-11-21 15:48:09.000000000 +0000
|
||||
+++ syslog-ng-3.1.4-modified/tests/functional/func_test.py 2011-06-17 03:44:18.057873606 +0100
|
||||
@@ -60,11 +60,12 @@
|
||||
# import test modules
|
||||
import test_file_source
|
||||
import test_filters
|
||||
-import test_input_drivers
|
||||
+#import test_input_drivers
|
||||
import test_performance
|
||||
-import test_sql
|
||||
+#import test_sql
|
||||
|
||||
-tests = (test_input_drivers, test_sql, test_file_source, test_filters, test_performance)
|
||||
+#tests = (test_input_drivers, test_sql, test_file_source, test_filters, test_performance)
|
||||
+tests = (test_file_source, test_filters, test_performance)
|
||||
|
||||
init_env()
|
||||
seed_rnd()
|
271
syslog-ng-3.1.4-pcre-dos.patch
Normal file
271
syslog-ng-3.1.4-pcre-dos.patch
Normal file
@ -0,0 +1,271 @@
|
||||
commit 35de55e53dd653c50c8da5daf41a99ab22e7e8aa
|
||||
Author: Balazs Scheidler <bazsi@balabit.hu>
|
||||
Date: Tue May 3 20:54:53 2011 +0200
|
||||
|
||||
pcre: fixed a potential resource hogging infinite loop when an error occurs
|
||||
|
||||
Any kind of PCRE error case would cause an infinite loop, when the
|
||||
"global" flag is present and pcre returns an error code.
|
||||
|
||||
The reported problem is that with PCRE 8.12 we indeed get such an error
|
||||
while doing a global replace.
|
||||
|
||||
This patch also reworks the way PCRE based replacements are made, that code
|
||||
was hairy, and I just hope this one is one bit less so. One performance
|
||||
related change also made it that improves the speed pcre replacements,
|
||||
which previously zeroed out a 3k array unconditionally in every invocation.
|
||||
|
||||
Also added some additional testcases to be sure I didn't break anything.
|
||||
|
||||
Reported-By: Micah Anderson <micah@riseup.net>
|
||||
Signed-off-by: Balazs Scheidler <bazsi@balabit.hu>
|
||||
|
||||
diff --git a/src/logmatcher.c b/src/logmatcher.c
|
||||
index 67b6c1b..6b70f13 100644
|
||||
--- a/src/logmatcher.c
|
||||
+++ b/src/logmatcher.c
|
||||
@@ -504,7 +504,6 @@ typedef struct _LogMatcherPcreRe
|
||||
pcre *pattern;
|
||||
pcre_extra *extra;
|
||||
gint match_options;
|
||||
- int start_offset;
|
||||
} LogMatcherPcreRe;
|
||||
|
||||
static gboolean
|
||||
@@ -623,29 +622,37 @@ static gboolean
|
||||
log_matcher_pcre_re_match(LogMatcher *s, LogMessage *msg, gint value_handle, const gchar *value, gssize value_len)
|
||||
{
|
||||
LogMatcherPcreRe *self = (LogMatcherPcreRe *) s;
|
||||
- int matches[RE_MAX_MATCHES * 3];
|
||||
+ gint *matches;
|
||||
+ gsize matches_size;
|
||||
+ gint num_matches;
|
||||
gint rc;
|
||||
|
||||
- if(value_len == -1)
|
||||
+ if (value_len == -1)
|
||||
value_len = strlen(value);
|
||||
|
||||
- self->start_offset = 0;
|
||||
- rc = pcre_exec(self->pattern, self->extra, value, value_len, self->start_offset, self->match_options, matches, (RE_MAX_MATCHES * 3));
|
||||
+ if (pcre_fullinfo(self->pattern, self->extra, PCRE_INFO_CAPTURECOUNT, &num_matches) < 0)
|
||||
+ g_assert_not_reached();
|
||||
+ if (num_matches > RE_MAX_MATCHES)
|
||||
+ num_matches = RE_MAX_MATCHES;
|
||||
+
|
||||
+ matches_size = 3 * (num_matches + 1);
|
||||
+ matches = g_alloca(matches_size * sizeof(gint));
|
||||
+
|
||||
+ rc = pcre_exec(self->pattern, self->extra,
|
||||
+ value, value_len, 0, self->match_options, matches, matches_size);
|
||||
if (rc < 0)
|
||||
{
|
||||
switch (rc)
|
||||
{
|
||||
- case PCRE_ERROR_NOMATCH:
|
||||
- /*
|
||||
- msg_debug("No match", NULL);
|
||||
- */
|
||||
+ case PCRE_ERROR_NOMATCH:
|
||||
break;
|
||||
+
|
||||
+ default:
|
||||
/* Handle other special cases */
|
||||
- default:
|
||||
- msg_error("Error while matching regexp",
|
||||
- evt_tag_int("error_code",rc),
|
||||
- NULL);
|
||||
- break;
|
||||
+ msg_error("Error while matching regexp",
|
||||
+ evt_tag_int("error_code", rc),
|
||||
+ NULL);
|
||||
+ break;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
@@ -668,78 +675,120 @@ static gchar *
|
||||
log_matcher_pcre_re_replace(LogMatcher *s, LogMessage *msg, gint value_handle, const gchar *value, gssize value_len, LogTemplate *replacement, gssize *new_length)
|
||||
{
|
||||
LogMatcherPcreRe *self = (LogMatcherPcreRe *) s;
|
||||
- int matches[RE_MAX_MATCHES * 3];
|
||||
- gint rc;
|
||||
- gboolean first_round = TRUE;
|
||||
GString *new_value = NULL;
|
||||
- gssize last_offset = 0;
|
||||
- gint options = 0;
|
||||
+ gint *matches;
|
||||
+ gsize matches_size;
|
||||
+ gint num_matches;
|
||||
+ gint rc;
|
||||
+ gint start_offset, last_offset;
|
||||
+ gint options;
|
||||
+ gboolean last_match_was_empty;
|
||||
+
|
||||
+ if (pcre_fullinfo(self->pattern, self->extra, PCRE_INFO_CAPTURECOUNT, &num_matches) < 0)
|
||||
+ g_assert_not_reached();
|
||||
+ if (num_matches > RE_MAX_MATCHES)
|
||||
+ num_matches = RE_MAX_MATCHES;
|
||||
+
|
||||
+ matches_size = 3 * (num_matches + 1);
|
||||
+ matches = g_alloca(matches_size * sizeof(gint));
|
||||
+
|
||||
+ /* we need zero initialized offsets for the last match as the
|
||||
+ * algorithm tries uses that as the base position */
|
||||
|
||||
- memset(matches, 0, sizeof(matches));
|
||||
+ matches[0] = matches[1] = matches[2] = 0;
|
||||
|
||||
if (value_len == -1)
|
||||
value_len = strlen(value);
|
||||
|
||||
+ last_offset = start_offset = 0;
|
||||
+ last_match_was_empty = FALSE;
|
||||
do
|
||||
{
|
||||
- options = 0;
|
||||
- self->start_offset = matches[1]; /* Start at end of previous match 0 on the first iteration*/
|
||||
-
|
||||
- /* If the previous match was for an empty string, we are finished if we are
|
||||
- at the end of the subject. Otherwise, arrange to run another match at the
|
||||
- same point to see if a non-empty match can be found.
|
||||
+ /* loop over the string, replacing one occurence at a time. */
|
||||
+
|
||||
+ /* NOTE: zero length matches need special care, as we could spin
|
||||
+ * forever otherwise (since the current position wouldn't be
|
||||
+ * advanced).
|
||||
+ *
|
||||
+ * A zero-length match can be as simple as "a*" which will be
|
||||
+ * returned unless PCRE_NOTEMPTY is specified.
|
||||
+ *
|
||||
+ * By supporting zero-length matches, we basically make it
|
||||
+ * possible to insert replacement between each incoming
|
||||
+ * character.
|
||||
+ *
|
||||
+ * For example:
|
||||
+ * pattern: a*
|
||||
+ * replacement: #
|
||||
+ * input: message
|
||||
+ * result: #m#e#s#s#a#g#e#
|
||||
+ *
|
||||
+ * This mimics Perl behaviour.
|
||||
*/
|
||||
|
||||
- if (matches[0] == matches[1] && !first_round)
|
||||
+ if (last_match_was_empty)
|
||||
{
|
||||
- if (matches[0] == value_len)
|
||||
- break;
|
||||
+ /* Otherwise, arrange to run another match at the same point
|
||||
+ * to see if a non-empty match can be found.
|
||||
+ */
|
||||
+
|
||||
options = PCRE_NOTEMPTY | PCRE_ANCHORED;
|
||||
}
|
||||
+ else
|
||||
+ {
|
||||
+ options = 0;
|
||||
+ }
|
||||
|
||||
- rc = pcre_exec(self->pattern, self->extra, value, value_len, self->start_offset/*start offset*/, (self->match_options | options) , matches, (RE_MAX_MATCHES * 3) );
|
||||
- if (rc < 0)
|
||||
+ rc = pcre_exec(self->pattern, self->extra,
|
||||
+ value, value_len,
|
||||
+ start_offset, (self->match_options | options), matches, matches_size);
|
||||
+ if (rc < 0 && rc != PCRE_ERROR_NOMATCH)
|
||||
{
|
||||
- if(rc == PCRE_ERROR_NOMATCH)
|
||||
- {
|
||||
- /* msg_debug("No match", NULL); */
|
||||
- if(!first_round)
|
||||
- {
|
||||
- if (options == 0)
|
||||
- break;
|
||||
- else
|
||||
- matches[1] = self->start_offset + 1;
|
||||
- continue; /* Go round the loop again */
|
||||
- }
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- /* Handle other special cases */
|
||||
- msg_error("Error while matching regexp",
|
||||
- evt_tag_int("error_code",rc),
|
||||
- NULL);
|
||||
- }
|
||||
+ msg_error("Error while matching regexp",
|
||||
+ evt_tag_int("error_code", rc),
|
||||
+ NULL);
|
||||
+ break;
|
||||
}
|
||||
- else if (rc == 0)
|
||||
+ else if (rc < 0)
|
||||
{
|
||||
- msg_error("Error while storing matching substrings", NULL);
|
||||
+ if ((options & PCRE_NOTEMPTY) == 0)
|
||||
+ {
|
||||
+ /* we didn't match, even when we permitted to match the
|
||||
+ * empty string. Nothing to find here, bail out */
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* we didn't match, quite possibly because the empty match
|
||||
+ * was not permitted. Skip one character in order to avoid
|
||||
+ * infinite loop over the same zero-length match. */
|
||||
+
|
||||
+ start_offset = start_offset + 1;
|
||||
+ /* FIXME: handle complex sequences like utf8 and newline characters */
|
||||
+ last_match_was_empty = FALSE;
|
||||
+ continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
+ /* if the output array was too small, truncate the number of
|
||||
+ captures to RE_MAX_MATCHES */
|
||||
+
|
||||
+ if (rc == 0)
|
||||
+ rc = matches_size / 3;
|
||||
+
|
||||
log_matcher_pcre_re_feed_backrefs(s, msg, value_handle, matches, rc, value);
|
||||
log_matcher_pcre_re_feed_named_substrings(s, msg, matches, value);
|
||||
|
||||
if (!new_value)
|
||||
new_value = g_string_sized_new(value_len);
|
||||
- /* literal */
|
||||
+ /* append non-matching portion */
|
||||
g_string_append_len(new_value, &value[last_offset], matches[0] - last_offset);
|
||||
/* replacement */
|
||||
log_template_append_format(replacement, msg, 0, TS_FMT_BSD, NULL, 0, 0, new_value);
|
||||
- last_offset = matches[1];
|
||||
+ last_match_was_empty = (matches[0] == matches[1]);
|
||||
+ start_offset = last_offset = matches[1];
|
||||
}
|
||||
- first_round = FALSE;
|
||||
}
|
||||
- while (TRUE && (self->super.flags & LMF_GLOBAL));
|
||||
+ while (self->super.flags & LMF_GLOBAL && start_offset < value_len);
|
||||
|
||||
if (new_value)
|
||||
{
|
||||
diff --git a/tests/unit/test_matcher.c b/tests/unit/test_matcher.c
|
||||
index 3df98e5..95866b3 100644
|
||||
--- a/tests/unit/test_matcher.c
|
||||
+++ b/tests/unit/test_matcher.c
|
||||
@@ -144,9 +144,18 @@ main()
|
||||
/* empty match with global flag*/
|
||||
testcase_replace("<155>2006-02-11T10:34:56+01:00 bzorp syslog-ng[23323]: aa bb", 0, "c*", "#", "#a#a# #b#b#", LMF_GLOBAL, log_matcher_pcre_re_new());
|
||||
testcase_replace("<155>2006-02-11T10:34:56+01:00 bzorp syslog-ng[23323]: aa bb", 0, "a*", "?", "?? ?b?b?", LMF_GLOBAL, log_matcher_pcre_re_new());
|
||||
+ testcase_replace("<155>2006-02-11T10:34:56+01:00 bzorp syslog-ng[23323]: aa", 0, "aa|b*", "@", "@@", LMF_GLOBAL, log_matcher_pcre_re_new());
|
||||
+ testcase_replace("<155>2006-02-11T10:34:56+01:00 bzorp syslog-ng[23323]: aa", 0, "aa|b*", "@", "@", 0, log_matcher_pcre_re_new());
|
||||
+ testcase_replace("<155>2006-02-11T10:34:56+01:00 bzorp syslog-ng[23323]: aa", 0, "b*|aa", "@", "@@@", LMF_GLOBAL, log_matcher_pcre_re_new());
|
||||
+ testcase_replace("<155>2006-02-11T10:34:56+01:00 bzorp syslog-ng[23323]: aa", 0, "b*|aa", "@", "@aa", 0, log_matcher_pcre_re_new());
|
||||
|
||||
testcase_replace("<155>2006-02-11T10:34:56+01:00 bzorp syslog-ng[23323]: wikiwiki", 0, "wi", "", "kiki", LMF_GLOBAL, log_matcher_pcre_re_new());
|
||||
testcase_replace("<155>2006-02-11T10:34:56+01:00 bzorp syslog-ng[23323]: wikiwiki", 0, "wi", "kuku", "kukukikukuki", LMF_GLOBAL, log_matcher_pcre_re_new());
|
||||
+
|
||||
+ /* this tests a pcre 8.12 incompatibility */
|
||||
+
|
||||
+ testcase_replace("<155>2006-02-11T10:34:56+01:00 bzorp syslog-ng[23323]: wikiwiki", 0, "([[:digit:]]{1,3}\\.){3}[[:digit:]]{1,3}", "foo", "wikiwiki", LMF_GLOBAL, log_matcher_pcre_re_new());
|
||||
+
|
||||
#endif
|
||||
|
||||
return 0;
|
@ -4,7 +4,7 @@
|
||||
|
||||
Name: syslog-ng
|
||||
Version: 3.1.4
|
||||
Release: 3%{?dist}
|
||||
Release: 4%{?dist}
|
||||
Summary: Next-generation syslog server
|
||||
|
||||
Group: System Environment/Daemons
|
||||
@ -16,6 +16,9 @@ Source2: syslog-ng.init.d
|
||||
Source3: syslog-ng.sysconfig
|
||||
Source4: syslog-ng.logrotate
|
||||
|
||||
Patch0: syslog-ng-3.1.4-disable-sql-and-ssl-tests.patch
|
||||
Patch1: syslog-ng-3.1.4-pcre-dos.patch
|
||||
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
||||
|
||||
BuildRequires: pkgconfig
|
||||
@ -49,6 +52,8 @@ ideal for firewalled environments.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
%patch0 -p1
|
||||
%patch1 -p1
|
||||
|
||||
# fix perl path
|
||||
%{__sed} -i 's|^#!/usr/local/bin/perl|#!%{__perl}|' contrib/relogger.pl
|
||||
@ -109,6 +114,10 @@ for vimver in 70 71 72 73 ; do
|
||||
done
|
||||
|
||||
|
||||
%check
|
||||
make check
|
||||
|
||||
|
||||
%clean
|
||||
rm -rf %{buildroot}
|
||||
|
||||
@ -179,6 +188,10 @@ fi
|
||||
|
||||
|
||||
%changelog
|
||||
* Fri Jun 17 2011 Jose Pedro Oliveira <jpo at di.uminho.pt> - 3.1.4-4
|
||||
- Patch for CVE-2011-1951: syslog-ng-3.1.4-pcre-dos.patch (#709088)
|
||||
- Enabled the test suite
|
||||
|
||||
* Mon May 9 2011 Jose Pedro Oliveira <jpo at di.uminho.pt> - 3.1.4-3
|
||||
- Bumped the eventlog version to match the latest upstream version (0.2.12)
|
||||
- Overrided the default _localstatedir value (configure --localstatedir)
|
||||
|
Loading…
Reference in New Issue
Block a user