Aliasing issue with TAILQ on ppc64 ?
Jilles Tjoelker
jilles at stack.nl
Tue Sep 18 22:09:03 UTC 2012
On Tue, Sep 18, 2012 at 08:14:52PM +0000, Poul-Henning Kamp wrote:
> In message <20120918195353.GA56160 at stack.nl>, Jilles Tjoelker writes:
> >An obvious fix is to make TAILQ_ENTRY and TAILQ_HEAD the same type (and
> >not just structurally identical) or to add an intermediate struct which
> >is the same between them.
> I've tried something along those lines several times, but I have so far
> not been able to make it work, without a major change to the TAILQ_*
> api, which I am not prepared to push.
> >However, I think the TAILQ_LAST and TAILQ_PREV macros are better
> >rewritten using __containerof,
> I really don't think that is an improvement, I'd prefer a typesafe
> standard C solution which static checker tools like Coverity and
> FlexeLint can see how works.
I think it is impossible to access another item's tqe_prev with a plain
pointer cast and no GCC __typeof__. This is because each time
TAILQ_ENTRY is used, it creates a new struct type that cannot be
accessed other than via the outer struct (hence needing a 'field' macro
parameter (which TAILQ_LAST does not have) and a way to refer to the
outer struct type (__typeof__ or a new macro parameter). This is
basically the __containerof solution.
In the TAILQ_PREV macro, the field parameter together with __typeof__
can be used to construct a cast that will allow legal access to another
entry's tqe_prev. However, an extra macro parameter would be required to
ensure we do not access the tail queue head using such a pointer. The
TAILQ_LAST macro lacks the field parameter to do this.
Probably even uglier is to construct a pointer to tqe_prev without
involving an lvalue of type struct headname. For example (untested):
#define TAILQ_LAST(head, headname, type) \
(**(struct type ***)((char *)(head)->tqh_last + \
offsetof(struct headname, tqh_last)))
With this, there is no strict-aliasing violation: a struct type **
object is accessed using an lvalue of the same type. The offsetof
expression returns an integer (size_t) and the pointer arithmetic
remains within bounds. As with the current illegal code using a pointer
cast, it is assumed that TAILQ_HEAD and TAILQ_ENTRY have the same
representation.
However, it again needs __typeof__ or an extra macro parameter.
A less ugly option breaks ABI, but I think API can be preserved:
#define TAILQ_HEAD(name, type) \
struct name { struct type *tqh_first, *tqh_last; }
#define TAILQ_ENTRY(type) \
struct { struct type *tqe_next, *tqe_prev; }
The macros will be easier to understand; they will have more branches
but fewer pointer dereferences than what we have now.
> I suspect it would be enough to make the tqh_last and tqe_prev
> pointer be volatile pointers to struct type pointers, but absent
> a deeper understanding of whats actually going on I can't tell
> if that would be a proper solution or merely obfuscation and
> workaround.
The text in the C standard about strict-aliasing does not make an
exception for volatile pointers, so it remains undefined behaviour.
However, it is quite likely that compilers will generate code as
expected. The strict-aliasing rules are generally used to assume certain
objects cannot alias, but this is irrelevant when reads and writes have
to go to memory directly anyway.
Another workaround is GCC's __attribute__((__may_alias__)), added to the
TAILQ_HEAD and TAILQ_ENTRY macros.
It is also an option to use a "proper" solution using __typeof__ if
available and a hack using volatile if not.
--
Jilles Tjoelker
More information about the freebsd-arch
mailing list