aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2021-07-05 21:53:07 +0200
committerPřemysl Eric Janouch <p@janouch.name>2021-07-05 21:58:43 +0200
commit8f350388ee2f6115bf4e1117c4968b2d51b73121 (patch)
tree79af458034ebee6769debda2b4b086768276894a
parent6bbe8aca2bcd6ea5ec2485b7d9351459da99b86c (diff)
downloadwdmtg-8f350388ee2f6115bf4e1117c4968b2d51b73121.tar.gz
wdmtg-8f350388ee2f6115bf4e1117c4968b2d51b73121.tar.xz
wdmtg-8f350388ee2f6115bf4e1117c4968b2d51b73121.zip
Rethink tests for compound-text.c
Instead of having the Xlib circular conversion test be random, add and run a new libFuzzer-based one. Sadly, there doesn't seem to be anything worthwhile to test that doesn't run indefinitely.
-rw-r--r--LICENSE2
-rwxr-xr-xcompound-text-fuzz7
-rw-r--r--compound-text.c91
3 files changed, 75 insertions, 25 deletions
diff --git a/LICENSE b/LICENSE
index 5826897..acb5eaa 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2016 - 2020, Přemysl Eric Janouch <p@janouch.name>
+Copyright (c) 2016 - 2021, Přemysl Eric Janouch <p@janouch.name>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
diff --git a/compound-text-fuzz b/compound-text-fuzz
new file mode 100755
index 0000000..910bb6e
--- /dev/null
+++ b/compound-text-fuzz
@@ -0,0 +1,7 @@
+#!/bin/sh -e
+clang -g -fsanitize=address,undefined,fuzzer -fno-sanitize-recover=all \
+ -DCOMPOUND_TEXT_LIBFUZZER_TEST compound-text.c \
+ -o compound-text-fuzz-bin `pkg-config --cflags --libs glib-2.0`
+trap 'rm compound-text-fuzz-bin' EXIT
+mkdir -p /tmp/compound-text-corpus
+./compound-text-fuzz-bin /tmp/compound-text-corpus
diff --git a/compound-text.c b/compound-text.c
index f3e9837..31d433a 100644
--- a/compound-text.c
+++ b/compound-text.c
@@ -1,7 +1,7 @@
//
// compound-text.c: partial X11 COMPOUND_TEXT to UCS-4 transcoder
//
-// Copyright (c) 2020, Přemysl Eric Janouch <p@janouch.name>
+// Copyright (c) 2020 - 2021, Přemysl Eric Janouch <p@janouch.name>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted.
@@ -599,35 +599,78 @@ compound_text_to_ucs4(const char *compound_text, size_t length)
return result;
}
-#if COMPOUND_TEXT_SELFTEST
-// Build with -DCOMPOUND_TEXT_SELFTEST `pkg-config --cflags --libs x11 glib-2.0`
+// We don't have any real conformance tests, but we shouldn't crash, at least
+#ifdef COMPOUND_TEXT_LIBFUZZER_TEST
+
+int
+LLVMFuzzerTestOneInput(const char *data, size_t size)
+{
+ free (compound_text_to_ucs4(data, size));
+ return 0;
+}
+
+#endif // COMPOUND_TEXT_LIBFUZZER_TEST
+#ifdef COMPOUND_TEXT_CIRCULAR_TEST
+
+// A particularly expensive circular test against Xlib, build with:
+// -DCOMPOUND_TEXT_CIRCULAR_TEST `pkg-config --cflags --libs x11 glib-2.0`
#include <X11/Xlib.h>
#include <X11/Xutil.h>
+static inline void
+test(Display *dpy, const char *text)
+{
+ XTextProperty prop;
+ Xutf8TextListToTextProperty(dpy, (char **) &text, 1,
+ XCompoundTextStyle, &prop);
+
+ int *ucs4 = NULL;
+ char *x = NULL;
+ if (!(ucs4 = compound_text_to_ucs4((char *) prop.value, prop.nitems)))
+ g_printerr("parse error '%s' -> '%s'\n", text, prop.value);
+ else if (!(x = g_ucs4_to_utf8((gunichar *) ucs4, -1, NULL, NULL, NULL)))
+ g_printerr("total failure: %s\n", prop.value);
+
+ free(ucs4);
+ free(x);
+ XFree(prop.value);
+}
+
+static inline bool
+try(gunichar c)
+{
+ // 1. GLib rejects surrogates
+ // 2. Xlib inserts a lone CSI (!)
+ // 3. not allowed or disruptive
+ return (c < 0xD800 || c > 0xDFFF) && (c != 0x9b) && (c >= 0x20);
+}
+
+#define LAST_RUNE 0x10FFFF /* 1114111, prime */
+
int
main(void)
{
- Display *dpy = XOpenDisplay(NULL); GString *s = g_string_new("");
- while (1) {
- g_string_truncate(s, 0);
- for (gsize i = 0; i < 10; i++) {
- int c = rand() & 0x10FFFF;
- if ((c < 0xD800 || c > 0xDFFF) && // GLib rejects surrogates
- (c != 0x9b) && // Xlib inserts a lone CSI (!)
- (c >= 0x20)) // not allowed or disruptive
- g_string_append_unichar(s, c);
+ Display *dpy = XOpenDisplay(NULL);
+ GString *s = g_string_new("");
+ for (gunichar i = 0; i < LAST_RUNE; i++) {
+ // Iterate through blocks first, might hit something interesting sooner.
+ // LAST_RUNE is a prime number, so anything can be used as a generator
+ // of a finite cyclic group, if we exclude this one last value.
+ gunichar a = (i << 8) % LAST_RUNE;
+ if (!try(a))
+ continue;
+
+ for (gunichar b = 0; b <= LAST_RUNE; b++) {
+ if (!try(b))
+ continue;
+
+ g_string_truncate(s, 0);
+ g_string_append_unichar(s, b);
+ g_string_append_unichar(s, a);
+ test(dpy, s->str);
}
-
- XTextProperty prop;
- Xutf8TextListToTextProperty(dpy, (char **) &s->str, 1,
- XCompoundTextStyle, &prop);
-
- int *ucs4 = NULL; char *x = NULL;
- if (!(ucs4 = compound_text_to_ucs4((char *) prop.value, prop.nitems)))
- g_printerr("parse error '%s' -> '%s'\n", s->str, prop.value);
- else if (!(x = g_ucs4_to_utf8((gunichar *) ucs4, -1, NULL, NULL, NULL)))
- g_printerr("total failure: %s\n", prop.value);
- free(ucs4); free(x); XFree(prop.value);
}
+ return 0;
}
-#endif
+
+#endif // COMPOUND_TEXT_CIRCULAR_TEST