Commit 43992de4 authored by David Gibson's avatar David Gibson

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: default avatarDavid Gibson <david@gibson.dropbear.id.au>
parent fced03d3
......@@ -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;
}
......@@ -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.
......
#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;
}
#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;
}
......@@ -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;
}
#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;
}
#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();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment