diff options
author | Nick Wellnhofer <wellnhofer@aevum.de> | 2019-04-15 16:00:14 +0200 |
---|---|---|
committer | Nick Wellnhofer <wellnhofer@aevum.de> | 2019-04-15 18:48:35 +0200 |
commit | 7f56378a73af8a61da78b8091f0e317316cee300 (patch) | |
tree | 12d079e8943ed2687cdfd9ea2eb23987d3590637 | |
parent | 06d193fabb096370a969ca9f017f60bca7057262 (diff) | |
download | libxslt-7f56378a73af8a61da78b8091f0e317316cee300.tar.gz |
Add libFuzzer targets
131 files changed, 758 insertions, 2 deletions
@@ -1,4 +1,4 @@ -*/*.o +*.o */*.lo tags */tags diff --git a/configure.ac b/configure.ac index 0780bc80..027081a3 100644 --- a/configure.ac +++ b/configure.ac @@ -661,9 +661,12 @@ tests/exslt/date/Makefile tests/exslt/dynamic/Makefile tests/exslt/crypto/Makefile tests/plugins/Makefile +tests/fuzz/Makefile doc/Makefile xslt-config libxslt.spec ]) +AC_CONFIG_LINKS([tests/fuzz/xpath.xml:tests/fuzz/xpath.xml]) +AC_CONFIG_LINKS([tests/fuzz/xslt.xml:tests/fuzz/xslt.xml]) AC_OUTPUT diff --git a/tests/Makefile.am b/tests/Makefile.am index 3eb1c3f4..5645bb01 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -2,7 +2,7 @@ SUBDIRS=docs REC1 REC2 REC general namespaces keys numbers documents \ extensions reports xmlspec multiple xinclude XSLTMark docbook \ - exslt plugins + exslt plugins fuzz all: diff --git a/tests/fuzz/.gitignore b/tests/fuzz/.gitignore new file mode 100644 index 00000000..15b2878d --- /dev/null +++ b/tests/fuzz/.gitignore @@ -0,0 +1,3 @@ +/corpus/ +/xpath +/xslt diff --git a/tests/fuzz/Makefile.am b/tests/fuzz/Makefile.am new file mode 100644 index 00000000..afb5e322 --- /dev/null +++ b/tests/fuzz/Makefile.am @@ -0,0 +1,34 @@ +LIBXSLT_LIBS = $(top_builddir)/libxslt/libxslt.la \ + $(top_builddir)/libexslt/libexslt.la + +EXTRA_PROGRAMS = xpath xslt +EXTRA_DIST = xpath.dict xpath.xml xslt.dict xslt.xml seed +CLEANFILES = $(EXTRA_PROGRAMS) +AM_CPPFLAGS = -I$(top_srcdir) +AM_CFLAGS = $(LIBXML_CFLAGS) +AM_LDFLAGS = -fsanitize=fuzzer +DEPENDENCIES = $(LIBXSLT_LIBS) +LDADD = $(LIBXSLT_LIBS) \ + $(LIBGCRYPT_LIBS) $(LIBXML_LIBS) $(EXTRA_LIBS) $(M_LIBS) + +$(top_builddir)/libxslt/libxslt.la: + cd $(top_builddir)/libxslt && $(MAKE) libxslt.la + +$(top_builddir)/libexslt/libexslt.la: $(top_builddir)/libxslt/libxslt.la + cd $(top_builddir)/libexslt && $(MAKE) libexslt.la + +.PHONY: fuzz-xpath fuzz-xslt + +fuzz-xpath: xpath$(EXEEXT) + @mkdir -p corpus/xpath + ./xpath$(EXEEXT) \ + -max_len=256 \ + -dict=$(srcdir)/xpath.dict \ + corpus/xpath $(srcdir)/seed/xpath + +fuzz-xslt: xslt$(EXEEXT) + @mkdir -p corpus/xslt + ./xslt$(EXEEXT) \ + -dict=$(srcdir)/xslt.dict \ + corpus/xslt $(srcdir)/seed/xslt + diff --git a/tests/fuzz/README b/tests/fuzz/README new file mode 100644 index 00000000..804ea124 --- /dev/null +++ b/tests/fuzz/README @@ -0,0 +1,29 @@ +libFuzzer instructions for libxslt +================================== + +Set compiler and options. Disable float-divide-by-zero and pointer-overflow +sanitizers when using UBSan. + + export CC=clang + export CFLAGS="-g -fsanitize=fuzzer-no-link,address,undefined \ + -fno-sanitize=float-divide-by-zero,pointer-overflow \ + -fno-sanitize-recover=all \ + -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION" + +Build libxml2 with instrumentation: + + cd /path/to/libxml2 + ./configure --without-python + make + +Build libxslt with instrumentation: + + cd /path/to/libxslt + ./configure --without-python --with-libxml-src=/path/to/libxml2 + make + +Run fuzzers: + + make -C tests/fuzz fuzz-xslt + make -C tests/fuzz fuzz-xpath + diff --git a/tests/fuzz/seed/xpath/crypto_md4 b/tests/fuzz/seed/xpath/crypto_md4 new file mode 100644 index 00000000..af26aed1 --- /dev/null +++ b/tests/fuzz/seed/xpath/crypto_md4 @@ -0,0 +1 @@ +crypto:md4('a') diff --git a/tests/fuzz/seed/xpath/crypto_md5 b/tests/fuzz/seed/xpath/crypto_md5 new file mode 100644 index 00000000..e0af6d39 --- /dev/null +++ b/tests/fuzz/seed/xpath/crypto_md5 @@ -0,0 +1 @@ +crypto:md5('a') diff --git a/tests/fuzz/seed/xpath/crypto_rc4_decrypt b/tests/fuzz/seed/xpath/crypto_rc4_decrypt new file mode 100644 index 00000000..fe346720 --- /dev/null +++ b/tests/fuzz/seed/xpath/crypto_rc4_decrypt @@ -0,0 +1 @@ +crypto:rc4_decrypt(crypto:rc4_encrypt('key','msg')) diff --git a/tests/fuzz/seed/xpath/crypto_sha1 b/tests/fuzz/seed/xpath/crypto_sha1 new file mode 100644 index 00000000..a139200d --- /dev/null +++ b/tests/fuzz/seed/xpath/crypto_sha1 @@ -0,0 +1 @@ +crypto:sha1('a') diff --git a/tests/fuzz/seed/xpath/date_add b/tests/fuzz/seed/xpath/date_add new file mode 100644 index 00000000..e12bd62c --- /dev/null +++ b/tests/fuzz/seed/xpath/date_add @@ -0,0 +1 @@ +date:add('2016-01-01T12:00:00','-P1Y2M3DT10H30M45S') diff --git a/tests/fuzz/seed/xpath/date_add_duration b/tests/fuzz/seed/xpath/date_add_duration new file mode 100644 index 00000000..3add82f3 --- /dev/null +++ b/tests/fuzz/seed/xpath/date_add_duration @@ -0,0 +1 @@ +date:add-duration('-P1Y2M3DT10H30M45S','-P1Y2M3DT10H30M45S') diff --git a/tests/fuzz/seed/xpath/date_date b/tests/fuzz/seed/xpath/date_date new file mode 100644 index 00000000..db7eb0fb --- /dev/null +++ b/tests/fuzz/seed/xpath/date_date @@ -0,0 +1 @@ +date:date('2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/date_date_time b/tests/fuzz/seed/xpath/date_date_time new file mode 100644 index 00000000..56c80c79 --- /dev/null +++ b/tests/fuzz/seed/xpath/date_date_time @@ -0,0 +1 @@ +date:date-time() diff --git a/tests/fuzz/seed/xpath/date_day_abbreviation b/tests/fuzz/seed/xpath/date_day_abbreviation new file mode 100644 index 00000000..ea19a5d4 --- /dev/null +++ b/tests/fuzz/seed/xpath/date_day_abbreviation @@ -0,0 +1 @@ +date:day-abbreviation('2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/date_day_in_month b/tests/fuzz/seed/xpath/date_day_in_month new file mode 100644 index 00000000..2b21b611 --- /dev/null +++ b/tests/fuzz/seed/xpath/date_day_in_month @@ -0,0 +1 @@ +date:day-in-month('2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/date_day_in_week b/tests/fuzz/seed/xpath/date_day_in_week new file mode 100644 index 00000000..78343b04 --- /dev/null +++ b/tests/fuzz/seed/xpath/date_day_in_week @@ -0,0 +1 @@ +date:day-in-week('2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/date_day_in_year b/tests/fuzz/seed/xpath/date_day_in_year new file mode 100644 index 00000000..32e55743 --- /dev/null +++ b/tests/fuzz/seed/xpath/date_day_in_year @@ -0,0 +1 @@ +date:day-in-year('2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/date_day_name b/tests/fuzz/seed/xpath/date_day_name new file mode 100644 index 00000000..69d66aee --- /dev/null +++ b/tests/fuzz/seed/xpath/date_day_name @@ -0,0 +1 @@ +date:day-name('2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/date_day_of_week_in_month b/tests/fuzz/seed/xpath/date_day_of_week_in_month new file mode 100644 index 00000000..e2525bd7 --- /dev/null +++ b/tests/fuzz/seed/xpath/date_day_of_week_in_month @@ -0,0 +1 @@ +date:day-of-week-in-month('2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/date_difference b/tests/fuzz/seed/xpath/date_difference new file mode 100644 index 00000000..deb5b9d1 --- /dev/null +++ b/tests/fuzz/seed/xpath/date_difference @@ -0,0 +1 @@ +date:difference('1999-06-10T20:03:48','2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/date_duration b/tests/fuzz/seed/xpath/date_duration new file mode 100644 index 00000000..1c8f4acb --- /dev/null +++ b/tests/fuzz/seed/xpath/date_duration @@ -0,0 +1 @@ +date:duration('1234567890') diff --git a/tests/fuzz/seed/xpath/date_format_date b/tests/fuzz/seed/xpath/date_format_date new file mode 100644 index 00000000..7025e1d5 --- /dev/null +++ b/tests/fuzz/seed/xpath/date_format_date @@ -0,0 +1 @@ +date:format-date('2016-01-01T12:00:00','GyyyyMMwwWWDDddFFEaHHkkKKhhMMssSSSzZ') diff --git a/tests/fuzz/seed/xpath/date_hour_in_day b/tests/fuzz/seed/xpath/date_hour_in_day new file mode 100644 index 00000000..68ed1f00 --- /dev/null +++ b/tests/fuzz/seed/xpath/date_hour_in_day @@ -0,0 +1 @@ +date:hour-in-day('2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/date_leap_year b/tests/fuzz/seed/xpath/date_leap_year new file mode 100644 index 00000000..4f7b98da --- /dev/null +++ b/tests/fuzz/seed/xpath/date_leap_year @@ -0,0 +1 @@ +date:leap-year('2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/date_minute_in_hour b/tests/fuzz/seed/xpath/date_minute_in_hour new file mode 100644 index 00000000..865fbc25 --- /dev/null +++ b/tests/fuzz/seed/xpath/date_minute_in_hour @@ -0,0 +1 @@ +date:minute-in-hour('2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/date_month_abbreviation b/tests/fuzz/seed/xpath/date_month_abbreviation new file mode 100644 index 00000000..89a2e2c4 --- /dev/null +++ b/tests/fuzz/seed/xpath/date_month_abbreviation @@ -0,0 +1 @@ +date:month-abbreviation('2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/date_month_in_year b/tests/fuzz/seed/xpath/date_month_in_year new file mode 100644 index 00000000..07c0c12d --- /dev/null +++ b/tests/fuzz/seed/xpath/date_month_in_year @@ -0,0 +1 @@ +date:month-in-year('2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/date_month_name b/tests/fuzz/seed/xpath/date_month_name new file mode 100644 index 00000000..7da78e79 --- /dev/null +++ b/tests/fuzz/seed/xpath/date_month_name @@ -0,0 +1 @@ +date:month-name('2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/date_parse_date b/tests/fuzz/seed/xpath/date_parse_date new file mode 100644 index 00000000..cea782cc --- /dev/null +++ b/tests/fuzz/seed/xpath/date_parse_date @@ -0,0 +1 @@ +date:parse-date('20160101120000','yyyyMMddkkmmss') diff --git a/tests/fuzz/seed/xpath/date_second_in_minute b/tests/fuzz/seed/xpath/date_second_in_minute new file mode 100644 index 00000000..2574fa74 --- /dev/null +++ b/tests/fuzz/seed/xpath/date_second_in_minute @@ -0,0 +1 @@ +date:second-in-minute('2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/date_seconds b/tests/fuzz/seed/xpath/date_seconds new file mode 100644 index 00000000..48ec1c2a --- /dev/null +++ b/tests/fuzz/seed/xpath/date_seconds @@ -0,0 +1 @@ +date:seconds('2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/date_sum b/tests/fuzz/seed/xpath/date_sum new file mode 100644 index 00000000..39500251 --- /dev/null +++ b/tests/fuzz/seed/xpath/date_sum @@ -0,0 +1 @@ +date:sum(str:split('-P1Y2M3DT10H30M45S,-P1Y2M3DT10H30M45S,P999999999S',',')) diff --git a/tests/fuzz/seed/xpath/date_time b/tests/fuzz/seed/xpath/date_time new file mode 100644 index 00000000..3333638c --- /dev/null +++ b/tests/fuzz/seed/xpath/date_time @@ -0,0 +1 @@ +date:time('2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/date_week_in_month b/tests/fuzz/seed/xpath/date_week_in_month new file mode 100644 index 00000000..415ed927 --- /dev/null +++ b/tests/fuzz/seed/xpath/date_week_in_month @@ -0,0 +1 @@ +date:week-in-month('2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/date_week_in_year b/tests/fuzz/seed/xpath/date_week_in_year new file mode 100644 index 00000000..baf0c2cd --- /dev/null +++ b/tests/fuzz/seed/xpath/date_week_in_year @@ -0,0 +1 @@ +date:week-in-year('2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/date_year b/tests/fuzz/seed/xpath/date_year new file mode 100644 index 00000000..b615fde4 --- /dev/null +++ b/tests/fuzz/seed/xpath/date_year @@ -0,0 +1 @@ +date:year('2016-01-01T12:00:00') diff --git a/tests/fuzz/seed/xpath/dyn_evaluate b/tests/fuzz/seed/xpath/dyn_evaluate new file mode 100644 index 00000000..cb0f284b --- /dev/null +++ b/tests/fuzz/seed/xpath/dyn_evaluate @@ -0,0 +1 @@ +dyn:evaluate('1+1') diff --git a/tests/fuzz/seed/xpath/dyn_map b/tests/fuzz/seed/xpath/dyn_map new file mode 100644 index 00000000..361ddb39 --- /dev/null +++ b/tests/fuzz/seed/xpath/dyn_map @@ -0,0 +1 @@ +dyn:map(//*,'.') diff --git a/tests/fuzz/seed/xpath/expr_arith b/tests/fuzz/seed/xpath/expr_arith new file mode 100644 index 00000000..bc4813df --- /dev/null +++ b/tests/fuzz/seed/xpath/expr_arith @@ -0,0 +1 @@ +(1.1+-24.5)*0.8-(25div3.5)mod0.2 diff --git a/tests/fuzz/seed/xpath/expr_location_path b/tests/fuzz/seed/xpath/expr_location_path new file mode 100644 index 00000000..789255c2 --- /dev/null +++ b/tests/fuzz/seed/xpath/expr_location_path @@ -0,0 +1 @@ +/a/b/c/text()|//e/c:d/@b diff --git a/tests/fuzz/seed/xpath/expr_predicate b/tests/fuzz/seed/xpath/expr_predicate new file mode 100644 index 00000000..191b3051 --- /dev/null +++ b/tests/fuzz/seed/xpath/expr_predicate @@ -0,0 +1 @@ +(//*[@*][1])[1] diff --git a/tests/fuzz/seed/xpath/exsl_object_type b/tests/fuzz/seed/xpath/exsl_object_type new file mode 100644 index 00000000..2a3784a2 --- /dev/null +++ b/tests/fuzz/seed/xpath/exsl_object_type @@ -0,0 +1 @@ +exsl:object-type(1) diff --git a/tests/fuzz/seed/xpath/func_boolean b/tests/fuzz/seed/xpath/func_boolean new file mode 100644 index 00000000..409bec66 --- /dev/null +++ b/tests/fuzz/seed/xpath/func_boolean @@ -0,0 +1 @@ +boolean(.) diff --git a/tests/fuzz/seed/xpath/func_ceiling b/tests/fuzz/seed/xpath/func_ceiling new file mode 100644 index 00000000..f23305db --- /dev/null +++ b/tests/fuzz/seed/xpath/func_ceiling @@ -0,0 +1 @@ +ceiling(.) diff --git a/tests/fuzz/seed/xpath/func_concat b/tests/fuzz/seed/xpath/func_concat new file mode 100644 index 00000000..b6c9a1b1 --- /dev/null +++ b/tests/fuzz/seed/xpath/func_concat @@ -0,0 +1 @@ +concat(.,'a') diff --git a/tests/fuzz/seed/xpath/func_contains b/tests/fuzz/seed/xpath/func_contains new file mode 100644 index 00000000..ed18a39c --- /dev/null +++ b/tests/fuzz/seed/xpath/func_contains @@ -0,0 +1 @@ +contains(.,'e') diff --git a/tests/fuzz/seed/xpath/func_count b/tests/fuzz/seed/xpath/func_count new file mode 100644 index 00000000..3fea95c0 --- /dev/null +++ b/tests/fuzz/seed/xpath/func_count @@ -0,0 +1 @@ +count(.) diff --git a/tests/fuzz/seed/xpath/func_false b/tests/fuzz/seed/xpath/func_false new file mode 100644 index 00000000..f9e9b505 --- /dev/null +++ b/tests/fuzz/seed/xpath/func_false @@ -0,0 +1 @@ +false() diff --git a/tests/fuzz/seed/xpath/func_floor b/tests/fuzz/seed/xpath/func_floor new file mode 100644 index 00000000..6c0d2db0 --- /dev/null +++ b/tests/fuzz/seed/xpath/func_floor @@ -0,0 +1 @@ +floor(.) diff --git a/tests/fuzz/seed/xpath/func_id b/tests/fuzz/seed/xpath/func_id new file mode 100644 index 00000000..9c29ea0d --- /dev/null +++ b/tests/fuzz/seed/xpath/func_id @@ -0,0 +1 @@ +id(.) diff --git a/tests/fuzz/seed/xpath/func_lang b/tests/fuzz/seed/xpath/func_lang new file mode 100644 index 00000000..1ff69fd2 --- /dev/null +++ b/tests/fuzz/seed/xpath/func_lang @@ -0,0 +1 @@ +lang(.) diff --git a/tests/fuzz/seed/xpath/func_last b/tests/fuzz/seed/xpath/func_last new file mode 100644 index 00000000..06e7e03f --- /dev/null +++ b/tests/fuzz/seed/xpath/func_last @@ -0,0 +1 @@ +last() diff --git a/tests/fuzz/seed/xpath/func_local_name b/tests/fuzz/seed/xpath/func_local_name new file mode 100644 index 00000000..1a0193b3 --- /dev/null +++ b/tests/fuzz/seed/xpath/func_local_name @@ -0,0 +1 @@ +local-name(.) diff --git a/tests/fuzz/seed/xpath/func_name b/tests/fuzz/seed/xpath/func_name new file mode 100644 index 00000000..6bce7bf5 --- /dev/null +++ b/tests/fuzz/seed/xpath/func_name @@ -0,0 +1 @@ +name(.) diff --git a/tests/fuzz/seed/xpath/func_namespace_uri b/tests/fuzz/seed/xpath/func_namespace_uri new file mode 100644 index 00000000..649a382c --- /dev/null +++ b/tests/fuzz/seed/xpath/func_namespace_uri @@ -0,0 +1 @@ +namespace-uri(.) diff --git a/tests/fuzz/seed/xpath/func_normalize_space b/tests/fuzz/seed/xpath/func_normalize_space new file mode 100644 index 00000000..84f6777f --- /dev/null +++ b/tests/fuzz/seed/xpath/func_normalize_space @@ -0,0 +1 @@ +normalize-space(.) diff --git a/tests/fuzz/seed/xpath/func_not b/tests/fuzz/seed/xpath/func_not new file mode 100644 index 00000000..44bc806e --- /dev/null +++ b/tests/fuzz/seed/xpath/func_not @@ -0,0 +1 @@ +not(.) diff --git a/tests/fuzz/seed/xpath/func_number_node b/tests/fuzz/seed/xpath/func_number_node new file mode 100644 index 00000000..c1f1c489 --- /dev/null +++ b/tests/fuzz/seed/xpath/func_number_node @@ -0,0 +1 @@ +number(.) diff --git a/tests/fuzz/seed/xpath/func_number_str b/tests/fuzz/seed/xpath/func_number_str new file mode 100644 index 00000000..31a8d993 --- /dev/null +++ b/tests/fuzz/seed/xpath/func_number_str @@ -0,0 +1 @@ +number('1.0') diff --git a/tests/fuzz/seed/xpath/func_position b/tests/fuzz/seed/xpath/func_position new file mode 100644 index 00000000..ec993e8b --- /dev/null +++ b/tests/fuzz/seed/xpath/func_position @@ -0,0 +1 @@ +position() diff --git a/tests/fuzz/seed/xpath/func_round b/tests/fuzz/seed/xpath/func_round new file mode 100644 index 00000000..54315843 --- /dev/null +++ b/tests/fuzz/seed/xpath/func_round @@ -0,0 +1 @@ +round(.) diff --git a/tests/fuzz/seed/xpath/func_start_with b/tests/fuzz/seed/xpath/func_start_with new file mode 100644 index 00000000..1a9f1e98 --- /dev/null +++ b/tests/fuzz/seed/xpath/func_start_with @@ -0,0 +1 @@ +starts-with(.,'t') diff --git a/tests/fuzz/seed/xpath/func_string_length b/tests/fuzz/seed/xpath/func_string_length new file mode 100644 index 00000000..26e107fe --- /dev/null +++ b/tests/fuzz/seed/xpath/func_string_length @@ -0,0 +1 @@ +string-length(.) diff --git a/tests/fuzz/seed/xpath/func_string_node b/tests/fuzz/seed/xpath/func_string_node new file mode 100644 index 00000000..e85c11ca --- /dev/null +++ b/tests/fuzz/seed/xpath/func_string_node @@ -0,0 +1 @@ +string(.) diff --git a/tests/fuzz/seed/xpath/func_string_num b/tests/fuzz/seed/xpath/func_string_num new file mode 100644 index 00000000..6c9fe040 --- /dev/null +++ b/tests/fuzz/seed/xpath/func_string_num @@ -0,0 +1 @@ +string(1.0) diff --git a/tests/fuzz/seed/xpath/func_substring b/tests/fuzz/seed/xpath/func_substring new file mode 100644 index 00000000..f069f81e --- /dev/null +++ b/tests/fuzz/seed/xpath/func_substring @@ -0,0 +1 @@ +substring(.,2,3) diff --git a/tests/fuzz/seed/xpath/func_substring_after b/tests/fuzz/seed/xpath/func_substring_after new file mode 100644 index 00000000..1bbecc93 --- /dev/null +++ b/tests/fuzz/seed/xpath/func_substring_after @@ -0,0 +1 @@ +substring-after(.,'e') diff --git a/tests/fuzz/seed/xpath/func_substring_before b/tests/fuzz/seed/xpath/func_substring_before new file mode 100644 index 00000000..fcb14f14 --- /dev/null +++ b/tests/fuzz/seed/xpath/func_substring_before @@ -0,0 +1 @@ +substring-before(.,'e') diff --git a/tests/fuzz/seed/xpath/func_sum b/tests/fuzz/seed/xpath/func_sum new file mode 100644 index 00000000..d110af82 --- /dev/null +++ b/tests/fuzz/seed/xpath/func_sum @@ -0,0 +1 @@ +sum(*) diff --git a/tests/fuzz/seed/xpath/func_translate b/tests/fuzz/seed/xpath/func_translate new file mode 100644 index 00000000..18fe731a --- /dev/null +++ b/tests/fuzz/seed/xpath/func_translate @@ -0,0 +1 @@ +translate(.,'e','a') diff --git a/tests/fuzz/seed/xpath/func_true b/tests/fuzz/seed/xpath/func_true new file mode 100644 index 00000000..c7fa7136 --- /dev/null +++ b/tests/fuzz/seed/xpath/func_true @@ -0,0 +1 @@ +true() diff --git a/tests/fuzz/seed/xpath/math_abs b/tests/fuzz/seed/xpath/math_abs new file mode 100644 index 00000000..a56bfbf9 --- /dev/null +++ b/tests/fuzz/seed/xpath/math_abs @@ -0,0 +1 @@ +math:abs(-1.5) diff --git a/tests/fuzz/seed/xpath/math_acos b/tests/fuzz/seed/xpath/math_acos new file mode 100644 index 00000000..79d181c8 --- /dev/null +++ b/tests/fuzz/seed/xpath/math_acos @@ -0,0 +1 @@ +math:acos(-0.5) diff --git a/tests/fuzz/seed/xpath/math_asin b/tests/fuzz/seed/xpath/math_asin new file mode 100644 index 00000000..919d6eb4 --- /dev/null +++ b/tests/fuzz/seed/xpath/math_asin @@ -0,0 +1 @@ +math:asin(-0.5) diff --git a/tests/fuzz/seed/xpath/math_atan b/tests/fuzz/seed/xpath/math_atan new file mode 100644 index 00000000..40e6610f --- /dev/null +++ b/tests/fuzz/seed/xpath/math_atan @@ -0,0 +1 @@ +math:atan(-0.5) diff --git a/tests/fuzz/seed/xpath/math_atan2 b/tests/fuzz/seed/xpath/math_atan2 new file mode 100644 index 00000000..c02562ba --- /dev/null +++ b/tests/fuzz/seed/xpath/math_atan2 @@ -0,0 +1 @@ +math:atan2(-1.5,-1.5) diff --git a/tests/fuzz/seed/xpath/math_constant b/tests/fuzz/seed/xpath/math_constant new file mode 100644 index 00000000..bf6fe12b --- /dev/null +++ b/tests/fuzz/seed/xpath/math_constant @@ -0,0 +1 @@ +math:constant('E',20) diff --git a/tests/fuzz/seed/xpath/math_cos b/tests/fuzz/seed/xpath/math_cos new file mode 100644 index 00000000..2a95639c --- /dev/null +++ b/tests/fuzz/seed/xpath/math_cos @@ -0,0 +1 @@ +math:cos(-1.5) diff --git a/tests/fuzz/seed/xpath/math_exp b/tests/fuzz/seed/xpath/math_exp new file mode 100644 index 00000000..5ddf4b6a --- /dev/null +++ b/tests/fuzz/seed/xpath/math_exp @@ -0,0 +1 @@ +math:exp(-1.5) diff --git a/tests/fuzz/seed/xpath/math_highest b/tests/fuzz/seed/xpath/math_highest new file mode 100644 index 00000000..7a64ae57 --- /dev/null +++ b/tests/fuzz/seed/xpath/math_highest @@ -0,0 +1 @@ +math:highest(str:split('1.2,-0.5,-2.2e8,-0.1e-5',',')) diff --git a/tests/fuzz/seed/xpath/math_log b/tests/fuzz/seed/xpath/math_log new file mode 100644 index 00000000..260e6bc5 --- /dev/null +++ b/tests/fuzz/seed/xpath/math_log @@ -0,0 +1 @@ +math:log(2.0) diff --git a/tests/fuzz/seed/xpath/math_lowest b/tests/fuzz/seed/xpath/math_lowest new file mode 100644 index 00000000..5590bee8 --- /dev/null +++ b/tests/fuzz/seed/xpath/math_lowest @@ -0,0 +1 @@ +math:lowest(str:split('1.2,-0.5,-2.2e8,-0.1e-5',',')) diff --git a/tests/fuzz/seed/xpath/math_max b/tests/fuzz/seed/xpath/math_max new file mode 100644 index 00000000..47e745b6 --- /dev/null +++ b/tests/fuzz/seed/xpath/math_max @@ -0,0 +1 @@ +math:max(str:split('1.2,-0.5,-2.2e8,-0.1e-5',',')) diff --git a/tests/fuzz/seed/xpath/math_min b/tests/fuzz/seed/xpath/math_min new file mode 100644 index 00000000..3265d2c2 --- /dev/null +++ b/tests/fuzz/seed/xpath/math_min @@ -0,0 +1 @@ +math:min(str:split('1.2,-0.5,-2.2e8,-0.1e-5',',')) diff --git a/tests/fuzz/seed/xpath/math_power b/tests/fuzz/seed/xpath/math_power new file mode 100644 index 00000000..3c7591c7 --- /dev/null +++ b/tests/fuzz/seed/xpath/math_power @@ -0,0 +1 @@ +math:power(2.0,0.5) diff --git a/tests/fuzz/seed/xpath/math_random b/tests/fuzz/seed/xpath/math_random new file mode 100644 index 00000000..9c6cca42 --- /dev/null +++ b/tests/fuzz/seed/xpath/math_random @@ -0,0 +1 @@ +math:random() diff --git a/tests/fuzz/seed/xpath/math_sin b/tests/fuzz/seed/xpath/math_sin new file mode 100644 index 00000000..ba2e6bb4 --- /dev/null +++ b/tests/fuzz/seed/xpath/math_sin @@ -0,0 +1 @@ +math:sin(-1.5) diff --git a/tests/fuzz/seed/xpath/math_sqrt b/tests/fuzz/seed/xpath/math_sqrt new file mode 100644 index 00000000..36f71c43 --- /dev/null +++ b/tests/fuzz/seed/xpath/math_sqrt @@ -0,0 +1 @@ +math:sqrt(2.0) diff --git a/tests/fuzz/seed/xpath/math_tan b/tests/fuzz/seed/xpath/math_tan new file mode 100644 index 00000000..2329ae52 --- /dev/null +++ b/tests/fuzz/seed/xpath/math_tan @@ -0,0 +1 @@ +math:tan(-1.5) diff --git a/tests/fuzz/seed/xpath/saxon_eval b/tests/fuzz/seed/xpath/saxon_eval new file mode 100644 index 00000000..0f520105 --- /dev/null +++ b/tests/fuzz/seed/xpath/saxon_eval @@ -0,0 +1 @@ +saxon:eval(saxon:expression('1+1')) diff --git a/tests/fuzz/seed/xpath/saxon_evaluate b/tests/fuzz/seed/xpath/saxon_evaluate new file mode 100644 index 00000000..b8102265 --- /dev/null +++ b/tests/fuzz/seed/xpath/saxon_evaluate @@ -0,0 +1 @@ +saxon:evaluate('1+1') diff --git a/tests/fuzz/seed/xpath/saxon_line_number b/tests/fuzz/seed/xpath/saxon_line_number new file mode 100644 index 00000000..5052bc2a --- /dev/null +++ b/tests/fuzz/seed/xpath/saxon_line_number @@ -0,0 +1 @@ +saxon:line-number() diff --git a/tests/fuzz/seed/xpath/saxon_systemId b/tests/fuzz/seed/xpath/saxon_systemId new file mode 100644 index 00000000..2c548b51 --- /dev/null +++ b/tests/fuzz/seed/xpath/saxon_systemId @@ -0,0 +1 @@ +saxon:systemId() diff --git a/tests/fuzz/seed/xpath/str_align_center b/tests/fuzz/seed/xpath/str_align_center new file mode 100644 index 00000000..4d906bf3 --- /dev/null +++ b/tests/fuzz/seed/xpath/str_align_center @@ -0,0 +1 @@ +str:align('déjà','--------','center') diff --git a/tests/fuzz/seed/xpath/str_align_left b/tests/fuzz/seed/xpath/str_align_left new file mode 100644 index 00000000..66a41084 --- /dev/null +++ b/tests/fuzz/seed/xpath/str_align_left @@ -0,0 +1 @@ +str:align('déjà','--------','left') diff --git a/tests/fuzz/seed/xpath/str_align_right b/tests/fuzz/seed/xpath/str_align_right new file mode 100644 index 00000000..03f20683 --- /dev/null +++ b/tests/fuzz/seed/xpath/str_align_right @@ -0,0 +1 @@ +str:align('déjà','--------','right') diff --git a/tests/fuzz/seed/xpath/str_concat b/tests/fuzz/seed/xpath/str_concat new file mode 100644 index 00000000..9b0bbce5 --- /dev/null +++ b/tests/fuzz/seed/xpath/str_concat @@ -0,0 +1 @@ +str:concat(str:split('ab,cd,ef',',')) diff --git a/tests/fuzz/seed/xpath/str_decode_uri b/tests/fuzz/seed/xpath/str_decode_uri new file mode 100644 index 00000000..f96b345c --- /dev/null +++ b/tests/fuzz/seed/xpath/str_decode_uri @@ -0,0 +1 @@ +str:decode-uri('%41%00%2d') diff --git a/tests/fuzz/seed/xpath/str_encode_uri_1 b/tests/fuzz/seed/xpath/str_encode_uri_1 new file mode 100644 index 00000000..97dbeae1 --- /dev/null +++ b/tests/fuzz/seed/xpath/str_encode_uri_1 @@ -0,0 +1 @@ +str:encode-uri(';/?:@&=+$,[]',true()) diff --git a/tests/fuzz/seed/xpath/str_encode_uri_2 b/tests/fuzz/seed/xpath/str_encode_uri_2 new file mode 100644 index 00000000..09827ae5 --- /dev/null +++ b/tests/fuzz/seed/xpath/str_encode_uri_2 @@ -0,0 +1 @@ +str:encode-uri('|<>',false()) diff --git a/tests/fuzz/seed/xpath/str_padding b/tests/fuzz/seed/xpath/str_padding new file mode 100644 index 00000000..35736cc7 --- /dev/null +++ b/tests/fuzz/seed/xpath/str_padding @@ -0,0 +1 @@ +str:padding(81,' ') diff --git a/tests/fuzz/seed/xpath/str_replace b/tests/fuzz/seed/xpath/str_replace new file mode 100644 index 00000000..56056ae8 --- /dev/null +++ b/tests/fuzz/seed/xpath/str_replace @@ -0,0 +1 @@ +str:replace('abcdefgh',str:split('a,c,e,g',','),str:split('w,x,y,z',',')) diff --git a/tests/fuzz/seed/xpath/str_split b/tests/fuzz/seed/xpath/str_split new file mode 100644 index 00000000..f67c03c9 --- /dev/null +++ b/tests/fuzz/seed/xpath/str_split @@ -0,0 +1 @@ +str:split('a, sim, lis',', ') diff --git a/tests/fuzz/seed/xpath/str_tokenize b/tests/fuzz/seed/xpath/str_tokenize new file mode 100644 index 00000000..fc74e187 --- /dev/null +++ b/tests/fuzz/seed/xpath/str_tokenize @@ -0,0 +1 @@ +str:tokenize('2016-01-01T12:00:00','-T:') diff --git a/tests/fuzz/seed/xpath/xslt_current b/tests/fuzz/seed/xpath/xslt_current new file mode 100644 index 00000000..79021cfc --- /dev/null +++ b/tests/fuzz/seed/xpath/xslt_current @@ -0,0 +1 @@ +current() diff --git a/tests/fuzz/seed/xpath/xslt_document b/tests/fuzz/seed/xpath/xslt_document new file mode 100644 index 00000000..c6027632 --- /dev/null +++ b/tests/fuzz/seed/xpath/xslt_document @@ -0,0 +1 @@ +document('') diff --git a/tests/fuzz/seed/xpath/xslt_element_available b/tests/fuzz/seed/xpath/xslt_element_available new file mode 100644 index 00000000..737c582c --- /dev/null +++ b/tests/fuzz/seed/xpath/xslt_element_available @@ -0,0 +1 @@ +element-available('exsl:document') diff --git a/tests/fuzz/seed/xpath/xslt_format_number b/tests/fuzz/seed/xpath/xslt_format_number new file mode 100644 index 00000000..f85ece94 --- /dev/null +++ b/tests/fuzz/seed/xpath/xslt_format_number @@ -0,0 +1 @@ +format-number(1.0,'##,##,00.00##') diff --git a/tests/fuzz/seed/xpath/xslt_format_number_neg b/tests/fuzz/seed/xpath/xslt_format_number_neg new file mode 100644 index 00000000..a7be9492 --- /dev/null +++ b/tests/fuzz/seed/xpath/xslt_format_number_neg @@ -0,0 +1 @@ +format-number(1.0,'#.#E0;-0.0%') diff --git a/tests/fuzz/seed/xpath/xslt_function_available b/tests/fuzz/seed/xpath/xslt_function_available new file mode 100644 index 00000000..814530b7 --- /dev/null +++ b/tests/fuzz/seed/xpath/xslt_function_available @@ -0,0 +1 @@ +function-available('exsl:node-set') diff --git a/tests/fuzz/seed/xpath/xslt_generate_id b/tests/fuzz/seed/xpath/xslt_generate_id new file mode 100644 index 00000000..6221ee56 --- /dev/null +++ b/tests/fuzz/seed/xpath/xslt_generate_id @@ -0,0 +1 @@ +generate-id(.) diff --git a/tests/fuzz/seed/xpath/xslt_system_property b/tests/fuzz/seed/xpath/xslt_system_property new file mode 100644 index 00000000..643acb18 --- /dev/null +++ b/tests/fuzz/seed/xpath/xslt_system_property @@ -0,0 +1 @@ +system-property('xsl:version') diff --git a/tests/fuzz/seed/xpath/xslt_unparsed_entity_uri b/tests/fuzz/seed/xpath/xslt_unparsed_entity_uri new file mode 100644 index 00000000..ba409c0c --- /dev/null +++ b/tests/fuzz/seed/xpath/xslt_unparsed_entity_uri @@ -0,0 +1 @@ +unparsed-entity-uri('a') diff --git a/tests/fuzz/seed/xslt/attr_set b/tests/fuzz/seed/xslt/attr_set new file mode 100644 index 00000000..9fc0a202 --- /dev/null +++ b/tests/fuzz/seed/xslt/attr_set @@ -0,0 +1,5 @@ +<x:stylesheet xmlns:x="http://www.w3.org/1999/XSL/Transform" xmlns:a="a" xmlns:b="b" version="1.0"> +<x:attribute-set name="s"><x:attribute name="f">v</x:attribute></x:attribute-set> +<x:attribute-set name="t" use-attribute-set="s"><x:attribute name="g">w</x:attribute></x:attribute-set> +<x:template match="a:*"><x:element name="e" use-attribute-sets="t"/><a:e x:use-attribute-sets="t"/></x:template> +</x:stylesheet> diff --git a/tests/fuzz/seed/xslt/basic b/tests/fuzz/seed/xslt/basic new file mode 100644 index 00000000..dc6f18d6 --- /dev/null +++ b/tests/fuzz/seed/xslt/basic @@ -0,0 +1,3 @@ +<x:stylesheet xmlns:x="http://www.w3.org/1999/XSL/Transform" xmlns:a="a" xmlns:b="b" version="1.0"> +<x:template match="*"></x:template> +</x:stylesheet> diff --git a/tests/fuzz/seed/xslt/element b/tests/fuzz/seed/xslt/element new file mode 100644 index 00000000..7086695e --- /dev/null +++ b/tests/fuzz/seed/xslt/element @@ -0,0 +1,5 @@ +<x:stylesheet xmlns:x="http://www.w3.org/1999/XSL/Transform" xmlns:a="a" xmlns:b="b" version="1.0"> +<x:template match="*"><x:element name="e"><x:apply-templates select="node()|@*"/></x:element></x:template> +<x:template match="@*"><x:attribute name="{local-name()}"></x:attribute></x:template> +<x:template match="text()"><x:text></x:text></x:template> +</x:stylesheet> diff --git a/tests/fuzz/seed/xslt/identity b/tests/fuzz/seed/xslt/identity new file mode 100644 index 00000000..0bab74da --- /dev/null +++ b/tests/fuzz/seed/xslt/identity @@ -0,0 +1,3 @@ +<x:stylesheet xmlns:x="http://www.w3.org/1999/XSL/Transform" xmlns:a="a" xmlns:b="b" version="1.0"> +<x:template match="@*|node()"><x:copy><x:apply-templates select="@*|node()"/></x:copy></x:template> +</x:stylesheet> diff --git a/tests/fuzz/seed/xslt/key b/tests/fuzz/seed/xslt/key new file mode 100644 index 00000000..bda12f75 --- /dev/null +++ b/tests/fuzz/seed/xslt/key @@ -0,0 +1,4 @@ +<x:stylesheet xmlns:x="http://www.w3.org/1999/XSL/Transform" xmlns:a="a" xmlns:b="b" version="1.0"> +<x:key name="k" match="*" use="."/> +<x:template match="a:*"><x:copy-of select="key('k',.)"/></x:template> +</x:stylesheet> diff --git a/tests/fuzz/seed/xslt/lre b/tests/fuzz/seed/xslt/lre new file mode 100644 index 00000000..4930ad52 --- /dev/null +++ b/tests/fuzz/seed/xslt/lre @@ -0,0 +1,3 @@ +<x:stylesheet xmlns:x="http://www.w3.org/1999/XSL/Transform" xmlns:a="a" xmlns:b="b" version="1.0"> +<x:template match="*"><a:e a="{1+1}"><a:f/></a:e></x:template> +</x:stylesheet> diff --git a/tests/fuzz/seed/xslt/mode b/tests/fuzz/seed/xslt/mode new file mode 100644 index 00000000..7d587a91 --- /dev/null +++ b/tests/fuzz/seed/xslt/mode @@ -0,0 +1,4 @@ +<x:stylesheet xmlns:x="http://www.w3.org/1999/XSL/Transform" xmlns:a="a" xmlns:b="b" version="1.0"> +<x:template match="a:*"><x:apply-templates select="*" mode="m"/></x:template> +<x:template match="*" mode="m"><r/></x:template> +</x:stylesheet> diff --git a/tests/fuzz/seed/xslt/param b/tests/fuzz/seed/xslt/param new file mode 100644 index 00000000..f35c92a2 --- /dev/null +++ b/tests/fuzz/seed/xslt/param @@ -0,0 +1,8 @@ +<x:stylesheet xmlns:x="http://www.w3.org/1999/XSL/Transform" xmlns:a="a" xmlns:b="b" version="1.0"> +<x:template match="*"> +<x:call-template name="n"><x:with-param name="p" select="."/></x:call-template> +</x:template> +<x:template name="n"> +<x:param name="p"/><x:value-of select="$p"/> +</x:template> +</x:stylesheet> diff --git a/tests/fuzz/seed/xslt/sort b/tests/fuzz/seed/xslt/sort new file mode 100644 index 00000000..bc7df1ca --- /dev/null +++ b/tests/fuzz/seed/xslt/sort @@ -0,0 +1,6 @@ +<x:stylesheet xmlns:x="http://www.w3.org/1999/XSL/Transform" xmlns:a="a" xmlns:b="b" version="1.0"> +<x:template match="*"> +<x:apply-templates select="*"><x:sort lang="en" select="."/></x:apply-templates> +<a:a/> +</x:template> +</x:stylesheet> diff --git a/tests/fuzz/seed/xslt/value-of b/tests/fuzz/seed/xslt/value-of new file mode 100644 index 00000000..f6013eb3 --- /dev/null +++ b/tests/fuzz/seed/xslt/value-of @@ -0,0 +1,3 @@ +<x:stylesheet xmlns:x="http://www.w3.org/1999/XSL/Transform" xmlns:a="a" xmlns:b="b" version="1.0"> +<x:template match="a:*"><x:value-of select="."/></x:template> +</x:stylesheet> diff --git a/tests/fuzz/seed/xslt/variable b/tests/fuzz/seed/xslt/variable new file mode 100644 index 00000000..5fa2be06 --- /dev/null +++ b/tests/fuzz/seed/xslt/variable @@ -0,0 +1,4 @@ +<x:stylesheet xmlns:x="http://www.w3.org/1999/XSL/Transform" xmlns:a="a" xmlns:b="b" version="1.0"> +<x:variable name="g" select="/*"/> +<x:template match="a:*"><x:variable name="v" select="*"/><x:value-of select="$v"/></x:template> +</x:stylesheet> diff --git a/tests/fuzz/xpath.c b/tests/fuzz/xpath.c new file mode 100644 index 00000000..9bbfe26c --- /dev/null +++ b/tests/fuzz/xpath.c @@ -0,0 +1,218 @@ +/* + * xpath.c: libFuzzer target for XPath expressions + * + * See Copyright for the status of this software. + * + * This fuzz target parses and evaluates XPath expressions in an (E)XSLT + * context using a static XML document. It heavily exercises the libxml2 + * XPath engine (xpath.c), a few other parts of libxml2, and most of + * libexslt. + * + * Some EXSLT functions need the transform context to create RVTs for + * node-sets. A couple of functions also access the stylesheet. The + * XPath context from the transform context is used to parse and + * evaluate expressions. + * + * All these objects are created once at startup. After fuzzing each input, + * they're reset as cheaply as possible. + * + * TODO + * + * - Some expressions can create lots of temporary node sets (RVTs) which + * aren't freed until the whole expression was evaluated, leading to + * extensive memory usage. Cleaning them up earlier would require + * callbacks from the XPath engine, for example after evaluating a + * predicate expression, which doesn't seem feasible. Terminating the + * evaluation after creating a certain number of RVTs is a simple + * workaround. + * - Register a custom xsl:decimal-format declaration for format-number(). + * - Some functions add strings to the stylesheet or transform context + * dictionary, for example via xsltGetQName, requiring a clean up of the + * dicts after fuzzing each input. This behavior seems questionable. + * Extension functions shouldn't needlessly modify the transform context + * or stylesheet. + * - Register xsl:keys and fuzz the key() function. + * - Add a few custom func:functions. + * - Fuzz the document() function with external documents. + */ + +#include <libgen.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <libxml/tree.h> +#include <libxml/parser.h> +#include <libxml/xpath.h> +#include <libxml/xpathInternals.h> +#include <libxslt/extensions.h> +#include <libxslt/functions.h> +#include <libxslt/security.h> +#include <libxslt/transform.h> +#include <libxslt/xsltutils.h> +#include <libexslt/exslt.h> + +static xmlDocPtr doc; +static xsltTransformContextPtr tctxt; +static xmlHashTablePtr saxonExtHash; + +static void +xmlFuzzErrorFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg ATTRIBUTE_UNUSED, + ...) { +} + +int +LLVMFuzzerInitialize(int *argc_p ATTRIBUTE_UNUSED, + char ***argv_p ATTRIBUTE_UNUSED) { + const char *xmlFilename = "xpath.xml"; + const char *dir; + char *argv0; + char *xmlPath; + xsltSecurityPrefsPtr sec; + xsltStylesheetPtr style; + xmlXPathContextPtr xpctxt; + + /* Init libxml2 and libexslt */ + xmlInitParser(); + xmlXPathInit(); + exsltRegisterAll(); + + /* Load XML document */ + argv0 = strdup((*argv_p)[0]); + dir = dirname(argv0); + xmlPath = malloc(strlen(dir) + 1 + strlen(xmlFilename) + 1); + sprintf(xmlPath, "%s/%s", dir, xmlFilename); + doc = xmlReadFile(xmlPath, NULL, 0); + free(xmlPath); + free(argv0); + if (doc == NULL) { + fprintf(stderr, "Error: unable to parse file \"%s\"\n", xmlPath); + return -1; + } + + /* Suppress error messages */ + xmlSetGenericErrorFunc(NULL, xmlFuzzErrorFunc); + xsltSetGenericErrorFunc(NULL, xmlFuzzErrorFunc); + + style = xsltNewStylesheet(); + tctxt = xsltNewTransformContext(style, doc); + + /* Disallow I/O */ + sec = xsltNewSecurityPrefs(); + xsltSetSecurityPrefs(sec, XSLT_SECPREF_READ_FILE, xsltSecurityForbid); + xsltSetSecurityPrefs(sec, XSLT_SECPREF_WRITE_FILE, xsltSecurityForbid); + xsltSetSecurityPrefs(sec, XSLT_SECPREF_CREATE_DIRECTORY, xsltSecurityForbid); + xsltSetSecurityPrefs(sec, XSLT_SECPREF_READ_NETWORK, xsltSecurityForbid); + xsltSetSecurityPrefs(sec, XSLT_SECPREF_WRITE_NETWORK, xsltSecurityForbid); + xsltSetCtxtSecurityPrefs(sec, tctxt); + + /* + * Some extension functions need the current instruction. + * + * - format-number() for namespaces. + * - document() for the base URL. + * - maybe others? + * + * For fuzzing, it's enough to use the source document's root element. + */ + tctxt->inst = xmlDocGetRootElement(doc); + + saxonExtHash = (xmlHashTablePtr) + xsltStyleGetExtData(style, SAXON_NAMESPACE); + + /* Set up XPath context */ + xpctxt = tctxt->xpathCtxt; + + /* Resource limits to avoid timeouts and call stack overflows */ + xpctxt->maxParserDepth = 15; + xpctxt->maxDepth = 100; + xpctxt->opLimit = 500000; + + /* Test namespaces used in xpath.xml */ + xmlXPathRegisterNs(xpctxt, BAD_CAST "a", BAD_CAST "a"); + xmlXPathRegisterNs(xpctxt, BAD_CAST "b", BAD_CAST "b"); + xmlXPathRegisterNs(xpctxt, BAD_CAST "c", BAD_CAST "c"); + + /* EXSLT namespaces */ + xmlXPathRegisterNs(xpctxt, BAD_CAST "crypto", EXSLT_CRYPTO_NAMESPACE); + xmlXPathRegisterNs(xpctxt, BAD_CAST "date", EXSLT_DATE_NAMESPACE); + xmlXPathRegisterNs(xpctxt, BAD_CAST "dyn", EXSLT_DYNAMIC_NAMESPACE); + xmlXPathRegisterNs(xpctxt, BAD_CAST "exsl", EXSLT_COMMON_NAMESPACE); + xmlXPathRegisterNs(xpctxt, BAD_CAST "math", EXSLT_MATH_NAMESPACE); + xmlXPathRegisterNs(xpctxt, BAD_CAST "saxon", SAXON_NAMESPACE); + xmlXPathRegisterNs(xpctxt, BAD_CAST "set", EXSLT_SETS_NAMESPACE); + xmlXPathRegisterNs(xpctxt, BAD_CAST "str", EXSLT_STRINGS_NAMESPACE); + + /* Register variables */ + xmlXPathRegisterVariable(xpctxt, BAD_CAST "f", xmlXPathNewFloat(-1.5)); + xmlXPathRegisterVariable(xpctxt, BAD_CAST "b", xmlXPathNewBoolean(1)); + xmlXPathRegisterVariable(xpctxt, BAD_CAST "s", + xmlXPathNewString(BAD_CAST "var")); + xmlXPathRegisterVariable( + xpctxt, BAD_CAST "n", + xmlXPathEval(BAD_CAST "//node() | /*/*/namespace::*", xpctxt)); + + return 0; +} + +int +LLVMFuzzerTestOneInput(const char *data, size_t size) { + xmlXPathContextPtr xpctxt = tctxt->xpathCtxt; + xmlChar *xpathExpr; + + /* Null-terminate */ + xpathExpr = malloc(size + 1); + memcpy(xpathExpr, data, size); + xpathExpr[size] = 0; + + /* + * format-number() can still cause memory errors with invalid UTF-8 in + * prefixes or suffixes. This shouldn't be exploitable in practice, but + * should be fixed. Check UTF-8 validity for now. + */ + if (xmlCheckUTF8(xpathExpr) == 0) { + free(xpathExpr); + return 0; + } + + /* Compile and return early if the expression is invalid */ + xmlXPathCompExprPtr compExpr = xmlXPathCtxtCompile(xpctxt, xpathExpr); + free(xpathExpr); + if (compExpr == NULL) + return 0; + + /* Initialize XPath evaluation context and evaluate */ + xpctxt->node = (xmlNodePtr) doc; /* Maybe test different context nodes? */ + xpctxt->contextSize = 1; + xpctxt->proximityPosition = 1; + xpctxt->opCount = 0; + xmlXPathObjectPtr xpathObj = xmlXPathCompiledEval(compExpr, xpctxt); + xmlXPathFreeObject(xpathObj); + xmlXPathFreeCompExpr(compExpr); + + /* Some XSLT extension functions create RVTs. */ + xsltFreeRVTs(tctxt); + + /* Clean object cache */ + xmlXPathContextSetCache(xpctxt, 0, 0, 0); + xmlXPathContextSetCache(xpctxt, 1, -1, 0); + + /* Clean dictionaries */ + if (xmlDictSize(tctxt->dict) > 0) { + xmlDictFree(tctxt->dict); + xmlDictFree(tctxt->style->dict); + tctxt->style->dict = xmlDictCreate(); + tctxt->dict = xmlDictCreateSub(tctxt->style->dict); + } + + /* Clean saxon:expression cache */ + if (xmlHashSize(saxonExtHash) > 0) { + /* There doesn't seem to be a cheaper way with the public API. */ + xsltShutdownCtxtExts(tctxt); + xsltInitCtxtExts(tctxt); + saxonExtHash = (xmlHashTablePtr) + xsltStyleGetExtData(tctxt->style, SAXON_NAMESPACE); + } + + return 0; +} diff --git a/tests/fuzz/xpath.dict b/tests/fuzz/xpath.dict new file mode 100644 index 00000000..a57026f1 --- /dev/null +++ b/tests/fuzz/xpath.dict @@ -0,0 +1,63 @@ +# XPath + +axis_ancestor="ancestor::" +axis_ancestor_or_self="ancestor-or-self::" +axis_attribute="attribute::" +axis_attribute_abbrev="@" +axis_child="child::" +axis_descendant="descendant::" +axis_descendant_or_self="descendant-or-self::" +axis_following="following::" +axis_following_sibling="following-sibling::" +axis_namespace="namespace::" +axis_parent="parent::" +axis_preceding="preceding::" +axis_preceding_siblings="preceding-sibling::" +axis_self="self::" + +node_test_ns="a:" + +val_num="=(1.0)" +val_str_sq="=('a')" +val_str_dq="=(\"a\")" +val_node_set="=(*)" +val_elem="=(b)" + +step_root="/" +step_descendant="//" +step_any="//*" +step_any_l="*//" +step_elem="//b" +step_ns_elem="//a:a" +step_comment="//comment()" +step_node="//node()" +step_node_l="node()//" +step_pi="//processing-instruction()" +step_text="//text()" +step_parent="../" + +op_plus="+1" +op_minus=" - 1" +op_neg="-" +op_mul="*1" +op_div=" div 1" +op_mod=" mod 1" +op_and=" and 1" +op_or=" or 1" +op_ne="!=1" +op_lt="<1" +op_gt=">1" +op_le="<=1" +op_ge=">=1" +op_predicate_num="[1]" +op_predicate_str="['a']" +op_predicate="[1=1]" +op_arg_num=",1" +op_arg_str=",'a'" +op_arg_node=",*" +op_union="|//b" + +var_num="=$f" +var_bool="=$b" +var_str="=$s" +var_node_set="=$n" diff --git a/tests/fuzz/xpath.xml b/tests/fuzz/xpath.xml new file mode 100644 index 00000000..0ab51932 --- /dev/null +++ b/tests/fuzz/xpath.xml @@ -0,0 +1,19 @@ +<?pi content?> +<a xmlns:a="a"> + <b xmlns:b="b" a="1" id="b"> + <c b="2">Ärger</c> + <b:d b="3">text</b:d> + <!-- comment --> + <a:b b="4">ß😀</a:b> + <b:c a="4"><![CDATA[text]]></b:c> + </b> + <?pi content?> + <a:e xmlns:c="c" a="αβγ"> + <c:d b="2"/> + <a:c>99</a:c> + <e a="2">content</e> + </a:e> + <b/> + <a:a/> + <!-- comment --> +</a> diff --git a/tests/fuzz/xslt.c b/tests/fuzz/xslt.c new file mode 100644 index 00000000..db3d45ca --- /dev/null +++ b/tests/fuzz/xslt.c @@ -0,0 +1,126 @@ +/* + * xslt.c: libFuzzer target for XSLT stylesheets + * + * See Copyright for the status of this software. + * + * This is a rather naive fuzz target using a static XML document. + * + * TODO + * + * - Improve seed corpus + * - Mutate multiple input documents: source, xsl:import, xsl:include + * - format-number() with xsl:decimal-format + * - Better coverage for xsl:key and key() function + * - EXSLT func:function + * - xsl:document + */ + +#include <libgen.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <libxml/tree.h> +#include <libxml/parser.h> +#include <libxslt/security.h> +#include <libxslt/transform.h> +#include <libxslt/xslt.h> +#include <libxslt/xsltInternals.h> +#include <libxslt/xsltutils.h> +#include <libexslt/exslt.h> + +static xmlDocPtr doc; +static xsltSecurityPrefsPtr sec; + +static void +errorFunc(void *ctx, const char *msg, ...) { + /* Discard error messages. */ +} + +int +LLVMFuzzerInitialize(int *argc_p ATTRIBUTE_UNUSED, + char ***argv_p ATTRIBUTE_UNUSED) { + const char *xmlFilename = "xslt.xml"; + const char *dir; + char *argv0; + char *xmlPath; + + /* Init libraries */ + xmlInitParser(); + xmlXPathInit(); + xsltInit(); + exsltRegisterAll(); + + /* Load XML document */ + argv0 = strdup((*argv_p)[0]); + dir = dirname(argv0); + xmlPath = malloc(strlen(dir) + 1 + strlen(xmlFilename) + 1); + sprintf(xmlPath, "%s/%s", dir, xmlFilename); + doc = xmlReadFile(xmlPath, NULL, 0); + free(xmlPath); + free(argv0); + if (doc == NULL) { + fprintf(stderr, "Error: unable to parse file \"%s\"\n", xmlPath); + return -1; + } + + /* Suppress error messages */ + xmlSetGenericErrorFunc(NULL, errorFunc); + xsltSetGenericErrorFunc(NULL, errorFunc); + + /* Disallow I/O */ + sec = xsltNewSecurityPrefs(); + xsltSetSecurityPrefs(sec, XSLT_SECPREF_READ_FILE, xsltSecurityForbid); + xsltSetSecurityPrefs(sec, XSLT_SECPREF_WRITE_FILE, xsltSecurityForbid); + xsltSetSecurityPrefs(sec, XSLT_SECPREF_CREATE_DIRECTORY, xsltSecurityForbid); + xsltSetSecurityPrefs(sec, XSLT_SECPREF_READ_NETWORK, xsltSecurityForbid); + xsltSetSecurityPrefs(sec, XSLT_SECPREF_WRITE_NETWORK, xsltSecurityForbid); + + return 0; +} + +int +LLVMFuzzerTestOneInput(const char *data, size_t size) { + xmlDocPtr xsltDoc; + xmlDocPtr result; + xmlNodePtr xsltRoot; + xsltStylesheetPtr sheet; + xsltTransformContextPtr ctxt; + + xsltDoc = xmlReadMemory(data, size, NULL, NULL, 0); + if (xsltDoc == NULL) + return 0; + xsltRoot = xmlDocGetRootElement(xsltDoc); + xmlNewNs(xsltRoot, EXSLT_COMMON_NAMESPACE, BAD_CAST "exsl"); + xmlNewNs(xsltRoot, EXSLT_COMMON_NAMESPACE, BAD_CAST "exslt"); + xmlNewNs(xsltRoot, EXSLT_CRYPTO_NAMESPACE, BAD_CAST "crypto"); + xmlNewNs(xsltRoot, EXSLT_DATE_NAMESPACE, BAD_CAST "date"); + xmlNewNs(xsltRoot, EXSLT_DYNAMIC_NAMESPACE, BAD_CAST "dyn"); + xmlNewNs(xsltRoot, EXSLT_MATH_NAMESPACE, BAD_CAST "math"); + xmlNewNs(xsltRoot, EXSLT_SETS_NAMESPACE, BAD_CAST "set"); + xmlNewNs(xsltRoot, EXSLT_STRINGS_NAMESPACE, BAD_CAST "str"); + xmlNewNs(xsltRoot, SAXON_NAMESPACE, BAD_CAST "saxon"); + + sheet = xsltParseStylesheetDoc(xsltDoc); + if (sheet == NULL) { + xmlFreeDoc(xsltDoc); + return 0; + } + + ctxt = xsltNewTransformContext(sheet, doc); + xsltSetCtxtSecurityPrefs(sec, ctxt); + ctxt->maxTemplateDepth = 100; + ctxt->xpathCtxt->maxParserDepth = 15; + ctxt->xpathCtxt->maxDepth = 100; + ctxt->xpathCtxt->opCount = 0; + ctxt->xpathCtxt->opLimit = 100000; + + result = xsltApplyStylesheetUser(sheet, doc, NULL, NULL, NULL, ctxt); + + xmlFreeDoc(result); + xsltFreeTransformContext(ctxt); + xsltFreeStylesheet(sheet); + + return 0; +} + diff --git a/tests/fuzz/xslt.dict b/tests/fuzz/xslt.dict new file mode 100644 index 00000000..69b9c257 --- /dev/null +++ b/tests/fuzz/xslt.dict @@ -0,0 +1,86 @@ +# Instructions + +inst_attribute="<x:attribute name=\"n\"></x:attribute>" +inst_apply_imports="<x:apply-imports/>" +inst_apply_templates="<x:apply-templates select=\"*\"/>" +inst_call_template="<x:call-template name=\"n\"/>" +inst_choose="<x:choose><x:when test=\"*\"></x:when><x:otherwise></x:otherwise></x:choose>" +inst_comment="<x:comment>c</x:comment>" +inst_copy="<x:copy></x:copy>" +inst_copy_of="<x:copy-of select=\"*\"/>" +inst_element="<x:element name=\"n\"></x:element>" +inst_for_each="<x:for-each select=\"*\"></x:for-each>" +inst_if="<x:if test=\"*\"></x:if>" +inst_processing_instruction="<x:processing-instruction name=\"pi\">c</x:processing-instruction>" +inst_template_element="<x:template match=\"*\"></x:template>" +inst_template_name="<x:template name=\"n\"></x:template>" +inst_text="<x:text>t</x:text>" +inst_text_noesc="<x:text disable-output-escaping=\"yes\"><>&</x:text>" +inst_var_select="<x:variable name=\"v\" select=\".\"/>" +inst_var_templ="<x:variable name=\"v\"></x:variable>" +inst_value_of="<x:value-of select=\"*\"/>" + +# Move to corpus? +inst_fallback="<x:foo><x:fallback>f</x:fallback></x:foo>" +inst_message_no="<x:message terminate=\"no\">m</x:message>" +inst_message_yes="<x:message terminate=\"yes\">m</x:message>" +inst_namespace_alias="<x:namespace-alias stylesheet-prefix=\"a\" result-prefix=\"x\"/>" +inst_number_value="<x:number value=\"1\" format=\"1\" grouping-separator=\",\" grouping-size=\"3\"/>" +inst_number_any="<x:number level=\"any\" count=\"*\" from=\"*\" format=\"1\" grouping-separator=\",\" grouping-size=\"3\"/>" +inst_number_multiple="<x:number level=\"multiple\" count=\"*\" from=\"*\" format=\"1\" grouping-separator=\",\" grouping-size=\"3\"/>" +inst_number_single="<x:number level=\"single\" count=\"*\" from=\"*\" format=\"1\" grouping-separator=\",\" grouping-size=\"3\"/>" +inst_output_xml="<x:output method=\"xml\" version=\"1.0\" encoding=\"iso-8859-1\" omit-xml-declaration=\"yes\" standalone=\"yes\" doctype-public=\"p\" doctype-system=\"s\" cdata-section-elements=\"a\" indent=\"yes\" media-type=\"t\"/>" +inst_output_html="<x:output method=\"html\" version=\"4.0\" encoding=\"iso-8859-1\" doctype-public=\"p\" doctype-system=\"s\" indent=\"yes\" media-type=\"t\"/>" +inst_output_text="<x:output method=\"text\" encoding=\"iso-8859-1\" media-type=\"t\"/>" +inst_space_preserve="<x:preserve-space elements=\"b:b\"/>" +inst_space_strip="<x:strip-space elements=\"a:e *\"/>" + +# Attributes + +attr_mode=" mode=\"m\"" +attr_namespace=" namespace=\"a\"" + +# XPath + +axis_attribute_abbrev="@" +axis_namespace="namespace::" + +node_test_any="|//*" +node_test_name="|//a" +node_test_qname="|//a:a" +node_test_ns_any="|//a:*" +node_test_comment="|//comment()" +node_test_node="|//node()" +node_test_pi="|//processing-instruction()" +node_test_text="|//text()" +node_test_attr_a="|@a" +node_test_attr_b="|@b" +node_test_parent="|.." + +step="/" +step_a="/a" +step_b="/b" +step_any="/*" +step_node="/node()" +step_text="/text()" +step_comment="/comment()" +step_pi="/processing-instruction()" +step_self="/." +step_parent="/.." +step_namespace="/namespace::*" + +op_and=" and 1" +op_or=" or 0" +op_eq="=*" +op_ne="!=*" +op_gt=">*" +op_plus="+1" +op_mod=" mod 2" + +pred_num="[1]" +pred_string="['a']" +pred_position="[position()]" +pred_last="[last()]" +pred_current="[current()]" + +expr_var="+$v" diff --git a/tests/fuzz/xslt.xml b/tests/fuzz/xslt.xml new file mode 100644 index 00000000..0ab51932 --- /dev/null +++ b/tests/fuzz/xslt.xml @@ -0,0 +1,19 @@ +<?pi content?> +<a xmlns:a="a"> + <b xmlns:b="b" a="1" id="b"> + <c b="2">Ärger</c> + <b:d b="3">text</b:d> + <!-- comment --> + <a:b b="4">ß😀</a:b> + <b:c a="4"><![CDATA[text]]></b:c> + </b> + <?pi content?> + <a:e xmlns:c="c" a="αβγ"> + <c:d b="2"/> + <a:c>99</a:c> + <e a="2">content</e> + </a:e> + <b/> + <a:a/> + <!-- comment --> +</a> |