[cfe-dev] [RFC] automatic variable initialization

JF Bastien via cfe-dev cfe-dev at lists.llvm.org
Fri Nov 16 14:38:49 PST 2018


Responding to Kostya and Chandler inline:

> On Nov 15, 2018, at 5:26 PM, Kostya Serebryany <kcc at google.com> wrote:
> 
> Very exciting, and long overdue. Thanks for doing this!
> Countless security bugs would have been mitigated by this, see below. 
> 
> Agree with the rationale: UUMs remain bugs, and we need to try hard to not let developers rely on auto-initialization. 
> (e.g. in future patches we may decide to change the patterns, or to make them different between the runs, etc)

Agreed. Chandler has good suggestions along those lines in his reply.


> All the old goodness (msan, -Wuninitialized, static analyses) is still relevant. 
> 
> I am separately excited with this work because it is essentially a precursor to efficient support for ARM's memory tagging extension (MTE). 
> if we can make enough compiler optimizations to auto-initialize locals with low overhead, then MTE stack instrumentation will come for ~ free. 
> http://llvm.org/devmtg/2018-10/talk-abstracts.html#talk16 <http://llvm.org/devmtg/2018-10/talk-abstracts.html#talk16>
> 
> Does -Wuninitialized still work with -ftrivial-auto-var-init=pattern|zero? 

AFAIK, yes. When you run this:
clang -cc1 test/Sema/uninit-variables.c -fblocks -Wuninitialized -Wconditional-uninitialized -ftrivial-auto-var-init=pattern
You get the same 41 initialization warnings as you do without -ftrivial-auto-var-init={pattern|zero} (i.e. -verify passes).


> In later patches we may need to have flags to separately control auto-init of scalars, PODs, arrays of data, arrays of pointers, etc.
> because in some cases we could achieve 90% of benefit at 10% of cost. 

Maybe? I’m hoping that we can quantify the cost and drive it close enough to zero that you’ll be wrong :-)
Adding the flags and collecting the data as you suggest won’t be hard, but likely not worth doing before we’ve spent some time driving down costs.


> I think that zero-init is going to be substantially cheaper than pattern-init, but happy to be wrong. 

I suspect that you’re right for now, and as I discussed with Tim I’d like to get to a point where you’re happily wrong.


> Here are some links to bugs, vulnerabilities and full exploits based on uses of uninitialized memory. 
> The list is not exhaustive by any means, and we keep finding them every day. 
> The problem is, of course, that we don't find all of them. 

Neat! It’s as if you had that list already, and were waiting to send it out.


