This is an archive of the discontinued LLVM Phabricator instance.

Provide __builtin_alloca*_uninitialized variants
ClosedPublic

Authored by melver on Dec 9 2021, 4:44 AM.

Details

Summary

When -ftrivial-auto-var-init= is enabled, allocas unconditionally
receive auto-initialization since [1].

In certain cases, it turns out, this is causing problems. For example,
when using alloca to add a random stack offset, as the Linux kernel does
on syscall entry [2]. In this case, none of the alloca'd stack memory is
ever used, and initializing it should be controllable; furthermore, it
is not always possible to safely call memset (see [2]).

Introduce __builtin_alloca_uninitialized() (and
__builtin_alloca_with_align_uninitialized), which never performs
initialization when -ftrivial-auto-var-init= is enabled.

[1] https://reviews.llvm.org/D60548
[2] https://lkml.kernel.org/r/YbHTKUjEejZCLyhX@elver.google.com

Diff Detail

Event Timeline

melver requested review of this revision.Dec 9 2021, 4:44 AM
melver created this revision.
Herald added a project: Restricted Project. · View Herald TranscriptDec 9 2021, 4:44 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
glider added a comment.Dec 9 2021, 5:12 AM

I second this proposal.
https://reviews.llvm.org/D60548 suggests to handle this case using a pragma, but the required change seems to be more intrusive.

clang/lib/CodeGen/CGBuiltin.cpp
3437

For the sake of completeness, shall we also implement an uninitialized version of __builtin_alloca_with_align()?

melver updated this revision to Diff 393122.Dec 9 2021, 5:23 AM
melver marked an inline comment as done.

For completeness, also provide __builtin_alloca_with_align_uninitialized().

clang/lib/CodeGen/CGBuiltin.cpp
3437

You are right, I've added that, too.

glider added a comment.Dec 9 2021, 6:35 AM

For completeness, also provide __builtin_alloca_with_align_uninitialized().

