Re: x86-64: Maintain 16-byte stack alignment

From: Linus Torvalds
Date: Thu Jan 12 2017 - 14:51:31 EST


On Thu, Jan 12, 2017 at 6:02 AM, Josh Poimboeuf <jpoimboe@xxxxxxxxxx> wrote:
>
> Just to clarify, I think you're asking if, for versions of gcc which
> don't support -mpreferred-stack-boundary=3, objtool can analyze all C
> functions to ensure their stacks are 16-byte aligned.
>
> It's certainly possible, but I don't see how that solves the problem.
> The stack will still be misaligned by entry code. Or am I missing
> something?

I think the argument is that we *could* try to align things, if we
just had some tool that actually then verified that we aren't missing
anything.

I'm not entirely happy with checking the generated code, though,
because as Ingo says, you have a 50:50 chance of just getting it right
by mistake. So I'd much rather have some static tool that checks
things at a code level (ie coccinelle or sparse).

Almost totally untested "sparse" patch appended. The problem with
sparse, obviously, is that few enough people run it, and it gives a
lot of other warnings. But maybe Herbert can test whether this would
actually have caught his situation, doing something like an
allmodconfig build with "C=2" to force a sparse run on everything, and
redirecting the warnings to stderr.

But this patch does seem to give a warning for the patch that Herbert
had, and that caused problems.

And in fact it seems to find a few other possible problems (most, but
not all, in crypto). This run was with the broken chacha20 patch
applied, to verify that I get a warning for that case:

arch/x86/crypto/chacha20_glue.c:70:13: warning: symbol 'state' has
excessive alignment (16)
arch/x86/crypto/aesni-intel_glue.c:724:12: warning: symbol 'iv' has
excessive alignment (16)
arch/x86/crypto/aesni-intel_glue.c:803:12: warning: symbol 'iv' has
excessive alignment (16)
crypto/shash.c:82:12: warning: symbol 'ubuf' has excessive alignment (16)
crypto/shash.c:118:12: warning: symbol 'ubuf' has excessive alignment (16)
drivers/char/hw_random/via-rng.c:89:14: warning: symbol 'buf' has
excessive alignment (16)
net/bridge/netfilter/ebtables.c:1809:31: warning: symbol 'tinfo'
has excessive alignment (64)
drivers/crypto/padlock-sha.c:85:14: warning: symbol 'buf' has
excessive alignment (16)
drivers/crypto/padlock-sha.c:147:14: warning: symbol 'buf' has
excessive alignment (16)
drivers/crypto/padlock-sha.c:304:12: warning: symbol 'buf' has
excessive alignment (16)
drivers/crypto/padlock-sha.c:388:12: warning: symbol 'buf' has
excessive alignment (16)
net/openvswitch/actions.c:797:33: warning: symbol 'ovs_rt' has
excessive alignment (64)
drivers/net/ethernet/neterion/vxge/vxge-config.c:1006:38: warning:
symbol 'vpath' has excessive alignment (64)

although I think at least some of these happen to be ok.

There are a few places that clearly don't care about exact alignment,
and use "__attribute__((aligned))" without any specific alignment
value.

It's just sparse that thinks that implies 16-byte alignment (it
doesn't, really - it's unspecified, and is telling gcc to use "maximum
useful alignment", so who knows _what_ gcc will assume).

But some of them may well be real issues - if the alignment is about
correctness rather than anything else.

Anyway, the advantage of this kind of source-level check is that it
should really catch things regardless of "luck" wrt alignment.

Linus
flow.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)

diff --git a/flow.c b/flow.c
index 7db9548..c876869 100644
--- a/flow.c
+++ b/flow.c
@@ -601,6 +601,20 @@ static void simplify_one_symbol(struct entrypoint *ep, struct symbol *sym)
unsigned long mod;
int all, stores, complex;

+ /*
+ * Warn about excessive local variable alignment.
+ *
+ * This needs to be linked up with some flag to enable
+ * it, and specify the alignment. The 'max_int_alignment'
+ * just happens to be what we want for the kernel for x86-64.
+ */
+ mod = sym->ctype.modifiers;
+ if (!(mod & (MOD_NONLOCAL | MOD_STATIC))) {
+ unsigned int alignment = sym->ctype.alignment;
+ if (alignment > max_int_alignment)
+ warning(sym->pos, "symbol '%s' has excessive alignment (%u)", show_ident(sym->ident), alignment);
+ }
+
/* Never used as a symbol? */
pseudo = sym->pseudo;
if (!pseudo)