Commit e4e15ed286f7739682737ec2ca6d681dbdd00e79

Guillem Jover 2018-05-22T13:50:44

Fix strnvis() and strnunvis() NetBSD ABI break The NetBSD implementations have different prototypes to the ones coming from OpenBSD, which will break builds, and have caused segfaults at run-time. We provide now both interfaces with different prototypes as different version nodes allow selecting them at compile-time, defaulting for now to the OpenBSD one to avoid build-time breakage, while emitting a compile-time warning. Later on, in 0.10.0, we will be switching the compile-time default to the NetBSD version. Ref: http://gnats.netbsd.org/44977 Fixes: https://bugs.debian.org/899282

diff --git a/include/bsd/vis.h b/include/bsd/vis.h
index d8b6635..84102b2 100644
--- a/include/bsd/vis.h
+++ b/include/bsd/vis.h
@@ -88,6 +88,22 @@
 #include <bsd/sys/cdefs.h>
 #endif
 
+/*
+ * NetBSD added an strnvis and unfortunately made it incompatible with the
+ * existing one in OpenBSD and Freedesktop's libbsd (the former having existed
+ * for over ten years). Despite this incompatibility being reported during
+ * development (see http://gnats.netbsd.org/44977) they still shipped it.
+ * Even more unfortunately FreeBSD and later MacOS picked up this incompatible
+ * implementation.
+ *
+ * Provide both implementations and default for now on the historical one to
+ * avoid breakage, we will switch to the NetBSD one in libbsd 0.10.0 or so.
+ * Define LIBBSD_NETBSD_VIS to switch to the NetBSD one now.
+ */
+#ifndef LIBBSD_NETBSD_VIS
+#warning "NetBSD added incompatible strnvis() and strnunvis(), please see <bsd/vis.h> for more detils."
+#endif
+
 __BEGIN_DECLS
 char	*vis(char *, int, int, int);
 char	*nvis(char *, size_t, int, int, int);
@@ -97,7 +113,14 @@ char	*snvis(char *, size_t, int, int, int, const char *);
 
 int	strvis(char *, const char *, int);
 int	stravis(char **, const char *, int);
-int	strnvis(char *, size_t, const char *, int);
+#ifdef LIBBSD_NETBSD_VIS
+/* NetBSD prototype. */
+int	LIBBSD_REDIRECT(strnvis, (char *, size_t, const char *, int),
+                        strnvis_netbsd);
+#else
+/* OpenBSD prototype (current default). */
+int	strnvis(char *, const char *, size_t, int);
+#endif
 
 int	strsvis(char *, const char *, int, const char *);
 int	strsnvis(char *, size_t, const char *, int, const char *);
@@ -112,7 +135,14 @@ int	strsenvisx(char *, size_t, const char *, size_t , int, const char *,
     int *);
 
 int	strunvis(char *, const char *);
-int	strnunvis(char *, size_t, const char *);
+#ifdef LIBBSD_NETBSD_VIS
+/* NetBSD prototype. */
+int	LIBBSD_REDIRECT(strnunvis, (char *, size_t, const char *),
+                        strnunvis_netbsd);
+#else
+/* OpenBSD prototype (current default). */
+ssize_t	strnunvis(char *, const char *, size_t);
+#endif
 
 int	strunvisx(char *, const char *, int);
 int	strnunvisx(char *, size_t, const char *, int);
diff --git a/src/libbsd.map b/src/libbsd.map
index 98b95ce..ce36040 100644
--- a/src/libbsd.map
+++ b/src/libbsd.map
@@ -160,3 +160,11 @@ LIBBSD_0.9 {
     strsvisx;
     svis;
 } LIBBSD_0.8;
+
+LIBBSD_0.9.1 {
+    /* The strnvis() and strnunvis() symbols changed prototype to match
+     * the NetBSD implementation. Provided as versioned nodes in 0.9.1, and
+     * exposed here explicitly so that we can redirect at compile-time. */
+    strnvis_netbsd;
+    strnunvis_netbsd;
+} LIBBSD_0.9;
diff --git a/src/unvis.c b/src/unvis.c
index e16bd50..94e3e7a 100644
--- a/src/unvis.c
+++ b/src/unvis.c
@@ -37,7 +37,10 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <errno.h>
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcpp"
 #include <vis.h>
+#pragma GCC diagnostic pop
 
 #ifdef __weak_alias
 __weak_alias(strnunvisx,_strnunvisx)
@@ -543,8 +546,30 @@ strunvis(char *dst, const char *src)
 	return strnunvisx(dst, (size_t)~0, src, 0);
 }
 
