Skip to content

Commit 4fd3a30

Browse files
committed
Avoid excessive backtracking in regex engine during fuzzing
The regex engine is prone to excessive backtracking, leading to timeouts, especially while fuzzing. This commit introduces a backtracking counter and a limit of 1000 backtracking steps. When this limit is exceeded during fuzzing, the regex engine aborts to prevent excessive backtracking. For this, the FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION macro is used, as per suggested by the documentation of libFuzzer.
1 parent 36911f0 commit 4fd3a30

File tree

2 files changed

+18
-5
lines changed

2 files changed

+18
-5
lines changed

Diff for: Makefile

+6-5
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ CFLAGS_DEBUG=$(CFLAGS) -O0
154154
CFLAGS_SMALL=$(CFLAGS) -Os
155155
CFLAGS_OPT=$(CFLAGS) -O2
156156
CFLAGS_NOLTO:=$(CFLAGS_OPT)
157+
CFLAGS_FUZZ=$(CFLAGS_OPT) -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1
157158
ifdef CONFIG_COSMO
158159
LDFLAGS+=-s # better to strip by default
159160
else
@@ -255,13 +256,13 @@ qjsc$(EXE): $(OBJDIR)/qjsc.o $(QJS_LIB_OBJS)
255256
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
256257

257258
fuzz_eval: $(OBJDIR)/fuzz_eval.o $(OBJDIR)/fuzz_common.o libquickjs.fuzz.a
258-
$(CC) $(CFLAGS_OPT) $^ -o fuzz_eval $(LIB_FUZZING_ENGINE)
259+
$(CC) $(CFLAGS_FUZZ) $^ -o fuzz_eval $(LIB_FUZZING_ENGINE)
259260

260261
fuzz_compile: $(OBJDIR)/fuzz_compile.o $(OBJDIR)/fuzz_common.o libquickjs.fuzz.a
261-
$(CC) $(CFLAGS_OPT) $^ -o fuzz_compile $(LIB_FUZZING_ENGINE)
262+
$(CC) $(CFLAGS_FUZZ) $^ -o fuzz_compile $(LIB_FUZZING_ENGINE)
262263

263264
fuzz_regexp: $(OBJDIR)/fuzz_regexp.o $(OBJDIR)/libregexp.fuzz.o $(OBJDIR)/cutils.fuzz.o $(OBJDIR)/libunicode.fuzz.o
264-
$(CC) $(CFLAGS_OPT) $^ -o fuzz_regexp $(LIB_FUZZING_ENGINE)
265+
$(CC) $(CFLAGS_FUZZ) $^ -o fuzz_regexp $(LIB_FUZZING_ENGINE)
265266

266267
libfuzzer: fuzz_eval fuzz_compile fuzz_regexp
267268

@@ -338,7 +339,7 @@ $(OBJDIR)/%.o: %.c | $(OBJDIR)
338339
$(CC) $(CFLAGS_OPT) -c -o $@ $<
339340

340341
$(OBJDIR)/fuzz_%.o: fuzz/fuzz_%.c | $(OBJDIR)
341-
$(CC) $(CFLAGS_OPT) -c -I. -o $@ $<
342+
$(CC) $(CFLAGS_FUZZ) -c -I. -o $@ $<
342343

343344
$(OBJDIR)/%.host.o: %.c | $(OBJDIR)
344345
$(HOST_CC) $(CFLAGS_OPT) -c -o $@ $<
@@ -359,7 +360,7 @@ $(OBJDIR)/%.debug.o: %.c | $(OBJDIR)
359360
$(CC) $(CFLAGS_DEBUG) -c -o $@ $<
360361

361362
$(OBJDIR)/%.fuzz.o: %.c | $(OBJDIR)
362-
$(CC) $(CFLAGS_OPT) -fsanitize=fuzzer-no-link -c -o $@ $<
363+
$(CC) $(CFLAGS_FUZZ) -fsanitize=fuzzer-no-link -c -o $@ $<
363364

364365
$(OBJDIR)/%.check.o: %.c | $(OBJDIR)
365366
$(CC) $(CFLAGS) -DCONFIG_CHECK_JSVALUE -c -o $@ $<

Diff for: libregexp.c

+12
Original file line numberDiff line numberDiff line change
@@ -1927,6 +1927,9 @@ typedef struct {
19271927
/* 0 = 8 bit chars, 1 = 16 bit chars, 2 = 16 bit chars, UTF-16 */
19281928
int cbuf_type;
19291929
int capture_count;
1930+
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
1931+
int backtrack_count;
1932+
#endif
19301933
int stack_size_max;
19311934
BOOL multi_line;
19321935
BOOL ignore_case;
@@ -1993,6 +1996,12 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
19931996

19941997
for(;;) {
19951998
// printf("top=%p: pc=%d\n", th_list.top, (int)(pc - (bc_buf + RE_HEADER_LEN)));
1999+
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
2000+
if (++s->backtrack_count > 1000) {
2001+
return -1; // backtracking limit exceeded
2002+
}
2003+
#endif
2004+
19962005
opcode = *pc++;
19972006
switch(opcode) {
19982007
case REOP_match:
@@ -2399,6 +2408,9 @@ int lre_exec(uint8_t **capture,
23992408
s->ignore_case = (re_flags & LRE_FLAG_IGNORECASE) != 0;
24002409
s->is_unicode = (re_flags & LRE_FLAG_UNICODE) != 0;
24012410
s->capture_count = bc_buf[RE_HEADER_CAPTURE_COUNT];
2411+
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
2412+
s->backtrack_count = 0;
2413+
#endif
24022414
s->stack_size_max = bc_buf[RE_HEADER_STACK_SIZE];
24032415
s->cbuf = cbuf;
24042416
s->cbuf_end = cbuf + (clen << cbuf_type);

0 commit comments

Comments
 (0)