From 43992de4769085b3aba0fd9c167a85ad14e771d2 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 20 Oct 2015 14:23:37 +1100 Subject: [PATCH] tcon: Add an alternate way of building type canaries 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 --- 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 f6a6f0f9..50dc0a5c 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 cf82f3e7..a256f103 100644 --- a/ccan/tcon/tcon.h +++ b/ccan/tcon/tcon.h @@ -42,6 +42,77 @@ #define TCON(decls) struct { decls; } _tcon[1] #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. 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 00000000..f24cb01b --- /dev/null +++ b/ccan/tcon/test/compile_fail-tcon_cast_wrap.c @@ -0,0 +1,25 @@ +#include +#include + +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 00000000..26da13c3 --- /dev/null +++ b/ccan/tcon/test/compile_fail-wrap.c @@ -0,0 +1,20 @@ +#include +#include + +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 26b712f6..694a53b5 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 447f0ee5..f3fe2c6f 100644 --- a/ccan/tcon/test/compile_ok.c +++ b/ccan/tcon/test/compile_ok.c @@ -1,4 +1,5 @@ #include +#include #include 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 00000000..0d5cfef6 --- /dev/null +++ b/ccan/tcon/test/run-wrap.c @@ -0,0 +1,18 @@ +#include +#include +#include + +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.39.2