Page MenuHomeFreeBSD

libc glob: Avoid pathological exponential behavior
AbandonedPublic

Authored by cem on Apr 30 2017, 7:20 PM.
Tags
None
Referenced Files
Unknown Object (File)
Thu, May 16, 1:25 PM
Unknown Object (File)
Feb 29 2024, 8:38 PM
Unknown Object (File)
Feb 15 2024, 11:39 PM
Unknown Object (File)
Nov 26 2023, 12:25 AM
Unknown Object (File)
Nov 22 2023, 10:19 AM
Unknown Object (File)
Nov 22 2023, 6:09 AM
Unknown Object (File)
Nov 11 2023, 10:55 AM
Unknown Object (File)
Oct 21 2023, 5:30 AM
Subscribers
None

Details

Reviewers
ache
marcel
jhb
kib
Summary

Adapt glob's match() routine to use a greedy algorithm that avoids
exponential runtime in byzantine inputs.

While here, add a testcase for the byzantine input.

Prompted by: https://research.swtch.com/glob
Authored by: Yves Orton <demerphq at gmail.com>
Obtained from: Perl (33252c318625f3c6c89b816ee88481940e3e6f95)

Test Plan

Tests included. They pass with the changed match() routine.

Diff Detail

Lint
Lint Passed
Unit
No Test Coverage
Build Status
Buildable 8995
Build 9392: arc lint + arc unit

Event Timeline

Note: Most of diff to match() is additional indentation from the added while loop. It's probably most useful to look at a diff which ignores whitespace changes.

Also note that NetBSD made a similar change for the same reasons. The NetBSD version is oddly very divergent from style(9). Perl's looked cleaner, so this change is derived from that instead.

I plan to commit this in a couple days, if I don't hear anything.

I prefer to copy NetBSD implementing the same thing, it appears they have additional safeguard against "name" goes too far:
http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/gen/glob.c.diff?r1=1.36&r2=1.37&f=h&f=u

In D10551#218887, @ache wrote:

I prefer to copy NetBSD implementing the same thing, it appears they have additional safeguard against "name" goes too far:
http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/gen/glob.c.diff?r1=1.36&r2=1.37&f=h&f=u

What safeguard are you referring to?

Note that name cannot iterate off the valid portion of the string; every step that increments name checks for EOS, as does the end of the outer loop.

+		if (nameNext != nameStart
+		    && (nameEnd == NULL || nameNext <= nameEnd)) {
+			pat = patNext;
+			name = nameNext;
+			continue;
+		}
In D10551#218889, @ache wrote:
+		if (nameNext != nameStart
+		    && (nameEnd == NULL || nameNext <= nameEnd)) {
+			pat = patNext;
+			name = nameNext;
+			continue;
+		}

nameEnd == NULL || nameNext <= nameEnd check is redundant. nameNext (or nextn) can be set to name + 1 IIF *name != EOS.

nameNext != nameStart check does not apply to our implementation, because nextn is never initialized to the beginning of name and can only be set to name + 1.

So these checks are useless for us.