Please make sure to update the patch description.
(You may have done already, but arc doesn't pull them automatically)

melver retitled this revision from [clang][auto-init] Provide __builtin_alloca_uninitialized to Provide __builtin_alloca*_uninitialized variants.Dec 9 2021, 7:33 AM
melver edited the summary of this revision. (Show Details)
melver abandoned this revision.Dec 9 2021, 1:00 PM

GCC devs say that initializing explicit alloca() is a bug, because they aren't "automatic storage": https://lkml.kernel.org/r/20211209201616.GU614@gate.crashing.org
.. which is also the reason why GCC's behaviour differs here at the moment.

I actually agree with that, and alloca auto-init behaviour needs a new -mllvm param.

jfb added a comment.Dec 13 2021, 5:47 PM

GCC devs say that initializing explicit alloca() is a bug, because they aren't "automatic storage": https://lkml.kernel.org/r/20211209201616.GU614@gate.crashing.org
.. which is also the reason why GCC's behaviour differs here at the moment.

I actually agree with that, and alloca auto-init behaviour needs a new -mllvm param.

GCC developer Segher says:

The space allocated by alloca is not an automatic variable, so of course it is not affected by this compiler flag. And it should not, this flag is explicitly for *small fixed-size* stack variables (initialising others can be much too expensive).

C. Introduce a new __builtin_alloca_uninitialized().

That is completely backwards. That is the normal behaviour of alloca already. Also you can get __builtin_alloca inserted by the compiler (for a variable length array for example), and you typically do not want those initialised either, for the same reasons.

Segher is simply wrong.

Let's consult libc's own documentation https://www.gnu.org/software/libc/manual/html_node/Variable-Size-Automatic.html

It states:

Automatic Storage with Variable Size
The function alloca supports a kind of half-dynamic allocation in which blocks are allocated dynamically but freed automatically.

The manpage for alloca(3), states:

This temporary space is automatically freed when the function that called alloca() returns to its caller".

Refer to C17 which says:

Storage durations of objects
An object has a storage duration that determines its lifetime. There are four storage durations: static, thread, automatic, and allocated.

Clearly alloca is neither static, nor thread, nor allocated (see "memory management functions" too). It's automatic storage.

Further, Segher misunderstand the purpose of automatic initialization. The goal is explicitly to change implementation-defined behavior from "the compiler leaves stack and register slots with whatever value it happened to overlap with (potentially leading to info leaks or UaF)" to "the compiler always overwrites previous values that were in stack and register slots before they are reused (preventing a large number of info leaks and UaF)". Saying "the normal behaviour of alloca already" is correct, but it ignores the objective of the flag: to explicitly *change* that behavior. The flag opts-in to that implementation-defined behavior change. And it has a very specific purpose w.r.t. security. This has measurable results in terms of CVEs avoided.

Additionally the flag is not explicitly for small fixed-sized variables, that was stated very clearly when it was committed, and the documentation is unambiguous about this fact. If initialization is too expensive then developers needs to opt-out with mechanisms such as __attribute__((uninitialized)). The support for VLAs and alloca was done very much on purpose, and if developers turn on variable auto-init then they reasonably expect that all automatic variables are automatically initialized, including VLAs and alloca. The patch you propose here is a good idea, because there's currently no way to opt-out for alloca. We should adopt a solution such as this one, and synchronize with GCC so that developers can use the same code.

melver reclaimed this revision.Dec 14 2021, 12:49 AM

Let's consult libc's own documentation https://www.gnu.org/software/libc/manual/html_node/Variable-Size-Automatic.html

It states:

Automatic Storage with Variable Size
The function alloca supports a kind of half-dynamic allocation in which blocks are allocated dynamically but freed automatically.

Thanks for clarifying -- this makes it clear it's "automatic storage". But I think we may still get conflicting views because -ftrivial-auto-var-init speaks about "automatic variables", and strictly speaking __builtin_alloca() is not a "variable".

Further, Segher misunderstand the purpose of automatic initialization. The goal is explicitly to change implementation-defined behavior from "the compiler leaves stack and register slots with whatever value it happened to overlap with (potentially leading to info leaks or UaF)" to "the compiler always overwrites previous values that were in stack and register slots before they are reused (preventing a large number of info leaks and UaF)". Saying "the normal behaviour of alloca already" is correct, but it ignores the objective of the flag: to explicitly *change* that behavior. The flag opts-in to that implementation-defined behavior change. And it has a very specific purpose w.r.t. security. This has measurable results in terms of CVEs avoided.

Additionally the flag is not explicitly for small fixed-sized variables, that was stated very clearly when it was committed, and the documentation is unambiguous about this fact. If initialization is too expensive then developers needs to opt-out with mechanisms such as __attribute__((uninitialized)). The support for VLAs and alloca was done very much on purpose, and if developers turn on variable auto-init then they reasonably expect that all automatic variables are automatically initialized, including VLAs and alloca. The patch you propose here is a good idea, because there's currently no way to opt-out for alloca. We should adopt a solution such as this one, and synchronize with GCC so that developers can use the same code.

I agree that having alloca() initialized is desirable normally.

The problem is that it is not at all obvious if -ftrivial-auto-var-init implies "automatic storage" or "automatic variables", only. (I mixed that up above as well!)

The latter is what (I think) led GCC folks to claim (and convinced me, too) that __builtin_alloca() is out-of-scope. That flag talks too much about "automatic stack variables".

If, however, you declare that it's the former, that's fine too. We just have to be very careful with how we document this -- do we want to replace "automatic stack variable" with "automatic stack storage" in documentation? We have to admit that this part is too easy to misinterpret, which is how we ended up here.

I'm going to reopen this.

@tstellar , we were told maybe you have some input.

TLDR;

  • Clang and GCC appear to behave differently wrt -ftrivial-auto-var-init= on __builtin_alloca() (and VLAs?)
  • Clang's behavior is (per @jfb above) correct;
  • How to document that better, so that this confusion can be avoided, and GCC can match Clang's behaviour.

Thanks.

Hello,

We need a solution for this problem, and because I haven't heard any objections I'll assume the general approach is fine -- @jfb already kindly confirmed that "[...] patch you propose here is a good idea".

Review of the implementation would still be much appreciated!

Many thanks!

glider accepted this revision.Jan 10 2022, 6:50 AM

FWIW the implementation looks good to me.

This revision is now accepted and ready to land.Jan 10 2022, 6:50 AM

Yes, this LGTM implementation-wise.