> Linux kernel: KMSAN trophies <https://github.com/google/kmsan/wiki/KMSAN-Trophies>, more trophies <https://groups.google.com/forum/#!searchin/syzkaller-bugs/subject$3Akmsan>, CVEs <https://www.cvedetails.com/product/47/Linux-Linux-Kernel.html?vendor_id=33>
> Chrome: 700+ UMRs Chromium found by fuzzing <https://bugs.chromium.org/p/chromium/issues/list?can=1&q=Stability%3DMemory-MemorySanitizer+-status%3ADuplicate+-status%3AWontFix+Use-of-uninitialized-value>
> Android: userspace: CVE-2018-9345/CVE-2018-9346 <https://android.googlesource.com/platform/frameworks/av/+/d6bd6091686dd7ea3b410fb8dce3794429066453>, CVE-2018-9420 <https://android.googlesource.com/platform/system/media/+/12df4b05fd918d836636e21f783df7ad9d5e17a3>, CVE-2018-9421 <https://android.googlesource.com/platform/frameworks/native/+/ff2171f2460e3a6d3443ab957732b8b7d4831d40>, CVE-2017-13252 <https://android.googlesource.com/platform/frameworks/av/+/86141f9df21cb8ac91f9cc9804f5b75d26d98996>; kernel: CVE-2017-9075 <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=fdcee2cbb8438702ea1b328fb6e0ac5e9a40c7f8>, CVE-2017-9076 <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=83eaddab4378db256d00d295bda6ca997cd13a52>, 12% of all bugs (as of 2016 <https://www.blackhat.com/docs/us-16/materials/us-16-Kralevich-The-Art-Of-Defense-How-Vulnerabilities-Help-Shape-Security-Features-And-Mitigations-In-Android.pdf>). 
> OSS: 700+ bugs in various OSS projects found by fuzzing <https://bugs.chromium.org/p/oss-fuzz/issues/list?can=1&q=use-of-uninitialized-value&colspec=ID+Type+Component+Status+Proj+Reported+Owner+Summary&cells=ids>
> Project Zero (P0) findings: ~139 total <https://bugs.chromium.org/p/project-zero/issues/list?can=1&q=uninitialized+&colspec=ID+Status+Restrict+Reported+Vendor+Product+Finder+Summary&cells=ids>.  
> Mozilla: 100+ bugs <https://bugzilla.mozilla.org/buglist.cgi?keywords=csectype-uninitialized%2C%20&keywords_type=allwords&resolution=FIXED&resolution=INVALID&resolution=WONTFIX&resolution=INACTIVE&resolution=DUPLICATE&resolution=WORKSFORME&resolution=INCOMPLETE&resolution=SUPPORT&resolution=EXPIRED&resolution=MOVED&query_format=advanced&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&bug_status=RESOLVED&bug_status=VERIFIED&bug_status=CLOSED>
> "Detecting Kernel Memory Disclosure with x86 Emulation and Taint Tracking" <https://j00ru.vexillium.org/papers/2018/bochspwn_reloaded.pdf> (Sections 3.5 and 6.1.2)
> Leaks of sensitive information
> Linux kernel: 
> P0#1431 <https://bugs.chromium.org/p/project-zero/issues/detail?id=1431>: disclosure of large chunks of kernel memory.
> https://alephsecurity.com/vulns/aleph-2016005 <https://alephsecurity.com/vulns/aleph-2016005>: Android, uninitialized kernel memory leak over USB
> Windows kernel: 
> CVE-2018-8493 <https://menschers.com/2018/10/30/what-is-cve-2018-8493/>
> P0#1276 <https://bugs.chromium.org/p/project-zero/issues/detail?id=1276> (CVE-2017-8685) - a continuous leak of 1kB from the Windows kernel stack, discovered by diffing win32k.sys between Windows 7 and Windows 10. It enabled an attacker to e.g. perform system-wide keyboard sniffing to some extent. Mentioned in P0 blog <https://googleprojectzero.blogspot.com/2017/10/using-binary-diffing-to-discover.html> post about bindiffing.
> P0#1352 <https://bugs.chromium.org/p/project-zero/issues/detail?id=1352> (CVE-2017-11817) - a leak of ~4kB of uninitialized Windows kernel pool memory to NTFS metadata upon mounting the file system, without requiring user interaction. Made it possible to "exfiltrate" kernel memory from a powered-on but locked Windows machine through the USB port.
> P0#1500 <https://bugs.chromium.org/p/project-zero/issues/detail?id=1500> (CVE-2018-1037) - 3 kB of uninitialized user-mode heap memory leaking from Microsoft build servers into a small percentage of .pdb symbol files publicly available through the Microsoft Symbol Server.
> P0#1267 <https://bugs.chromium.org/p/project-zero/issues/detail?id=1267> (CVE-2017-8680) - disclosure of a controlled number of uninitialized bytes from the Windows kernel pool.
> P0#176 <https://bugs.chromium.org/p/project-zero/issues/detail?id=176>, #248 <https://bugs.chromium.org/p/project-zero/issues/detail?id=>, #259 <https://bugs.chromium.org/p/project-zero/issues/detail?id=259>, #277 <https://bugs.chromium.org/p/project-zero/issues/detail?id=277>, #281 <https://bugs.chromium.org/p/project-zero/issues/detail?id=281> (CVE-2015-0089, many other CVEs) - a disclosure of uninitialized user/kernel-mode heap memory in the OpenType glyph outline VM program, which affected the Windows kernel, user-mode DirectWrite and WPF components, Adobe Reader, and Oracle Java. Discussed in detail in a P0 blog post <https://googleprojectzero.blogspot.com/2015/09/enabling-qr-codes-in-internet-explorer.html>.
> User space:
>  *bleed continues <https://scarybeastsecurity.blogspot.com/2017/05/bleed-continues-18-byte-file-14k-bounty.html>
> Leaks of pointers (allows further attacks)
> Windows kernel:	
> P0#825 <https://bugs.chromium.org/p/project-zero/issues/detail?id=825> (CVE-2016-3262) - rendering of uninitialized heap bytes as pixels in EMF files parsed by user-mode Microsoft GDI+. Considered a WontFix by Microsoft until it turned out that Office Online was vulnerable and could leak memory from Microsoft servers, at which point they fixed the bug.
> P0#480 <https://bugs.chromium.org/p/project-zero/issues/detail?id=480> (CVE-2015-2433) - a 0-day Windows kernel memory disclosure that was discovered in the Hacking Team dump in July 2015, and was independently found by ex-P0 member Matt Tait. It was used in an exploit chain to defeat KASLR and reveal the base address of win32k.sys.
> P0#1153 <https://bugs.chromium.org/p/project-zero/issues/detail?id=1153>, #1159 <https://bugs.chromium.org/p/project-zero/issues/detail?id=1159>, #1191 <https://bugs.chromium.org/p/project-zero/issues/detail?id=1191>, #1268 <https://bugs.chromium.org/p/project-zero/issues/detail?id=1268>, #1275 <https://bugs.chromium.org/p/project-zero/issues/detail?id=1275>, #1311 <https://bugs.chromium.org/p/project-zero/issues/detail?id=1311> - various examples of relatively long (~100+ bytes) continuous disclosure of Windows kernel memory, which could be easily used to de-aslr the kernel, leak stack cookies etc.
> MacOS kernel: 
> CVE-2017-2357 <https://nvd.nist.gov/vuln/detail/CVE-2017-2357>, CVE-2017-{13836, 13840, 13841, 13842}, 
> P0#1410 <https://bugs.chromium.org/p/project-zero/issues/detail?id=1410>
> (more of such <https://www.google.com/search?q=%22An+application+may+be+able+to+read+restricted+memory%22+inurl%3Asupport.apple.com&oq=%22An+application+may+be+able+to+read+restricted+memory%22+inurl%3Asupport.apple.com&aqs=chrome..69i57.2441j0j7&sourceid=chrome&ie=UTF-8>)
> User space:
> P0#711 <https://bugs.chromium.org/p/project-zero/issues/detail?id=711>: Android, uninitialized heap memory which could help break ASLR in a privileged process
> Privilege escalation / code execution
> Linux kernel: unauthorized access to IPC objects <https://groups.google.com/forum/#!msg/syzkaller-bugs/j5_w9tlG5Fo/esrHTJw0AwAJ>
> Windows kernel: 
> CVE-2016-0040 <https://blogs.technet.microsoft.com/srd/2017/06/20/tales-from-the-msrc-from-pixels-to-poc/>
> P0#177 <https://bugs.chromium.org/p/project-zero/issues/detail?id=177> (CVE-2015-0090) - off-by-one in the OpenType glyph outline VM in the Windows kernel, which led to arbitrary read/write thanks to accessing uninitialized pointers. Successfully exploited for privilege escalation on Windows 8.1 64-bit, as shown here <https://www.youtube.com/watch?v=FVBSvjYQgq8>.
> MacOS kernel: 
> CVE-2017-2358 <https://nvd.nist.gov/vuln/detail/CVE-2017-2358>
> P0#618 <https://bugs.chromium.org/p/project-zero/issues/detail?id=618> (CVE-2016-1721 <https://support.apple.com/en-us/HT205732>): a local user may be able to execute arbitrary code with kernel privileges


> On Nov 15, 2018, at 5:58 PM, Chandler Carruth <chandlerc at google.com> wrote:
> 
> I'm super excited about all of the non-zeroing options here. I'd actually like to mention some more options that I want to see explored (in future work):
> 
> 1) An enhancement to the pattern suggestion:
> 
> I'd like variables to be initialized to one of N patterns (using the very nice pattern scheme you outline). The N patterns need to *include* zero. The selection of the pattern needs to be very hard to rely on. My suggestion would be to rotate between a shuffled list of patterns for each initialization in the function (even better to do this in LLVM after inlining etc). And shuffle the list of patterns using various inputs: the version of the compiler, some user-provided input (random seed?), and the (mangled) name of the function.

