aboutsummaryrefslogtreecommitdiff
path: root/src/pcre2_fuzzsupport.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pcre2_fuzzsupport.c')
-rw-r--r--src/pcre2_fuzzsupport.c334
1 files changed, 253 insertions, 81 deletions
diff --git a/src/pcre2_fuzzsupport.c b/src/pcre2_fuzzsupport.c
index a2585180..7decec55 100644
--- a/src/pcre2_fuzzsupport.c
+++ b/src/pcre2_fuzzsupport.c
@@ -13,8 +13,19 @@ Written by Philip Hazel, October 2016
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
+/* stack size adjustment */
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#define STACK_SIZE_MB 256
+
+#ifndef PCRE2_CODE_UNIT_WIDTH
#define PCRE2_CODE_UNIT_WIDTH 8
+#endif
+
+#include "config.h"
#include "pcre2.h"
#define MAX_MATCH_SIZE 1000
@@ -36,6 +47,165 @@ Written by Philip Hazel, October 2016
PCRE2_NOTEMPTY_ATSTART|PCRE2_PARTIAL_HARD| \
PCRE2_PARTIAL_SOFT)
+static void print_compile_options(FILE *stream, uint32_t compile_options)
+{
+fprintf(stream, "Compile options %.8x never_backslash_c", compile_options);
+fprintf(stream, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+ ((compile_options & PCRE2_ALT_BSUX) != 0)? ",alt_bsux" : "",
+ ((compile_options & PCRE2_ALT_CIRCUMFLEX) != 0)? ",alt_circumflex" : "",
+ ((compile_options & PCRE2_ALT_VERBNAMES) != 0)? ",alt_verbnames" : "",
+ ((compile_options & PCRE2_ALLOW_EMPTY_CLASS) != 0)? ",allow_empty_class" : "",
+ ((compile_options & PCRE2_ANCHORED) != 0)? ",anchored" : "",
+ ((compile_options & PCRE2_AUTO_CALLOUT) != 0)? ",auto_callout" : "",
+ ((compile_options & PCRE2_CASELESS) != 0)? ",caseless" : "",
+ ((compile_options & PCRE2_DOLLAR_ENDONLY) != 0)? ",dollar_endonly" : "",
+ ((compile_options & PCRE2_DOTALL) != 0)? ",dotall" : "",
+ ((compile_options & PCRE2_DUPNAMES) != 0)? ",dupnames" : "",
+ ((compile_options & PCRE2_ENDANCHORED) != 0)? ",endanchored" : "",
+ ((compile_options & PCRE2_EXTENDED) != 0)? ",extended" : "",
+ ((compile_options & PCRE2_FIRSTLINE) != 0)? ",firstline" : "",
+ ((compile_options & PCRE2_MATCH_UNSET_BACKREF) != 0)? ",match_unset_backref" : "",
+ ((compile_options & PCRE2_MULTILINE) != 0)? ",multiline" : "",
+ ((compile_options & PCRE2_NEVER_UCP) != 0)? ",never_ucp" : "",
+ ((compile_options & PCRE2_NEVER_UTF) != 0)? ",never_utf" : "",
+ ((compile_options & PCRE2_NO_AUTO_CAPTURE) != 0)? ",no_auto_capture" : "",
+ ((compile_options & PCRE2_NO_AUTO_POSSESS) != 0)? ",no_auto_possess" : "",
+ ((compile_options & PCRE2_NO_DOTSTAR_ANCHOR) != 0)? ",no_dotstar_anchor" : "",
+ ((compile_options & PCRE2_NO_UTF_CHECK) != 0)? ",no_utf_check" : "",
+ ((compile_options & PCRE2_NO_START_OPTIMIZE) != 0)? ",no_start_optimize" : "",
+ ((compile_options & PCRE2_UCP) != 0)? ",ucp" : "",
+ ((compile_options & PCRE2_UNGREEDY) != 0)? ",ungreedy" : "",
+ ((compile_options & PCRE2_USE_OFFSET_LIMIT) != 0)? ",use_offset_limit" : "",
+ ((compile_options & PCRE2_UTF) != 0)? ",utf" : "");
+}
+
+static void print_match_options(FILE *stream, uint32_t match_options)
+{
+fprintf(stream, "Match options %.8x", match_options);
+fprintf(stream, "%s%s%s%s%s%s%s%s%s\n",
+ ((match_options & PCRE2_ANCHORED) != 0)? ",anchored" : "",
+ ((match_options & PCRE2_ENDANCHORED) != 0)? ",endanchored" : "",
+ ((match_options & PCRE2_NO_UTF_CHECK) != 0)? ",no_utf_check" : "",
+ ((match_options & PCRE2_NOTBOL) != 0)? ",notbol" : "",
+ ((match_options & PCRE2_NOTEMPTY) != 0)? ",notempty" : "",
+ ((match_options & PCRE2_NOTEMPTY_ATSTART) != 0)? ",notempty_atstart" : "",
+ ((match_options & PCRE2_NOTEOL) != 0)? ",noteol" : "",
+ ((match_options & PCRE2_PARTIAL_HARD) != 0)? ",partial_hard" : "",
+ ((match_options & PCRE2_PARTIAL_SOFT) != 0)? ",partial_soft" : "");
+}
+
+static void dump_matches(FILE *stream, int count, pcre2_match_data *match_data, pcre2_match_context *match_context)
+{
+#if PCRE2_CODE_UNIT_WIDTH == 8
+PCRE2_UCHAR error_buf[256];
+#endif
+int errorcode;
+
+for (uint32_t index = 0; index < count; index++)
+ {
+ PCRE2_UCHAR *bufferptr = NULL;
+ PCRE2_SIZE bufflen = 0;
+
+ errorcode = pcre2_substring_get_bynumber(match_data, index, &bufferptr, &bufflen);
+
+ if (errorcode >= 0)
+ {
+ fprintf(stream, "Match %d (hex encoded): ", index);
+ for (PCRE2_SIZE i = 0; i < bufflen; i++)
+ {
+ fprintf(stream, "%02x", bufferptr[i]);
+ }
+ fprintf(stream, "\n");
+ }
+ else
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ pcre2_get_error_message(errorcode, error_buf, 256);
+ fprintf(stream, "Match %d failed: %s\n", index, error_buf);
+#else
+ fprintf(stream, "Match %d failed: %d\n", index, errorcode);
+#endif
+ }
+ }
+}
+
+/* This function describes the current test case being evaluated, then aborts */
+
+#ifdef SUPPORT_JIT
+static void describe_failure(
+ const char *task,
+ const unsigned char *data,
+ size_t size,
+ uint32_t compile_options,
+ uint32_t match_options,
+ int errorcode,
+ int errorcode_jit,
+ int matches,
+ int matches_jit,
+ pcre2_match_data *match_data,
+ pcre2_match_data *match_data_jit,
+ pcre2_match_context *match_context
+) {
+#if PCRE2_CODE_UNIT_WIDTH == 8
+PCRE2_UCHAR buffer[256];
+#endif
+
+fprintf(stderr, "Encountered failure while performing %s; context:\n", task);
+
+fprintf(stderr, "Pattern/sample string (hex encoded): ");
+for (size_t i = 0; i < size; i++)
+ {
+ fprintf(stderr, "%02x", data[i]);
+ }
+fprintf(stderr, "\n");
+
+print_compile_options(stderr, compile_options);
+print_match_options(stderr, match_options);
+
+if (errorcode < 0)
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ pcre2_get_error_message(errorcode, buffer, 256);
+ fprintf(stderr, "Non-JIT'd operation emitted an error: %s (%d)\n", buffer, errorcode);
+#else
+ fprintf(stderr, "Non-JIT'd operation emitted an error: %d\n", errorcode);
+#endif
+ }
+if (matches >= 0)
+ {
+ fprintf(stderr, "Non-JIT'd operation did not emit an error.\n");
+ if (match_data != NULL)
+ {
+ fprintf(stderr, "%d matches discovered by non-JIT'd regex:\n", matches);
+ dump_matches(stderr, matches, match_data, match_context);
+ fprintf(stderr, "\n");
+ }
+ }
+
+if (errorcode_jit < 0)
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ pcre2_get_error_message(errorcode_jit, buffer, 256);
+ fprintf(stderr, "JIT'd operation emitted an error: %s (%d)\n", buffer, errorcode_jit);
+#else
+ fprintf(stderr, "JIT'd operation emitted an error: %d\n", errorcode);
+#endif
+ }
+if (matches_jit >= 0)
+ {
+ fprintf(stderr, "JIT'd operation did not emit an error.\n");
+ if (match_data_jit != NULL)
+ {
+ fprintf(stderr, "%d matches discovered by JIT'd regex:\n", matches_jit);
+ dump_matches(stderr, matches_jit, match_data_jit, match_context);
+ fprintf(stderr, "\n");
+ }
+ }
+
+abort();
+}
+#endif
+
/* This is the callout function. Its only purpose is to halt matching if there
are more than 100 callouts, as one way of stopping too much time being spent on
fruitless matches. The callout data is a pointer to the counter. */
@@ -50,8 +220,31 @@ return (*((uint32_t *)callout_data) > 100)? PCRE2_ERROR_CALLOUT : 0;
/* Putting in this apparently unnecessary prototype prevents gcc from giving a
"no previous prototype" warning when compiling at high warning level. */
+int LLVMFuzzerInitialize(int *, char ***);
+
int LLVMFuzzerTestOneInput(const unsigned char *, size_t);
+int LLVMFuzzerInitialize(int *argc, char ***argv)
+{
+int rc;
+struct rlimit rlim;
+getrlimit(RLIMIT_STACK, &rlim);
+rlim.rlim_cur = STACK_SIZE_MB * 1024 * 1024;
+if (rlim.rlim_cur > rlim.rlim_max)
+ {
+ fprintf(stderr, "hard stack size limit is too small (needed 8MiB)!\n");
+ _exit(1);
+ }
+rc = setrlimit(RLIMIT_STACK, &rlim);
+if (rc != 0)
+ {
+ fprintf(stderr, "failed to expand stack size\n");
+ _exit(1);
+ }
+
+return 0;
+}
+
/* Here's the driving function. */
int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size)
@@ -76,6 +269,7 @@ in large trees taking too much time. */
random_options = *(uint64_t *)(data);
data += sizeof(random_options);
size -= sizeof(random_options);
+size /= PCRE2_CODE_UNIT_WIDTH / 8;
match_size = (size > MAX_MATCH_SIZE)? MAX_MATCH_SIZE : size;
@@ -87,7 +281,9 @@ reason to disallow UTF and UCP. Force PCRE2_NEVER_BACKSLASH_C to be set because
compile_options = ((random_options >> 32) & ALLOWED_COMPILE_OPTIONS) |
PCRE2_NEVER_BACKSLASH_C;
-match_options = (((uint32_t)random_options) & ALLOWED_MATCH_OPTIONS) | PCRE2_NO_JIT;
+match_options = (((uint32_t)random_options) & ALLOWED_MATCH_OPTIONS) |
+ PCRE2_NO_JIT |
+ PCRE2_DISABLE_RECURSELOOP_CHECK;
/* Discard partial matching if PCRE2_ENDANCHORED is set, because they are not
allowed together and just give an immediate error return. */
@@ -104,40 +300,14 @@ for (i = 0; i < 2; i++)
int errorcode;
#ifdef SUPPORT_JIT
int errorcode_jit;
- uint32_t ovector_count;
+ int matches = 0;
+ int matches_jit = 0;
#endif
PCRE2_SIZE erroroffset;
pcre2_code *code;
#ifdef STANDALONE
- printf("Compile options %.8x never_backslash_c", compile_options);
- printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
- ((compile_options & PCRE2_ALT_BSUX) != 0)? ",alt_bsux" : "",
- ((compile_options & PCRE2_ALT_CIRCUMFLEX) != 0)? ",alt_circumflex" : "",
- ((compile_options & PCRE2_ALT_VERBNAMES) != 0)? ",alt_verbnames" : "",
- ((compile_options & PCRE2_ALLOW_EMPTY_CLASS) != 0)? ",allow_empty_class" : "",
- ((compile_options & PCRE2_ANCHORED) != 0)? ",anchored" : "",
- ((compile_options & PCRE2_AUTO_CALLOUT) != 0)? ",auto_callout" : "",
- ((compile_options & PCRE2_CASELESS) != 0)? ",caseless" : "",
- ((compile_options & PCRE2_DOLLAR_ENDONLY) != 0)? ",dollar_endonly" : "",
- ((compile_options & PCRE2_DOTALL) != 0)? ",dotall" : "",
- ((compile_options & PCRE2_DUPNAMES) != 0)? ",dupnames" : "",
- ((compile_options & PCRE2_ENDANCHORED) != 0)? ",endanchored" : "",
- ((compile_options & PCRE2_EXTENDED) != 0)? ",extended" : "",
- ((compile_options & PCRE2_FIRSTLINE) != 0)? ",firstline" : "",
- ((compile_options & PCRE2_MATCH_UNSET_BACKREF) != 0)? ",match_unset_backref" : "",
- ((compile_options & PCRE2_MULTILINE) != 0)? ",multiline" : "",
- ((compile_options & PCRE2_NEVER_UCP) != 0)? ",never_ucp" : "",
- ((compile_options & PCRE2_NEVER_UTF) != 0)? ",never_utf" : "",
- ((compile_options & PCRE2_NO_AUTO_CAPTURE) != 0)? ",no_auto_capture" : "",
- ((compile_options & PCRE2_NO_AUTO_POSSESS) != 0)? ",no_auto_possess" : "",
- ((compile_options & PCRE2_NO_DOTSTAR_ANCHOR) != 0)? ",no_dotstar_anchor" : "",
- ((compile_options & PCRE2_NO_UTF_CHECK) != 0)? ",no_utf_check" : "",
- ((compile_options & PCRE2_NO_START_OPTIMIZE) != 0)? ",no_start_optimize" : "",
- ((compile_options & PCRE2_UCP) != 0)? ",ucp" : "",
- ((compile_options & PCRE2_UNGREEDY) != 0)? ",ungreedy" : "",
- ((compile_options & PCRE2_USE_OFFSET_LIMIT) != 0)? ",use_offset_limit" : "",
- ((compile_options & PCRE2_UTF) != 0)? ",utf" : "");
+ print_compile_options(stdout, compile_options);
#endif
code = pcre2_compile((PCRE2_SPTR)data, (PCRE2_SIZE)size, compile_options,
@@ -169,7 +339,7 @@ for (i = 0; i < 2; i++)
#endif
{
#ifdef STANDALONE
- printf("** Failed to create match data block\n");
+ fprintf(stderr, "** Failed to create match data block\n");
#endif
abort();
}
@@ -181,7 +351,7 @@ for (i = 0; i < 2; i++)
if (match_context == NULL)
{
#ifdef STANDALONE
- printf("** Failed to create match context block\n");
+ fprintf(stderr, "** Failed to create match context block\n");
#endif
abort();
}
@@ -195,18 +365,7 @@ for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++)
{
#ifdef STANDALONE
- printf("Match options %.8x", match_options);
- printf("%s%s%s%s%s%s%s%s%s%s\n",
- ((match_options & PCRE2_ANCHORED) != 0)? ",anchored" : "",
- ((match_options & PCRE2_ENDANCHORED) != 0)? ",endanchored" : "",
- ((match_options & PCRE2_NO_JIT) != 0)? ",no_jit" : "",
- ((match_options & PCRE2_NO_UTF_CHECK) != 0)? ",no_utf_check" : "",
- ((match_options & PCRE2_NOTBOL) != 0)? ",notbol" : "",
- ((match_options & PCRE2_NOTEMPTY) != 0)? ",notempty" : "",
- ((match_options & PCRE2_NOTEMPTY_ATSTART) != 0)? ",notempty_atstart" : "",
- ((match_options & PCRE2_NOTEOL) != 0)? ",noteol" : "",
- ((match_options & PCRE2_PARTIAL_HARD) != 0)? ",partial_hard" : "",
- ((match_options & PCRE2_PARTIAL_SOFT) != 0)? ",partial_soft" : "");
+ print_match_options(stdout, match_options);
#endif
callout_count = 0;
@@ -216,9 +375,13 @@ for (i = 0; i < 2; i++)
#ifdef STANDALONE
if (errorcode >= 0) printf("Match returned %d\n", errorcode); else
{
+#if PCRE2_CODE_UNIT_WIDTH == 8
unsigned char buffer[256];
pcre2_get_error_message(errorcode, buffer, 256);
printf("Match failed: error %d: %s\n", errorcode, buffer);
+#else
+ printf("Match failed: error %d\n", errorcode);
+#endif
}
#endif
@@ -229,54 +392,52 @@ for (i = 0; i < 2; i++)
errorcode_jit = pcre2_match(code, (PCRE2_SPTR)data, (PCRE2_SIZE)match_size, 0,
match_options & ~PCRE2_NO_JIT, match_data_jit, match_context);
- if (errorcode_jit != errorcode)
- {
- printf("JIT errorcode %d did not match original errorcode %d\n", errorcode_jit, errorcode);
- abort();
- }
-
- ovector_count = pcre2_get_ovector_count(match_data);
+ matches = errorcode;
+ matches_jit = errorcode_jit;
- if (ovector_count != pcre2_get_ovector_count(match_data_jit))
+ if (errorcode_jit != errorcode)
{
- puts("JIT ovector count did not match original");
- abort();
+ if (!(errorcode < 0 && errorcode_jit < 0) &&
+ errorcode != PCRE2_ERROR_MATCHLIMIT && errorcode != PCRE2_ERROR_CALLOUT &&
+ errorcode_jit != PCRE2_ERROR_MATCHLIMIT && errorcode_jit != PCRE2_ERROR_JIT_STACKLIMIT && errorcode_jit != PCRE2_ERROR_CALLOUT)
+ {
+ describe_failure("match errorcode comparison", data, size, compile_options, match_options, errorcode, errorcode_jit, matches, matches_jit, match_data, match_data_jit, match_context);
+ }
}
-
- for (uint32_t ovector = 0; ovector < ovector_count; ovector++)
+ else
{
- PCRE2_UCHAR *bufferptr, *bufferptr_jit;
- PCRE2_SIZE bufflen, bufflen_jit;
+ for (int index = 0; index < errorcode; index++)
+ {
+ PCRE2_UCHAR *bufferptr, *bufferptr_jit;
+ PCRE2_SIZE bufflen, bufflen_jit;
- bufferptr = bufferptr_jit = NULL;
- bufflen = bufflen_jit = 0;
+ bufferptr = bufferptr_jit = NULL;
+ bufflen = bufflen_jit = 0;
- errorcode = pcre2_substring_get_bynumber(match_data, ovector, &bufferptr, &bufflen);
- errorcode_jit = pcre2_substring_get_bynumber(match_data_jit, ovector, &bufferptr_jit, &bufflen_jit);
+ errorcode = pcre2_substring_get_bynumber(match_data, (uint32_t) index, &bufferptr, &bufflen);
+ errorcode_jit = pcre2_substring_get_bynumber(match_data_jit, (uint32_t) index, &bufferptr_jit, &bufflen_jit);
- if (errorcode != errorcode_jit)
- {
- printf("when extracting substring, JIT errorcode %d did not match original %d\n", errorcode_jit, errorcode);
- abort();
- }
-
- if (errorcode >= 0)
- {
- if (bufflen != bufflen_jit)
+ if (errorcode != errorcode_jit)
{
- printf("when extracting substring, JIT buffer length %zu did not match original %zu\n", bufflen_jit, bufflen);
- abort();
+ describe_failure("match entry errorcode comparison", data, size, compile_options, match_options, errorcode, errorcode_jit, matches, matches_jit, match_data, match_data_jit, match_context);
}
- if (memcmp(bufferptr, bufferptr_jit, bufflen) != 0)
+ if (errorcode >= 0)
{
- puts("when extracting substring, JIT buffer contents did not match original");
- abort();
+ if (bufflen != bufflen_jit)
+ {
+ describe_failure("match entry length comparison", data, size, compile_options, match_options, errorcode, errorcode_jit, matches, matches_jit, match_data, match_data_jit, match_context);
+ }
+
+ if (memcmp(bufferptr, bufferptr_jit, bufflen) != 0)
+ {
+ describe_failure("match entry content comparison", data, size, compile_options, match_options, errorcode, errorcode_jit, matches, matches_jit, match_data, match_data_jit, match_context);
+ }
}
- }
- pcre2_substring_free(bufferptr);
- pcre2_substring_free(bufferptr_jit);
+ pcre2_substring_free(bufferptr);
+ pcre2_substring_free(bufferptr_jit);
+ }
}
}
#endif
@@ -312,9 +473,13 @@ for (i = 0; i < 2; i++)
#ifdef STANDALONE
if (errorcode >= 0) printf("Match returned %d\n", errorcode); else
{
+#if PCRE2_CODE_UNIT_WIDTH == 8
unsigned char buffer[256];
pcre2_get_error_message(errorcode, buffer, 256);
printf("Match failed: error %d: %s\n", errorcode, buffer);
+#else
+ printf("Match failed: error %d\n", errorcode);
+#endif
}
#endif
@@ -329,12 +494,17 @@ for (i = 0; i < 2; i++)
else
{
+#ifdef STANDALONE
+#if PCRE2_CODE_UNIT_WIDTH == 8
unsigned char buffer[256];
pcre2_get_error_message(errorcode, buffer, 256);
-#ifdef STANDALONE
printf("Error %d at offset %lu: %s\n", errorcode, erroroffset, buffer);
#else
- if (strstr((const char *)buffer, "internal error") != NULL) abort();
+ printf("Error %d at offset %lu\n", errorcode, erroroffset);
+#endif
+
+#else
+ if (errorcode == PCRE2_ERROR_INTERNAL) abort();
#endif
}
@@ -358,6 +528,8 @@ int main(int argc, char **argv)
{
int i;
+LLVMFuzzerInitialize(&argc, &argv);
+
if (argc < 2)
{
printf("** No arguments given\n");