Add fuzzing support for some libpurple features
Testing Done:
Hi!
I built and tested all of these fuzzers for libpurple.
You can build them by first building pidgin/libpurple with `--enable-fuzzing` then going into `libpurple/tests` and run `make check`. After that you can run these fuzzers. With a dictionary if you want :)
for example:
```bash
$ ./fuzz_markup_strip_html -dict=dictionaries/html.dict
Dictionary: 465 entries
INFO: Seed: 2274862685
INFO: Loaded 1 modules (3 inline 8-bit counters): 3 [0x5a4ec0, 0x5a4ec3),
INFO: Loaded 1 PC tables (3 PCs): 3 [0x568ee8,0x568f18),
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2 INITED cov: 2 ft: 2 corp: 1/1b exec/s: 0 rss: 30Mb
#1048576 pulse cov: 2 ft: 2 corp: 1/1b lim: 4096 exec/s: 524288 rss: 789Mb
#2097152 pulse cov: 2 ft: 2 corp: 1/1b lim: 4096 exec/s: 524288 rss: 792Mb
```
Best Regards,
Jordy Zomer
Reviewed at https://reviews.imfreedom.org/r/760/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/FUZZING Thu Jun 24 21:44:39 2021 -0500
@@ -0,0 +1,108 @@
+# Introduction and setup +Pidgin has fuzzing support for libpurple. Libfuzzer (https://llvm.org/docs/LibFuzzer.html) is used for this. +There are currently a few fuzzers mentioned in libpurple/tests/fuzz *.c. You can build the fuzzers by following the usual build process and adding `--enable-fuzzing` to `./configure`, for this you'll need to set CC to `clang`, once you've done this you can go to `libpurple/tests` and run `make check` this will build the fuzzers for you. +$ CC=clang ./configure --enable-fuzzing --disable-cyrus-sasl --disable-gtkui --disable-gstreamer --disable-vv --disable-idn --disable-meanwhile --disable-avahi --disable-libgadu --disable-dbus --disable-libsecret --disable-gnome-keyring --disable-kwallet --disable-plugin +# This will configure build system +# The next step would be actually building pidgin and it's libraries. +# -j $(nproc) is optional, this build it with all available cores +# Now pidgin is actually built, we can build the fuzzers +# Now the fuzzers should be built and can be run +# The -dict= paramater can be used to define a dictionary to be used by fuzzing +# For fuzzing common formats like xml you could for example use the xml dict, this is optional +$ ./fuzz_xmlnode -dict=dictionaries/xml.dict +Because Libfuzzer is a sophisticated program, here are some handy options: +jobs -> Number of jobs to run. If jobs >= 1 we spawn this number of jobs in separate worker processes with stdout/stderr redirected to fuzz-JOB.log. +workers -> Number of simultaneous worker processes to run the jobs. If zero, "min(jobs,NumberOfCpuCores()/2)" is used. +max_len -> Maximum length of the test input. If 0, libFuzzer tries to guess a good value based on the corpus and reports it. +You can also show the help with: +`./fuzz_html_to_xhtml -help=1` +This will show you all the options you can give to your fuzzer. +In addition, if you're new to fuzzing with libfuzzer, https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md is a fantastic place to start. +Of course, having more fuzzers and covering more areas of the code used in libpurple is always a good thing. It's simple to incorporate a fuzzer into the current build system! +If you open the `Makefile.am` file in `libpurple/tests` you'll see a `fuzz_programs` variable, you have to add the name to your new fuzzing harness in there. + fuzz_markup_strip_html \ + fuzz_newfuzzer # This is the newly added fuzzer +We'll also need to define the sources, which we can do by copying and changing the lines from an existing fuzzer. +For example we have a `fuzz_xmlnode.c` fuzzer, these are the lines that define the sources and the flags: +fuzz_xmlnode_SOURCES=fuzz_xmlnode.c +fuzz_xmlnode_LDADD=$(check_libpurple_LDADD) +fuzz_xmlnode_CFLAGS=-fsanitize=fuzzer,address $(check_libpurple_CFLAGS) +We'll need to change the names of these to match the name of our new fuzzer and add any necessary flags: +fuzz_new_SOURCES=fuzz_new.c +fuzz_new_LDADD=$(check_libpurple_LDADD) +fuzz_new_CFLAGS=-fsanitize=fuzzer,address $(check_libpurple_CFLAGS) +Now you must include your harness in `fuzz_new.c`, an example of a new harness could be as follows: +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + char *malicious_input = g_new0(char, size + 1); + memcpy(malicious_input, data, size); + malicious_input[size] = '\0'; + function_you_want_to_fuzz(malicious_input); + g_free(malicious_input); +Make sure to include the relevant includes, and then run `./configure` again in the repository's root directory, after that run `make check` in `libpurple/tests` to create your new fuzzer. Then, by executing this binary, you can run it. --- a/configure.ac Tue Jun 22 19:50:58 2021 -0500
+++ b/configure.ac Thu Jun 24 21:44:39 2021 -0500
@@ -2532,6 +2532,18 @@
AM_CONDITIONAL(HAVE_DOXYGEN, test "x$enable_doxygen" = "xyes")
AM_CONDITIONAL(HAVE_XSLTPROC, test "x$enable_devhelp" = "xyes")
+AC_ARG_ENABLE([fuzzing], [--enable-fuzzing Turn on fuzzing], + [case "${enableval}" in yes) fuzzing=true ;; no) fuzzing=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-fuzzing]) ;; esac],[fuzzing=false]) +AM_CONDITIONAL([FUZZ], [test x$fuzzing = xtrue]) +if test "x$enable_fuzzing" = "xyes" ; then + if ! test "x$CC" = "xclang" ; then + AC_MSG_ERROR(["You need to set CC=clang to use --enable-fuzzing, used $CC"]) AC_ARG_ENABLE(debug, [AC_HELP_STRING([--enable-debug],
[compile with debugging support])], , enable_debug=no)
--- a/libpurple/tests/Makefile.am Tue Jun 22 19:50:58 2021 -0500
+++ b/libpurple/tests/Makefile.am Thu Jun 24 21:44:39 2021 -0500
@@ -4,8 +4,21 @@
check_PROGRAMS=check_libpurple
+ fuzz_markup_strip_html \ +check_PROGRAMS+=$(fuzz_programs) check_libpurple_SOURCES=\
@@ -33,4 +46,31 @@
+fuzz_xmlnode_SOURCES=fuzz_xmlnode.c +fuzz_xmlnode_LDADD=$(check_libpurple_LDADD) +fuzz_xmlnode_CFLAGS=-fsanitize=fuzzer,address $(check_libpurple_CFLAGS) +fuzz_jabber_id_new_SOURCES=fuzz_jabber_id_new.c +fuzz_jabber_id_new_LDADD=$(check_libpurple_LDADD) +fuzz_jabber_id_new_CFLAGS=-fsanitize=fuzzer,address $(check_libpurple_CFLAGS) +fuzz_jabber_caps_SOURCES=fuzz_jabber_caps.c +fuzz_jabber_caps_LDADD=$(check_libpurple_LDADD) +fuzz_jabber_caps_CFLAGS=-fsanitize=fuzzer,address $(check_libpurple_CFLAGS) +fuzz_mime_SOURCES=fuzz_mime.c +fuzz_mime_LDADD=$(check_libpurple_LDADD) +fuzz_mime_CFLAGS=-fsanitize=fuzzer,address $(check_libpurple_CFLAGS) +fuzz_html_to_xhtml_SOURCES=fuzz_html_to_xhtml.c +fuzz_html_to_xhtml_LDADD=$(check_libpurple_LDADD) +fuzz_html_to_xhtml_CFLAGS=-fsanitize=fuzzer,address $(check_libpurple_CFLAGS) +fuzz_markup_strip_html_SOURCES=fuzz_markup_strip_html.c +fuzz_markup_strip_html_LDADD=$(check_libpurple_LDADD) +fuzz_markup_strip_html_CFLAGS=-fsanitize=fuzzer,address $(check_libpurple_CFLAGS) --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/tests/dictionaries/html.dict Thu Jun 24 21:44:39 2021 -0500
@@ -0,0 +1,478 @@
+# AFL dictionary for HTML parsers +# ------------------------------- +# A basic collection of HTML string likely to matter to HTML parsers. +# Created by Michal Zalewski <lcamtuf@google.com> +tag_annotation_xml="<annotation-xml>" +tag_basefont="<basefont>" +tag_blockquote="<blockquote>" +tag_colgroup="<colgroup>" +tag_datalist="<datalist>" +tag_fieldset="<fieldset>" +tag_figcaption="<figcaption>" +tag_foreignobject="<foreignobject>" +tag_frameset="<frameset>" +tag_malignmark="<malignmark>" +tag_menuitem="<menuitem>" +tag_multicol="<multicol>" +tag_noframes="<noframes>" +tag_noscript="<noscript>" +tag_optgroup="<optgroup>" +tag_plaintext="<plaintext>" +tag_progress="<progress>" +tag_template="<template>" +tag_textarea="<textarea>" +"oncompassneedscalibration" +"onmozpointerlockchange" +"onwebkitanimationiteration" +"onwebkitanimationstart" +"onwebkitmouseforcechanged" +"onwebkitmouseforcedown" +"onwebkitmouseforcewillbegin" +"onwebkitwillrevealbottom" --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/tests/dictionaries/xml.dict Thu Jun 24 21:44:39 2021 -0500
@@ -0,0 +1,82 @@
+# AFL dictionary for XML +# ---------------------- +# Several basic syntax elements and attributes, modeled on libxml2. +# Created by Michal Zalewski <lcamtuf@google.com> +attr_encoding=" encoding=\"1\"" +attr_standalone=" standalone=\"no\"" +attr_version=" version=\"1\"" +attr_xml_base=" xml:base=\"1\"" +attr_xml_id=" xml:id=\"1\"" +attr_xml_lang=" xml:lang=\"1\"" +attr_xml_space=" xml:space=\"1\"" +attr_xmlns=" xmlns=\"1\"" +string_col_fallback=":fallback" +string_col_include=":include" +string_empty_dblquotes="\"\"" +string_empty_quotes="''" +string_entities="ENTITIES" +string_implied="#IMPLIED" +string_nmtoken="NMTOKEN" +string_nmtokens="NMTOKENS" +string_notation="NOTATION" +string_required="#REQUIRED" +tag_include="<![INCLUDE[" +tag_notation="<!NOTATION" +tag_open_exclamation="<!" +encoding_iso1="ISO-8859" +encoding_iso3="ISO-10646-UCS" +encoding_iso5="ISO-LATIN-1" +encoding_jis="SHIFT_JIS" +encoding_utf16le="UTF-16BE" +encoding_utf16le="UTF-16LE" +encoding_ascii="US-ASCII" --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/tests/fuzz_html_to_xhtml.c Thu Jun 24 21:44:39 2021 -0500
@@ -0,0 +1,49 @@
+ * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + char *malicious_html = g_new0(char, size + 1); + memcpy(malicious_html, data, size); + malicious_html[size] = '\0'; + gchar *xhtml = NULL, *plaintext = NULL; + purple_markup_html_to_xhtml(malicious_html, &xhtml, &plaintext); + g_free(malicious_html); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/tests/fuzz_jabber_caps.c Thu Jun 24 21:44:39 2021 -0500
@@ -0,0 +1,59 @@
+ * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA +#include "../protocols/jabber/caps.h" +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + char *malicious_xml = g_new0(char, size + 1); + memcpy(malicious_xml, data, size); + malicious_xml[size] = '\0'; + if (*malicious_xml == '\0') { + query = xmlnode_new(malicious_xml); + jabber_caps_parse_client_info(query); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/tests/fuzz_jabber_id_new.c Thu Jun 24 21:44:39 2021 -0500
@@ -0,0 +1,48 @@
+ * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA +#include "../conversation.h" +#include "../protocols/jabber/jutil.h" +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + char *malicious_jid = g_new0(char, size + 1); + memcpy(malicious_jid, data, size); + malicious_jid[size] = '\0'; + JabberID *jid = jabber_id_new(malicious_jid); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/tests/fuzz_markup_strip_html.c Thu Jun 24 21:44:39 2021 -0500
@@ -0,0 +1,47 @@
+ * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + char *malicious_html = g_new0(char, size + 1); + memcpy(malicious_html, data, size); + malicious_html[size] = '\0'; + stripped = purple_markup_strip_html(malicious_html); + g_free(malicious_html); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/tests/fuzz_mime.c Thu Jun 24 21:44:39 2021 -0500
@@ -0,0 +1,46 @@
+ * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + char *malicious_mime = g_new0(char, size + 1); + memcpy(malicious_mime, data, size); + malicious_mime[size] = '\0'; + gchar *result = purple_mime_decode_field(malicious_mime); + g_free(malicious_mime); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/tests/fuzz_xmlnode.c Thu Jun 24 21:44:39 2021 -0500
@@ -0,0 +1,69 @@
+ * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + char *malicious_xml = g_new0(char, size + 1); + memcpy(malicious_xml, data, size); + malicious_xml[size] = '\0'; + xml = xmlnode_from_str(malicious_xml, -1); + str = xmlnode_to_str(xml, NULL); + if (strcmp(malicious_xml, str) != 0) {