At a high level this is totally doable and pretty neat, I like it.

Details:
Why do you think that it’s important to do after inlining?
It seems like you want a “pattern memset” intrinsic, and we’d let it survive early optimizations? It would have to take in the type so it can treat pointers / FP different from other types. We’d then move the initialization logic from clang to whatever LLVM lowering pass. I guess we’d pass in some nonce too, which clang derives as you’ve suggested (user input, compiler version, mangled name).
Added benefit: non-clang frontends could use this.
I want to avoid making the builds non-reproducible, or rather I’d like users to opt-in to this.
I’m worried that this makes incremental software updates much harder, because the random values change so much. We probably need a way to “stabilize” the randomness.
Security-wise, this is still fairly predictable in that an attacker can disassemble their binary to see which values are where. Agreed it’s less reliable than infinite scream, and it likely is different for different builds. For a JIT it would be great.


> The reason I want this is that I think even all 0xAA can be relied upon inadvertently by programmers. As an example: it will reliably initialize booleans to `true`.
> 
> I would like to see something like this as the default instead of the all-0xAA option.

“Infinite scream” ;-)


> 2) An extension to this pattern (maybe call it "dynamic-pattern") would be to read the pattern (or some of the N patterns) from a buffer initialized at program start time.
> 
> While #2 may imply some overhead, it may be lower than expected -- copying memory is in some weird cases faster than materializing patterns and then setting memory. And it may have some advantages.

