diff --git a/.gitignore b/.gitignore
index d974d4d9..0c7d9b55 100644
--- a/.gitignore
+++ b/.gitignore
@@ -85,6 +85,10 @@ libespeak-ng-test.a
libespeak-ng-test.la
libespeak-ng-test.so*
+tests/libfuzzrunner.a
+tests/libfuzzrunner.la
+tests/libfuzzrunner.so*
+
# build output
*.html
diff --git a/Makefile.am b/Makefile.am
index 3946f322..4760f731 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -66,6 +66,7 @@ install-exec-hook:
clean-local:
cd src && rm -f *.o *~ && cd ..
cd src/libespeak-ng/ && rm -f *.o *.lo && cd ../..
+ cd tests && rm -f *.o *.lo && cd ..
if HAVE_GRADLE
cd android && $(GRADLE) clean
endif
@@ -252,10 +253,33 @@ check: tests/encoding.check \
tests/readclause.check \
tests/translate.check \
tests/ssml.check \
+ tests/ssml-fuzzer.check \
tests/api.check \
tests/language-phonemes.check \
tests/language-pronunciation.check
+
+##### fuzzer:
+
+# When OSS-Fuzz [https://github.com/google/oss-fuzz] runs our fuzzer, it sets
+# LIB_FUZZING_ENGINE to libFuzzer [http://llvm.org/docs/LibFuzzer.html].
+# When running outside OSS-Fuzz, we link against our own stub fuzzrunner.
+LIB_FUZZING_ENGINE ?= tests/libfuzzrunner.la
+
+lib_LTLIBRARIES += tests/libfuzzrunner.la
+tests_libfuzzrunner_la_CFLAGS = -Isrc/libespeak-ng ${AM_CFLAGS}
+tests_libfuzzrunner_la_SOURCES = tests/fuzzrunner.c
+
+noinst_bin_PROGRAMS += tests/ssml-fuzzer.test
+tests_ssml_fuzzer_test_LDADD = src/libespeak-ng.la ${LIB_FUZZING_ENGINE}
+tests_ssml_fuzzer_test_SOURCES = tests/ssml-fuzzer.c
+tests_ssml_fuzzer_test_DEPENDENCIES = src/libespeak-ng.la tests/libfuzzrunner.la
+
+tests/ssml-fuzzer.check: tests/ssml-fuzzer.test
+ @echo " TEST $<"
+ @ESPEAK_DATA_PATH=$(PWD) $< tests/ssml/*.ssml tests/ssml-fuzzer/*.ssml && echo " PASSED $<"
+
+
##### phoneme data:
espeak-ng-data/phondata: phsource/phonemes.stamp
diff --git a/tests/fuzzrunner.c b/tests/fuzzrunner.c
new file mode 100644
index 00000000..680d2cb2
--- /dev/null
+++ b/tests/fuzzrunner.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 Sascha Brawer
+ *
+ * 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 3 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 see:
+ * .
+ */
+
+#include "config.h"
+#include "speech.h"
+
+#include
+#include
+#include
+#include
+
+#include
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+int main(int argc, char **argv) {
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ size_t filesize = GetFileLength(argv[i]);
+ FILE *stream = fopen(argv[i], "r");
+ unsigned char *text = NULL;
+ if (stream == NULL) {
+ perror(argv[i]);
+ exit(EXIT_FAILURE);
+ }
+
+ text = (unsigned char *) malloc(filesize + 1);
+ if (text == NULL) {
+ espeak_ng_PrintStatusCodeMessage(ENOMEM, stderr, NULL);
+ exit(EXIT_FAILURE);
+ }
+
+ fread(text, 1, filesize, stream);
+ text[filesize] = 0;
+ fclose(stream);
+ LLVMFuzzerTestOneInput(text, filesize);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/ssml-fuzzer.c b/tests/ssml-fuzzer.c
new file mode 100644
index 00000000..d5bea200
--- /dev/null
+++ b/tests/ssml-fuzzer.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 Sascha Brawer
+ *
+ * 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 3 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 see:
+ * .
+ */
+
+#include "config.h"
+
+#include
+#include
+
+#include
+
+static int initialized = 0;
+
+static int SynthCallback(short *wav, int numsamples, espeak_EVENT *events) {
+ /* prevent warning for unused arguments */
+ (void) wav;
+ (void) numsamples;
+ (void) events;
+
+ return 0;
+}
+
+
+/* See http://llvm.org/docs/LibFuzzer.html */
+extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (!initialized) {
+ espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS, 0, NULL, 0);
+ espeak_SetSynthCallback(SynthCallback);
+ initialized = 1;
+ }
+
+ int synth_flags = espeakCHARS_UTF8 | espeakPHONEMES | espeakSSML;
+ espeak_Synth((char*) data, size + 1, 0, POS_CHARACTER, 0,
+ synth_flags, NULL, NULL);
+
+ return 0;
+}
diff --git a/tests/ssml-fuzzer/100e74d96310ff38a0747812810a713d21dab708.ssml b/tests/ssml-fuzzer/100e74d96310ff38a0747812810a713d21dab708.ssml
new file mode 100644
index 00000000..eae9e8b3
--- /dev/null
+++ b/tests/ssml-fuzzer/100e74d96310ff38a0747812810a713d21dab708.ssml
@@ -0,0 +1,6 @@
+
+
+ Test
+
diff --git a/tests/ssml-fuzzer/ca53d4601ced9346305e5b66b1e3d2012395ab25.ssml b/tests/ssml-fuzzer/ca53d4601ced9346305e5b66b1e3d2012395ab25.ssml
new file mode 100644
index 00000000..fb8345ff
--- /dev/null
+++ b/tests/ssml-fuzzer/ca53d4601ced9346305e5b66b1e3d2012395ab25.ssml
@@ -0,0 +1 @@
+B"!"";"ó.<<<