[PATCH v2 3/3 wayland] Validate the protocol xml during scanning

Peter Hutterer peter.hutterer at who-t.net
Mon Nov 9 15:53:08 PST 2015


Embed the wayland.dtd protocol data into the scanner binary so we can validate
external protocol files without requiring makefile changes. Hat-tip to Pekka
Paalanen for the embedding trick.
The embedding trick doesn't work well if the to-be-embedded file is in a
different location than the source file, so copy/link it during configure and
then build it in from the local directory.

The current expat parser is not a validating parser, moving scanner.c to
another parser has the risk of breaking compatibility. This patch adds libxml2
as extra (optional) dependency, but that also requires parsing the input
twice.

If the protocol fails validation a warning is printed but no error is returned
otherwise.

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
Changes to v1:
- move the dtddata.S to SOURCES and add AM_PROG_AS to configure. This fixes
  distcheck
- handle explicit input files, rather than just stdin
- changed the warning to refer to a built-in dtd 

 Makefile.am   | 11 ++++++---
 configure.ac  | 13 +++++++++++
 src/dtddata.S | 40 +++++++++++++++++++++++++++++++++
 src/scanner.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 132 insertions(+), 3 deletions(-)
 create mode 100644 src/dtddata.S

diff --git a/Makefile.am b/Makefile.am
index 9114d98..7d31a39 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -23,11 +23,16 @@ pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA =
 
 bin_PROGRAMS = wayland-scanner
-wayland_scanner_SOURCES = src/scanner.c
-wayland_scanner_CFLAGS = $(EXPAT_CFLAGS) $(AM_CFLAGS)
-wayland_scanner_LDADD = $(EXPAT_LIBS) libwayland-util.la
+wayland_scanner_SOURCES = src/scanner.c src/dtddata.S
+wayland_scanner_CFLAGS = $(EXPAT_CFLAGS) $(LIBXML_CFLAGS) $(AM_CFLAGS)
+wayland_scanner_LDADD = $(EXPAT_LIBS) $(LIBXML_LIBS) libwayland-util.la
 pkgconfig_DATA += src/wayland-scanner.pc
 
+src/dtddata.o: protocol/wayland.dtd
+
+%.o: %.S
+	$(AM_V_GEN)$(CC) $(ASFLAGS) $(CPPFLAGS) $(TARGET_MACH) -c -o $@ $<
+
 if USE_HOST_SCANNER
 wayland_scanner = wayland-scanner
 else
diff --git a/configure.ac b/configure.ac
index ef26929..48658cf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -28,6 +28,7 @@ AM_SILENT_RULES([yes])
 AC_PROG_CC
 AC_PROG_CXX
 AC_PROG_GREP
+AM_PROG_AS
 
 # check if we have C++ compiler. This is hacky workaround,
 # for a reason why it is this way see
@@ -71,6 +72,12 @@ AC_ARG_ENABLE([documentation],
 	      [],
 	      [enable_documentation=yes])
 
+AC_ARG_ENABLE([dtd-validation],
+	      [AC_HELP_STRING([--disable-dtd-validation],
+			      [Disable DTD validation of the protocol])],
+	      [],
+	      [enable_dtdvalidation=yes])
+
 AM_CONDITIONAL(USE_HOST_SCANNER, test "x$with_host_scanner" = xyes)
 
 AM_CONDITIONAL(ENABLE_LIBRARIES, test "x$enable_libraries" = xyes)
@@ -105,6 +112,12 @@ PKG_CHECK_MODULES(EXPAT, [expat], [],
 	 AC_SUBST(EXPAT_LIBS)
 	])
 
+if test "x$enable_dtdvalidation" = "xyes"; then
+	PKG_CHECK_MODULES(LIBXML, [libxml-2.0])
+	AC_DEFINE(HAVE_LIBXML, 1, [libxml-2.0 is available])
+	AC_CONFIG_LINKS([src/wayland.dtd.embed:protocol/wayland.dtd])
+fi
+
 AC_PATH_PROG(XSLTPROC, xsltproc)
 AM_CONDITIONAL([HAVE_XSLTPROC], [test "x$XSLTPROC" != "x"])
 
