[Snort-users] Bug in mSearchREG() that can make Snort go into an infinite loop.
andreaso at ...236...
Mon Feb 11 16:20:01 EST 2002
As described by a few others (for example
my Snort sometimes also stopped logging, and went up to near 100% CPU
I recently managed to reproduce this without just starting Snort and wait
a few days for it to happen again. In particular, SID 1250, which is a
regex rule, seems to cause the behaviour when certain packets are seen.
A simplified version of this rule is:
alert tcp any any -> any 80 (msg:"test"; content:"/level/*/exec/"; regex;)
When starting Snort with this (and only this) rule, sending the string
"123456/ABCDEFGHIJKLMNO/exec/" to a host having port 80 listening will
make Snort immediately jump up to near 100% CPU usage, and just stay
there forever. Ktrace confirms that Snort is probably stuck in an endless
internal loop (no output at all). Tests below are done with 1.8.4-beta1
(Build 91). Everything is probably not correct, but will hopefully at
least be enough to point to the right direction.
Snort is started with:
snort -c snort.conf -l /tmp -d -i lo0
The only line in snort.conf is the rule above.
Then sending the evil string:
echo '123456/ABCDEFGHIJKLMNO/exec/' | nc localhost 80
After a few seconds, 'top' output shows:
PID USERNAME PRI NICE SIZE RES STATE WAIT TIME CPU COMMAND
23845 root 64 0 644K 856K run - 0:59 94.97% snort
And trust me, my loopback interface is not THAT busy :)
The problem seems to be in the mSearchREG function, found in mstring.c.
I'll try to show what I think is going on in the code, step by step.
Feel free to point out all the misassumptions/errors I've made.
There are probably also a few typos since it was made in kind of a hurry.
("*" is where the character comparision will start)
"G" does not equal "/", and since "G" is not even in the pattern,
b_idx is "skip-shifted" to 28. So we get:
The while(buf[--b_idx] == ptrn[--p_idx] ... || (ptrn[p_idx] == '*' &&
!literal) will end when p_idx is 7, since we have a "*" there.
The character left to it is a "/". Next, while(buf[--b_idx] !=
ptrn[p_idx]) will check buf from right to left until it finds a matching
"/" (the 'wildcard final char'), which exists in buf.
Here "6" will not match "l", and b_idx will be skip-shifted to 27 (22
steps) since there is no "6" in the pattern.
"c" does not equal "/", and the pattern will be skip-shifted to 28.
We now get:
However, this is exactly where we were at step 2), so we're now in
an infinite loop.
Here is a quick and wild guess... I would say that the problem is in 3).
Since the "*" wildcard matched "ABCDEFGHIJKLMNO", we're not interested in
that part anymore. Therefore, b_idx should be skip-shifted to 22 + the
length of the pattern just matched by the wildcard.
For example with this patch:
--- mstring.c.org Mon Feb 11 23:32:58 2002
+++ mstring.c Mon Feb 11 23:37:32 2002
@@ -581,6 +581,7 @@
int b_idx = plen;
int literal = 0;
int cmpcnt = 0;
+ int regexcomp = 0;
DebugMessage(DEBUG_PATTERN_MATCH, "buf: %p blen: %d ptrn: %p "
" plen: %d b_idx: %d\n", buf, blen, ptrn, plen, b_idx);
@@ -624,6 +625,7 @@
DebugMessage(DEBUG_PATTERN_MATCH, "comparing: b[%d]:%c -> p[%d]:%c\n",
b_idx, buf[b_idx], p_idx, ptrn[p_idx]);
if(b_idx == 0)
DebugMessage(DEBUG_PATTERN_MATCH, "b_idx went to 0, returning 0\n");
@@ -651,6 +653,9 @@
b_idx += (skip_stride > shift_stride) ? skip_stride : shift_stride;
DebugMessage(DEBUG_PATTERN_MATCH, "b_idx skip-shifted to %d\n", b_idx);
+ b_idx += regexcomp;
+ DebugMessage(DEBUG_PATTERN_MATCH, "b_idx regex compensated %d steps, to %d\n", regexcomp, b_idx);
+ regexcomp = 0;
DebugMessage(DEBUG_PATTERN_MATCH, "no match: compares = %d, b_idx = %d, "
But again, be warned that this is mostly a wild guess and may break many
other things. Paranoid people may instead want to disable the regex rules
(should not be a big deal) until a confirmed working fix is out.
It would be nice to know if disabling the regex rules actually solves the
similar problems other people are having, or if this is a totally
More information about the Snort-users