CCAN Archive mirror
 help / color / mirror / Atom feed
From: David Gibson <david@gibson.dropbear.id.au>
To: ccan@lists.ozlabs.org, rusty@rustcorp.com.au
Subject: [PATCH 1/6] tcon: Add an alternate way of building type canaries
Date: Tue, 20 Oct 2015 15:45:55 +1100	[thread overview]
Message-ID: <1445316360-23727-2-git-send-email-david@gibson.dropbear.id.au> (raw)
In-Reply-To: <1445316360-23727-1-git-send-email-david@gibson.dropbear.id.au>

The tcon module allows you to add "type canaries" to a structures, which
can be used for later typechecks.  The canaries are implemented using
a flexible array member, to avoid them taking any actual space at runtime.
That means the canaries must go at the end of your structure.

That doesn't seem like a big limitation, except that it also means the
structure containing the canaries must be at the end of any structure it
is embedded in in turn, which is a rather more serious limitation.

This patch adds a TCON_WRAP() macro which wraps a given type in a new type
which also contains type canaries, and doesn't suffer the last member
limitation.  The drawback is that if the wrapped type has smaller size than
a pointer, the type canaries will pad the wrapped type out to the size of a
pointer.

By constructing the wrappers carefully, the existing tcon macros will work
on both wrapper types constructed with TCON_WRAP and on structures
explicitly including TCON type canaries.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 ccan/tcon/_info                              |  5 ++
 ccan/tcon/tcon.h                             | 71 ++++++++++++++++++++++++++++
 ccan/tcon/test/compile_fail-tcon_cast_wrap.c | 25 ++++++++++
 ccan/tcon/test/compile_fail-wrap.c           | 20 ++++++++
 ccan/tcon/test/compile_ok-void.c             |  6 +++
 ccan/tcon/test/compile_ok.c                  | 11 +++++
 ccan/tcon/test/run-wrap.c                    | 18 +++++++
 7 files changed, 156 insertions(+)
 create mode 100644 ccan/tcon/test/compile_fail-tcon_cast_wrap.c
 create mode 100644 ccan/tcon/test/compile_fail-wrap.c
 create mode 100644 ccan/tcon/test/run-wrap.c

diff --git a/ccan/tcon/_info b/ccan/tcon/_info
index f6a6f0f..50dc0a5 100644
--- a/ccan/tcon/_info
+++ b/ccan/tcon/_info
@@ -71,5 +71,10 @@ int main(int argc, char *argv[])
 		return 0;
 	}
 
+	if (strcmp(argv[1], "testdepends") == 0) {
+		printf("ccan/build_assert\n");
+		return 0;
+	}
+
 	return 1;
 }