Totally agreed. Even just (random) byte-read + broadcast + store should be relatively cheap. I expect that we’d create some weak global that’s initialized with a low init_priority. The linker would make sure there’s just one of those. Each initialization could read a byte from that buffer chosen at compile time, different from the byte other initializations use.


> All of that said:
> 
> On Thu, Nov 15, 2018 at 5:06 PM Tim Northover via cfe-dev <cfe-dev at lists.llvm.org <mailto:cfe-dev at lists.llvm.org>> wrote:
> On Fri, 16 Nov 2018 at 00:42, JF Bastien <jfbastien at apple.com <mailto:jfbastien at apple.com>> wrote:
> > Sounds fair?
> 
> No, it doesn't. It's putting the entire burden on backend optimizers,
> with the goal of removing zero-init at some unspecified future date.
> It's nothing even remotelty approaching a compromise.
> 
> The fragmentation issues need to be considered up front.
> 
> FWIW, I agree about the zero case. I'm deeply concerned about fragmentation here.
> 
> But I *also* really want to be able to get the data and measurements needed to address performance problems with non-zero initialization.
> 
> I would love to see a way to get the zero initialization behavior for performance testing, but *not* expose this as a supported flag to users. I can imagine many ways to do that. Tim, would that address your concerns? In that way, we could actually refuse to support the zero behavior long term by making it much more apparent that it is only intended to gather data.

I expected this to be the main sticking point, and I agree there’s a bunch of ways we can hide the option, or purposefully break in the future. I’m open to suggestions on which approach seems more palatable to people. I absolutely want zero-init for performance measurements in the near-medium term, though.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20181116/5c5c7e36/attachment.html>


More information about the cfe-dev mailing list