diff --git a/src/dtddata.S b/src/dtddata.S
new file mode 100644
index 0000000..bb27de7
--- /dev/null
+++ b/src/dtddata.S
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2015 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* from: http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967#comment-348129 */
+
+.altmacro
+.macro binfile name file
+	.p2align 2
+	.globl \name&_begin
+\name&_begin:
+	.incbin \file
+\name&_end:
+	.byte 0
+	.p2align 2
+	.globl \name&_len
+\name&_len:
+	.int (\name&_end - \name&_begin)
+.endm
+
+.section .rodata
+binfile DTD_DATA "src/wayland.dtd.embed"
diff --git a/src/scanner.c b/src/scanner.c
index 8ecdd56..406519f 100644
--- a/src/scanner.c
+++ b/src/scanner.c
@@ -25,6 +25,8 @@
  * SOFTWARE.
  */
 
+#include "config.h"
+
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdarg.h>
@@ -34,9 +36,18 @@
 #include <expat.h>
 #include <getopt.h>
 #include <limits.h>
+#include <unistd.h>
+
+#if HAVE_LIBXML
+#include <libxml/parser.h>
+#endif
 
 #include "wayland-util.h"
 
+/* Embedded wayland.dtd file, see dtddata.S */
+extern char DTD_DATA_begin;
+extern int DTD_DATA_len;
+
 enum side {
 	CLIENT,
 	SERVER,
@@ -59,6 +70,57 @@ usage(int ret)
 	exit(ret);
 }
 
+static bool
+is_dtd_valid(FILE *input)
+{
+	bool rc = true;
+#if HAVE_LIBXML
+	xmlParserCtxtPtr ctx = NULL;
+	xmlDocPtr doc = NULL;
+	xmlDtdPtr dtd = NULL;
+	xmlValidCtxtPtr	dtdctx;
+	xmlParserInputBufferPtr	buffer;
+	int fd = fileno(input);
+
+	dtdctx = xmlNewValidCtxt();
+	ctx = xmlNewParserCtxt();
+	if (!ctx || !dtdctx)
+		abort();
+
+	buffer = xmlParserInputBufferCreateMem(&DTD_DATA_begin,
+					       DTD_DATA_len,
+					       XML_CHAR_ENCODING_UTF8);
+	if (!buffer) {
+		fprintf(stderr, "Failed to init buffer for DTD.\n");
+		abort();
+	}
+
+	dtd = xmlIOParseDTD(NULL, buffer, XML_CHAR_ENCODING_UTF8);
+	if (!dtd) {
+		fprintf(stderr, "Failed to parse DTD.\n");
+		abort();
+	}
+
+	doc = xmlCtxtReadFd(ctx, fd, "protocol", NULL, 0);
+	if (!doc) {
+		fprintf(stderr, "Failed to read XML\n");
+		abort();
+	}
+
+	rc = xmlValidateDtd(dtdctx, doc, dtd);
+	xmlFreeDoc(doc);
+	xmlFreeParserCtxt(ctx);
+	xmlFreeValidCtxt(dtdctx);
+	/* xmlIOParseDTD consumes buffer */
+
+	if (lseek(fd, 0, SEEK_SET) != 0) {
+		fprintf(stderr, "Failed to reset fd, output would be garbage.\n");
+		abort();
+	}
+#endif
+	return rc;
+}
+
 #define XML_BUFFER_SIZE 4096
 
 struct location {
@@ -1617,6 +1679,15 @@ int main(int argc, char *argv[])
 	ctx.protocol = &protocol;
 	ctx.loc.filename = "<stdin>";
 
+	if (!is_dtd_valid(input)) {
+		fprintf(stderr,
+		"*******************************************************\n"
+		"*                                                     *\n"
+		"* WARNING: XML failed validation against built-in DTD *\n"
+		"*                                                     *\n"
+		"*******************************************************\n");
+	}
+
 	/* create XML parser */
 	ctx.parser = XML_ParserCreate(NULL);
 	XML_SetUserData(ctx.parser, &ctx);
-- 
2.4.3



More information about the wayland-devel mailing list