diff --git a/ccan/tcon/tcon.h b/ccan/tcon/tcon.h
index cf82f3e..ff96e89 100644
--- a/ccan/tcon/tcon.h
+++ b/ccan/tcon/tcon.h
@@ -43,6 +43,77 @@
 #endif
 
 /**
+ * TCON_WRAP - declare a wrapper type containing a base type and type canaries
+ * @basetype: the base type to wrap
+ * @decls: the semi-colon separated list of type canaries.
+ *
+ * This expands to a new type which includes the given base type, and
+ * also type canaries, similar to those created with TCON.
+ *
+ * The embedded base type value can be accessed using tcon_unwrap().
+ *
+ * Differences from using TCON()
+ * - The wrapper type will take either the size of the base type, or
+ *   the size of a single pointer, whichever is greater (regardless of
+ *   compiler)
+ * - A TCON_WRAP type may be included in another structure, and need
+ *   not be the last element.
+ *
+ * A type of "void *" will allow tcon_check() to pass on any (pointer) type.
+ *
+ * Example:
+ *	// Simply typesafe linked list.
+ *	struct list_head {
+ *		struct list_head *prev, *next;
+ *	};
+ *
+ *	typedef TCON_WRAP(struct list_head, char *canary) string_list_t;
+ *
+ *	// More complex: mapping from one type to another.
+ *	struct map {
+ *		void *contents;
+ *	};
+ *
+ *	typedef TCON_WRAP(struct map, char *charp_canary; int int_canary)
+ *		int_to_string_map_t;
+ */
+#define TCON_WRAP(basetype, decls) \
+	union {			   \
+		basetype _base;	   \
+		struct {	   \
+			decls;	   \
+		} *_tcon;	   \
+	}
+
+/**
+ * TCON_WRAP_INIT - an initializer for a variable declared with TCON_WRAP
+ * @...: Initializer for the base type (treated as variadic so commas
+ *       can be included)
+ *
+ * Converts converts an initializer suitable for a base type into one
+ * suitable for that type wrapped with TCON_WRAP.
+ *
+ * Example:
+ *	TCON_WRAP(int, char *canary) canaried_int = TCON_WRAP_INIT(17);
+ */
+#define TCON_WRAP_INIT(...)			\
+	{ ._base = __VA_ARGS__, }
+
+/**
+ * tcon_unwrap: Access the base type of a TCON_WRAP
+ * @ptr: pointer to an object declared with TCON_WRAP
+ *
+ * tcon_unwrap() returns a pointer to the base type of the TCON_WRAP()
+ * object pointer to by @ptr.
+ *
+ * Example:
+ *	TCON_WRAP(int, char *canary) canaried_int;
+ *
+ *	*tcon_unwrap(&canaried_int) = 17;
+ */
+#define tcon_unwrap(ptr) (&((ptr)->_base))
+
+/**
  * tcon_check - typecheck a typed container
  * @x: the structure containing the TCON.
  * @canary: which canary to check against.
diff --git a/ccan/tcon/test/compile_fail-tcon_cast_wrap.c b/ccan/tcon/test/compile_fail-tcon_cast_wrap.c
new file mode 100644
index 0000000..f24cb01
--- /dev/null
+++ b/ccan/tcon/test/compile_fail-tcon_cast_wrap.c
@@ -0,0 +1,25 @@
+#include <ccan/tcon/tcon.h>
+#include <stdlib.h>
+
+struct container {
+	void *p;
+};
+
+int main(int argc, char *argv[])
+{
+	TCON_WRAP(struct container,
+		  int *tc1; char *tc2) icon;
+#ifdef FAIL
+#if !HAVE_TYPEOF
+#error We cannot detect type problems without HAVE_TYPEOF
+#endif
+	char *
+#else
+	int *
+#endif
+		x;
+
+	tcon_unwrap(&icon)->p = NULL;
+	x = tcon_cast(&icon, tc1, tcon_unwrap(&icon)->p);
+	return x != NULL ? 0 : 1;
+}
diff --git a/ccan/tcon/test/compile_fail-wrap.c b/ccan/tcon/test/compile_fail-wrap.c
new file mode 100644
index 0000000..26da13c
--- /dev/null
+++ b/ccan/tcon/test/compile_fail-wrap.c
@@ -0,0 +1,20 @@
+#include <ccan/tcon/tcon.h>
+#include <stdlib.h>
+
+struct container {
+	void *p;
+};
+
+int main(int argc, char *argv[])
+{
+	TCON_WRAP(struct container, int *canary) icon;
+#ifdef FAIL
+	char *
+#else
+	int *
+#endif
+		x = NULL;
+
+	tcon_unwrap(tcon_check(&icon, canary, x))->p = x;
+	return 0;
+}
diff --git a/ccan/tcon/test/compile_ok-void.c b/ccan/tcon/test/compile_ok-void.c
index 26b712f..694a53b 100644
--- a/ccan/tcon/test/compile_ok-void.c
+++ b/ccan/tcon/test/compile_ok-void.c
@@ -13,9 +13,15 @@ struct void_container {
 int main(int argc, char *argv[])
 {
 	struct void_container vcon;
+	TCON_WRAP(struct container, void *canary) vconw;
 
 	tcon_check(&vcon, canary, NULL)->raw.p = NULL;
 	tcon_check(&vcon, canary, argv[0])->raw.p = NULL;
 	tcon_check(&vcon, canary, main)->raw.p = NULL;
+
+	tcon_unwrap(tcon_check(&vconw, canary, NULL))->p = NULL;
+	tcon_unwrap(tcon_check(&vconw, canary, argv[0]))->p = NULL;
+	tcon_unwrap(tcon_check(&vconw, canary, main))->p = NULL;
+
 	return 0;
 }
diff --git a/ccan/tcon/test/compile_ok.c b/ccan/tcon/test/compile_ok.c
index 447f0ee..f3fe2c6 100644
--- a/ccan/tcon/test/compile_ok.c
+++ b/ccan/tcon/test/compile_ok.c
@@ -1,4 +1,5 @@
 #include <ccan/tcon/tcon.h>
+#include <ccan/build_assert/build_assert.h>
 #include <stdlib.h>
 
 struct container {
@@ -19,9 +20,19 @@ int main(int argc, char *argv[])
 {
 	struct int_container icon;
 	struct charp_and_int_container cicon;
+	TCON_WRAP(struct container, int tc) iconw;
+	TCON_WRAP(struct container, int tc1; char *tc2) ciconw;
 
 	tcon_check(&icon, tc, 7)->raw.p = NULL;
 	tcon_check(&cicon, tc1, 7)->raw.p = argv[0];
 	tcon_check(&cicon, tc2, argv[0])->raw.p = argv[0];
+
+	tcon_unwrap(tcon_check(&iconw, tc, 7))->p = NULL;
+	tcon_unwrap(tcon_check(&ciconw, tc1, 7))->p = argv[0];
+	tcon_unwrap(tcon_check(&ciconw, tc2, argv[0]))->p = argv[0];
+
+	BUILD_ASSERT(sizeof(iconw) == sizeof(struct container));
+	BUILD_ASSERT(sizeof(ciconw) == sizeof(struct container));
+
 	return 0;
 }
diff --git a/ccan/tcon/test/run-wrap.c b/ccan/tcon/test/run-wrap.c
new file mode 100644
index 0000000..0d5cfef
--- /dev/null
+++ b/ccan/tcon/test/run-wrap.c
@@ -0,0 +1,18 @@
+#include <ccan/tcon/tcon.h>
+#include <ccan/tap/tap.h>
+#include <stdlib.h>
+
+typedef TCON_WRAP(int, char *canary) canaried_int;
+
+int main(int argc, char *argv[])
+{
+	canaried_int ci = TCON_WRAP_INIT(0);
+
+	plan_tests(2);
+
+	ok1(*tcon_unwrap(&ci) == 0);
+	*tcon_unwrap(&ci) = 17;
+	ok1(*tcon_unwrap(&ci) == 17);
+
+	return exit_status();
+}
-- 
2.4.3

_______________________________________________
ccan mailing list
ccan@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/ccan

  reply	other threads:[~2015-10-20  4:45 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-10-20  4:45 [PATCH 0/6] tcon extensions for easier typesafe wrapping David Gibson
2015-10-20  4:45 ` David Gibson [this message]
2015-10-20  4:45 ` [PATCH 2/6] tcon: Add tcon_sizeof() helper David Gibson
2015-10-20  4:45 ` [PATCH 3/6] tcon: Encode integer values into "type" canaries David Gibson
2015-10-20  4:45 ` [PATCH 4/6] tcon: Encode information on container members in " David Gibson
2015-10-20  4:45 ` [PATCH 5/6] lstack: Streamline interface with TCON_CONTAINER David Gibson
2015-10-20  4:46 ` [PATCH 6/6] lqueue: " David Gibson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1445316360-23727-2-git-send-email-david@gibson.dropbear.id.au \
    --to=david@gibson.dropbear.id.au \
    --cc=ccan@lists.ozlabs.org \
    --cc=rusty@rustcorp.com.au \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).