+/*
+ * NetBSD added an strnvis and unfortunately made it incompatible with the
+ * existing one in OpenBSD and Freedesktop's libbsd (the former having existed
+ * for over ten years). Despite this incompatibility being reported during
+ * development (see http://gnats.netbsd.org/44977) they still shipped it.
+ * Even more unfortunately FreeBSD and later MacOS picked up this incompatible
+ * implementation.
+ *
+ * Provide both implementations and default for now on the historical one to
+ * avoid breakage, we will switch to the NetBSD one in libbsd 0.10.0 or so.
+ *
+ * OpenBSD, 2001: strnunvis(char *dst, const char *src, size_t dlen);
+ * NetBSD: 2012,  strnunvis(char *dst, size_t dlen, const char *src);
+ */
+ssize_t
+strnunvis_openbsd(char *dst, const char *src, size_t dlen)
+{
+	return strnunvisx(dst, dlen, src, 0);
+}
+__asm__(".symver strnunvis_openbsd,strnunvis@@LIBBSD_0.2");
+
 int
-strnunvis(char *dst, size_t dlen, const char *src)
+strnunvis_netbsd(char *dst, size_t dlen, const char *src)
 {
 	return strnunvisx(dst, dlen, src, 0);
 }
+__asm__(".symver strnunvis_netbsd,strnunvis@LIBBSD_0.9.1");
diff --git a/src/vis.c b/src/vis.c
index 5599439..c2cd2d8 100644
--- a/src/vis.c
+++ b/src/vis.c
@@ -60,7 +60,10 @@
 #include <sys/param.h>
 
 #include <assert.h>
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcpp"
 #include <vis.h>
+#pragma GCC diagnostic pop
 #include <errno.h>
 #include <stdlib.h>
 #include <wchar.h>
@@ -701,11 +704,33 @@ strvis(char *mbdst, const char *mbsrc, int flags)
 	return istrsenvisxl(&mbdst, NULL, mbsrc, flags, "", NULL);
 }
 
+/*
+ * NetBSD added an strnvis and unfortunately made it incompatible with the
+ * existing one in OpenBSD and Freedesktop's libbsd (the former having existed
+ * for over ten years). Despite this incompatibility being reported during
+ * development (see http://gnats.netbsd.org/44977) they still shipped it.
+ * Even more unfortunately FreeBSD and later MacOS picked up this incompatible
+ * implementation.
+ *
+ * Provide both implementations and default for now on the historical one to
+ * avoid breakage, we will switch to the NetBSD one in libbsd 0.10.0 or so.
+ *
+ * OpenBSD, 2001: strnvis(char *dst, const char *src, size_t dlen, int flag);
+ * NetBSD: 2012,  strnvis(char *dst, size_t dlen, const char *src, int flag);
+ */
+int
+strnvis_openbsd(char *mbdst, const char *mbsrc, size_t dlen, int flags)
+{
+	return istrsenvisxl(&mbdst, &dlen, mbsrc, flags, "", NULL);
+}
+__asm__(".symver strnvis_openbsd,strnvis@@LIBBSD_0.2");
+
 int
-strnvis(char *mbdst, size_t dlen, const char *mbsrc, int flags)
+strnvis_netbsd(char *mbdst, size_t dlen, const char *mbsrc, int flags)
 {
 	return istrsenvisxl(&mbdst, &dlen, mbsrc, flags, "", NULL);
 }
+__asm__(".symver strnvis_netbsd,strnvis@LIBBSD_0.9.1");
 
 int
 stravis(char **mbdstp, const char *mbsrc, int flags)
diff --git a/test/.gitignore b/test/.gitignore
index 256574c..47063b9 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -19,3 +19,5 @@ setmode
 strl
 strmode
 strnstr
+vis
+vis-openbsd
diff --git a/test/Makefile.am b/test/Makefile.am
index d86539a..452558d 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -47,6 +47,8 @@ check_PROGRAMS = \
 	strl \
 	strmode \
 	strnstr \
+	vis \
+	vis-openbsd \
 	$(nil)
 
 if HAVE_LIBTESTU01
diff --git a/test/vis-openbsd.c b/test/vis-openbsd.c
new file mode 100644
index 0000000..99c4198
--- /dev/null
+++ b/test/vis-openbsd.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 2018 Guillem Jover <guillem@hadrons.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <string.h>
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcpp"
+#include <vis.h>
+#pragma GCC diagnostic pop
+
+int
+main(int argc, char **argv)
+{
+	char str[200];
+	char unstr[200];
+
+	strnvis(str, "0123456789abcdef", 10, 0);
+	assert(strcmp(str, "0123456789") == 0);
+
+	strnunvis(unstr, str, 100);
+	assert(strcmp(unstr, "0123456789") == 0);
+
+	return 0;
+}
diff --git a/test/vis.c b/test/vis.c
new file mode 100644
index 0000000..10b8b0f
--- /dev/null
+++ b/test/vis.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright © 2018 Guillem Jover <guillem@hadrons.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LIBBSD_NETBSD_VIS 1
+
+#include <assert.h>
+#include <string.h>
+#include <vis.h>
+
+int
+main(int argc, char **argv)
+{
+	char str[200];
+	char unstr[200];
+
+	strnvis(str, 10, "0123456789abcdef", 0);
+	assert(strcmp(str, "0123456789") == 0);
+
+	strnunvis(unstr, 100, str);
+	assert(strcmp(unstr, "0123456789") == 0);
+
+	return 0;
+}