<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">Hello security fans!<div class=""><br class=""></div><div class="">I’ve just uploaded a patch proposing opt-in automatic variable initialization. I’d appreciate comments on the overall approach, as well as on the specific implementation.</div><div class=""><br class=""></div><div class="">Here’s the patch:</div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span><a href="https://reviews.llvm.org/D54604" class="">https://reviews.llvm.org/D54604</a></div><div class=""><br class=""></div><div class="">And here’s the description:</div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><div class="">Automatic variable initialization</div><div class=""><br class=""></div><div class="">Add an option to initialize automatic variables with either a pattern or with</div><div class="">zeroes. The default is still that automatic variables are uninitialized. Also</div><div class="">add attributes to request pattern / zero / uninitialized on a per-variable</div><div class="">basis, mainly to disable initialization of large stack arrays when deemed too</div><div class="">expensive.</div><div class=""><br class=""></div><div class="">This isn't meant to change the semantics of C and C++. Rather, it's meant to be</div><div class="">a last-resort when programmers inadvertently have some undefined behavior in</div><div class="">their code. This patch aims to make undefined behavior hurt less, which</div><div class="">security-minded people will be very happy about. Notably, this means that</div><div class="">there's no inadvertent information leak when:</div><div class=""><br class=""></div><div class="">  - The compiler re-uses stack slots, and a value is used uninitialized.</div><div class="">  - The compiler re-uses a register, and a value is used uninitialized.</div><div class="">  - Stack structs / arrays / unions with padding are copied.</div><div class=""><br class=""></div><div class="">This patch only addresses stack and register information leaks. There's many</div><div class="">more infoleaks that we could address, and much more undefined behavior that</div><div class="">could be tamed. Let's keep this patch focused, and I'm happy to address related</div><div class="">issues elsewhere.</div><div class=""><br class=""></div><div class="">To keep the patch simple, only some `undef` is removed for now, see</div><div class="">`replaceUndef`. The padding-related infoleaks are therefore not all gone yet.</div><div class="">This will be addressed in a follow-up, mainly because addressing padding-related</div><div class="">leaks should be a stand-alone option which is implied by variable</div><div class="">initialization.</div><div class=""><br class=""></div><div class="">There are three options when it comes to automatic variable initialization:</div><div class=""><br class=""></div><div class="">  0. Uninitialized</div><div class=""><br class=""></div><div class="">    This is C and C++'s default. It's not changing. Depending on code</div><div class="">    generation, a programmer who runs into undefined behavior by using an</div><div class="">    uninialized automatic variable may observe any previous value (including</div><div class="">    program secrets), or any value which the compiler saw fit to materialize on</div><div class="">    the stack or in a register (this could be to synthesize an immediate, to</div><div class="">    refer to code or data locations, to generate cookies, etc).</div><div class=""><br class=""></div><div class="">  1. Pattern initialization</div><div class=""><br class=""></div><div class="">    This is the recommended initialization approach. Pattern initialization's</div><div class="">    goal is to initialize automatic variables with values which will likely</div><div class="">    transform logic bugs into crashes down the line, are easily recognizable in</div><div class="">    a crash dump, without being values which programmers can rely on for useful</div><div class="">    program semantics. At the same time, pattern initialization tries to</div><div class="">    generate code which will optimize well. You'll find the following details in</div><div class="">    `patternFor`:</div><div class=""><br class=""></div><div class="">    - Integers are initialized with repeated 0xAA bytes (infinite scream).</div><div class="">    - Vectors of integers are also initialized with infinite scream.</div><div class="">    - Pointers are initialized with infinite scream on 64-bit platforms because</div><div class="">      it's an unmappable pointer value on architectures I'm aware of. Pointers</div><div class="">      are initialize to 0x000000AA (small scream) on 32-bit platforms because</div><div class="">      32-bit platforms don't consistently offer unmappable pages. When they do</div><div class="">      it's usually the zero page. As people try this out, I expect that we'll</div><div class="">      want to allow different platforms to customize this, let's do so later.</div><div class="">    - Vectors of pointers are initialized the same way pointers are.</div><div class="">    - Floating point values and vectors are initialized with a vanilla quiet NaN</div><div class="">      (e.g. 0x7ff00000 and 0x7ffe000000000000). We could use other NaNs, say</div><div class="">      0xfffaaaaa (negative NaN, with infinite scream payload). NaNs are nice</div><div class="">      (here, anways) because they propagate on arithmetic, making it more likely</div><div class="">      that entire computations become NaN when a single uninitialized value</div><div class="">      sneaks in.</div><div class="">    - Arrays are initialized to their homogeneous elements' initialization</div><div class="">      value, repeated. Stack-based Variable-Length Arrays (VLAs) are</div><div class="">      runtime-initialized to the allocated size (no effort is made for negative</div><div class="">      size, but zero-sized VLAs are untouched even if technically undefined).</div><div class="">    - Structs are initialized to their heterogeneous element's initialization</div><div class="">      values. Zero-size structs are initialized as 0xAA since they're allocated</div><div class="">      a single byte.</div><div class="">    - Unions are initialized using the initialization for the largest member of</div><div class="">      the union.</div><div class=""><br class=""></div><div class="">    Expect the values used for pattern initialization to change over time, as we</div><div class="">    refine heuristics (both for performance and security). The goal is truly to</div><div class="">    avoid injecting semantics into undefined behavior, and we should be</div><div class="">    comfortable changing these values when there's a worthwhile point in doing</div><div class="">    so.</div><div class=""><br class=""></div><div class="">    Why so much infinite scream? Repeated byte patterns tend to be easy to</div><div class="">    synthesize on most architectures, and otherwise memset is usually very</div><div class="">    efficient. For values which aren't entirely repeated byte patterns, LLVM</div><div class="">    will often generate code which does memset + a few stores.</div><div class=""><br class=""></div><div class="">  2. Zero initialization</div><div class=""><br class=""></div><div class="">    Zero initialize all values. This has the unfortunate side-effect of</div><div class="">    providing semantics to otherwise undefined behavior, programs therefore</div><div class="">    might start to rely on this behavior, and that's sad. However, some</div><div class="">    programmers believe that pattern initialization is too expensive for them,</div><div class="">    and data might show that they're right. The only way to make these</div><div class="">    programmers wrong is to offer zero-initialization as an option, figure out</div><div class="">    where they are right, and optimize the compiler into submission. Until the</div><div class="">    compiler provides acceptable performance for all security-minded code, zero</div><div class="">    initialization is a useful (if blunt) tool.</div><div class=""><br class=""></div><div class="">I've been asked for a fourth initialization option: user-provided byte value.</div><div class="">This might be useful, and can easily be added later.</div><div class=""><br class=""></div><div class="">Why is an out-of band initialization mecanism desired? We could instead use</div><div class="">-Wuninitialized! Indeed we could, but then we're forcing the programmer to</div><div class="">provide semantics for something which doesn't actually have any (it's</div><div class="">uninitialized!). It's then unclear whether `int derp = 0;` lends meaning to `0`,</div><div class="">or whether it's just there to shut that warning up. It's also way easier to use</div><div class="">a compiler flag than it is to manually and intelligently initialize all values</div><div class="">in a program.</div><div class=""><br class=""></div><div class="">Why not just rely on static analysis? Because it cannot reason about all dynamic</div><div class="">code paths effectively, and it has false positives. It's a great tool, could get</div><div class="">even better, but it's simply incapable of catching all uses of uninitialized</div><div class="">values.</div><div class=""><br class=""></div><div class="">Why not just rely on memory sanitizer? Because it's not universally available,</div><div class="">has a 3x performance cost, and shouldn't be deployed in production. Again, it's</div><div class="">a great tool, it'll find the dynamic uses of uninitialized variables that your</div><div class="">test coverage hits, but it won't find the ones that you encounter in production.</div><div class=""><br class=""></div><div class="">What's the performance like? Not too bad! Previous publications [0] have cited</div><div class="">2.7 to 4.5% averages. We've commmitted a few patches over the last few months to</div><div class="">address specific regressions, both in code size and performance. In all cases,</div><div class="">the optimizations are generally useful, but variable initialization benefits</div><div class="">from them a lot more than regular code does. We've got a handful of other</div><div class="">optimizations in mind, but the code is in good enough shape and has found enough</div><div class="">latent issues that it's a good time to get the change reviewed, checked in, and</div><div class="">have others kick the tires. We'll continue reducing overheads as we try this out</div><div class="">on diverse codebases.</div><div class=""><br class=""></div><div class="">Is it a good idea? Security-minded folks think so, and apparently so does the</div><div class="">Microsoft Visual Studio team [1] who say "Between 2017 and mid 2018, this</div><div class="">feature would have killed 49 MSRC cases that involved uninitialized struct data</div><div class="">leaking across a trust boundary. It would have also mitigated a number of bugs</div><div class="">involving uninitialized struct data being used directly.". They seem to use pure</div><div class="">zero initialization, and claim to have taken the overheads down to within noise.</div><div class="">Don't just trust Microsoft though, here's another relevant person asking for</div><div class="">this [2]. It's been proposed for GCC [3] and LLVM [4] before.</div><div class=""><br class=""></div><div class="">What are the caveats? A few!</div><div class=""><br class=""></div><div class="">  - Variables declared in unreachable code, and used later, aren't initialized.</div><div class="">    This goto, Duff's device, other objectionable uses of switch. This should</div><div class="">    instead be a hard-error in any serious codebase.</div><div class="">  - Volatile stack variables are still weird. That's pre-existing, it's really</div><div class="">    the language's fault and this patch keeps it weird. We should deprecate</div><div class="">    volatile [5].</div><div class="">  - As noted above, padding isn't fully handled yet.</div><div class=""><br class=""></div><div class="">I don't think these caveats make the patch untenable because they can be</div><div class="">addressed separately.</div><div class=""><br class=""></div><div class="">Should this be on by default? Maybe, in some circumstances. It's a conversation</div><div class="">we can have when we've tried it out sufficiently, and we're confident that we've</div><div class="">eliminated enough of the overheads that most codebases would want to opt-in.</div><div class="">Let's keep our precious undefined behavior until that point in time.</div><div class=""><br class=""></div><div class="">How do I use it:</div><div class=""><br class=""></div><div class="">  1. On the command-line:</div><div class=""><br class=""></div><div class="">    -ftrivial-auto-var-init=uninitialized (the default)</div><div class="">    -ftrivial-auto-var-init=pattern</div><div class="">    -ftrivial-auto-var-init=zero</div><div class=""><br class=""></div><div class="">  2. Using an attribute:</div><div class=""><br class=""></div><div class="">    int dont_initialize_me __attribute((trivial_auto_init("uninitialized")));</div><div class="">    int zero_me __attribute((trivial_auto_init("zero")));</div><div class="">    int pattern_me __attribute((trivial_auto_init("pattern")));</div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">  [0]: <a href="https://users.elis.ugent.be/~jsartor/researchDocs/OOPSLA2011Zero-submit.pdf" class="">https://users.elis.ugent.be/~jsartor/researchDocs/OOPSLA2011Zero-submit.pdf</a></div><div class="">  [1]: <a href="https://twitter.com/JosephBialek/status/1062774315098112001" class="">https://twitter.com/JosephBialek/status/1062774315098112001</a></div><div class="">  [2]: <a href="https://outflux.net/slides/2018/lss/danger.pdf" class="">https://outflux.net/slides/2018/lss/danger.pdf</a></div><div class="">  [3]: <a href="https://gcc.gnu.org/ml/gcc-patches/2014-06/msg00615.html" class="">https://gcc.gnu.org/ml/gcc-patches/2014-06/msg00615.html</a></div><div class="">  [4]: <a href="https://github.com/AndroidHardeningArchive/platform_external_clang/commit/776a0955ef6686d23a82d2e6a3cbd4a6a882c31c" class="">https://github.com/AndroidHardeningArchive/platform_external_clang/commit/776a0955ef6686d23a82d2e6a3cbd4a6a882c31c</a></div><div class="">  [5]: <a href="http://wg21.link/p1152" class="">http://wg21.link/p1152</a></div></div><div class=""><br class=""></div></body></html>