Allele Security Alert
ASA-2019-00654
Identifier(s)
ASA-2019-00654, CVE-2019-19602, CID-59c4bd853abc
Title
Memory corruption due to the use of cached fpu_fpregs_owner_ctx
Vendor(s)
Linux foundation
Product(s)
Linux kernel
Affected version(s)
Linux kernel versions 5.4.x before 5.4.2
Linux kernel versions 5.3.x before 5.3.15
Linux kernel versions since the following commit:
x86/fpu: Defer FPU state load until return to userspace
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5f409e20b794565e2d60ad333e79334630a6c798
Fixed version(s)
Linux kernel version 5.4.2
Linux kernel version 5.3.15
Linux kernel versions with the following commit:
x86/fpu: Don’t cache access to fpu_fpregs_owner_ctx
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=59c4bd853abcea95eccc167a7d7fd5f1a5f47b98
Proof of concept
Yes
Description
fpregs_state_valid in arch/x86/include/asm/fpu/internal.h in the Linux kernel, when GCC 9 is used, allows context-dependent attackers to cause a denial of service (memory corruption) or possibly have unspecified other impact because of incorrect fpu_fpregs_owner_ctx caching, as demonstrated by mishandling of signal-based non-cooperative preemption in Go 1.14 prereleases on amd64.
Technical details
The state/owner of the FPU is saved to fpu_fpregs_owner_ctx by pointing to the context that is currently loaded. It never changed during the lifetime of a task – it remained stable/constant.
After deferred FPU registers loading until return to userland was implemented, the content of fpu_fpregs_owner_ctx may change during preemption and must not be cached.
This went unnoticed for some time and was now noticed, in particular since gcc 9 is caching that load in copy_fpstate_to_sigframe() and reusing it in the retry loop:
copy_fpstate_to_sigframe() load fpu_fpregs_owner_ctx and save on stack fpregs_lock() copy_fpregs_to_sigframe() /* failed */ fpregs_unlock() *** PREEMPTION, another uses FPU, changes fpu_fpregs_owner_ctx *** fault_in_pages_writeable() /* succeed, retry */ fpregs_lock() __fpregs_load_activate() fpregs_state_valid() /* uses fpu_fpregs_owner_ctx from stack */ copy_fpregs_to_sigframe() /* succeeds, random FPU content */
This is a comparison of the assembly produced by gcc 9, without vs with this patch:
| # arch/x86/kernel/fpu/signal.c:173: if (!access_ok(buf, size)) | cmpq %rdx, %rax # tmp183, _4 | jb .L190 #, |-# arch/x86/include/asm/fpu/internal.h:512: return fpu == this_cpu_read_stable(fpu_fpregs_owner_ctx) && cpu == fpu->last_cpu; |-#APP |-# 512 "arch/x86/include/asm/fpu/internal.h" 1 |- movq %gs:fpu_fpregs_owner_ctx,%rax #, pfo_ret__ |-# 0 "" 2 |-#NO_APP |- movq %rax, -88(%rbp) # pfo_ret__, %sfp … |-# arch/x86/include/asm/fpu/internal.h:512: return fpu == this_cpu_read_stable(fpu_fpregs_owner_ctx) && cpu == fpu->last_cpu; |- movq -88(%rbp), %rcx # %sfp, pfo_ret__ |- cmpq %rcx, -64(%rbp) # pfo_ret__, %sfp |+# arch/x86/include/asm/fpu/internal.h:512: return fpu == this_cpu_read(fpu_fpregs_owner_ctx) && cpu == fpu->last_cpu; |+#APP |+# 512 "arch/x86/include/asm/fpu/internal.h" 1 |+ movq %gs:fpu_fpregs_owner_ctx(%rip),%rax # fpu_fpregs_owner_ctx, pfo_ret__ |+# 0 "" 2 |+# arch/x86/include/asm/fpu/internal.h:512: return fpu == this_cpu_read(fpu_fpregs_owner_ctx) && cpu == fpu->last_cpu; |+#NO_APP |+ cmpq %rax, -64(%rbp) # pfo_ret__, %sfp
Credits
The Go Team
Reference(s)
AVX register corruption from signal delivery
https://bugzilla.kernel.org/show_bug.cgi?id=205663
x86/fpu: Don’t cache access to fpu_fpregs_owner_ctx
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=59c4bd853abcea95eccc167a7d7fd5f1a5f47b98
x86/fpu: Don’t cache access to fpu_fpregs_owner_ctx
https://github.com/torvalds/linux/commit/59c4bd853abcea95eccc167a7d7fd5f1a5f47b98
x86/fpu: Defer FPU state load until return to userspace
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5f409e20b794565e2d60ad333e79334630a6c798
x86/fpu: Defer FPU state load until return to userspace
https://github.com/torvalds/commit/5f409e20b794565e2d60ad333e79334630a6c798
runtime: memory corruption on Linux 5.2+ #35777
https://github.com/golang/go/issues/35777
AVX register corruption from signal delivery
https://lore.kernel.org/lkml/c87e93c3-5f30-f242-74b7-6c7ccc91158a@google.com/
Linux 5.4.2
https://cdn.kernel.org/pub/linux/kernel/v5.x/ChangeLog-5.4.2
Linux 5.3.15
https://cdn.kernel.org/pub/linux/kernel/v5.x/ChangeLog-5.3.15
CVE-2019-19602 - Red Hat Customer Portal
https://access.redhat.com/security/cve/CVE-2019-19602
CVE-2019-19602
https://security-tracker.debian.org/tracker/CVE-2019-19602
CVE-2019-19602 | SUSE
https://www.suse.com/security/cve/CVE-2019-19602
https://people.canonical.com/~ubuntu-security/cve/CVE-2019-19602.html
CVE-2019-19602
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-19602
CVE-2019-19602
https://nvd.nist.gov/vuln/detail/CVE-2019-19602
If there is any error in this alert or you wish a comprehensive analysis, let us know.
Last modified: December 19, 2019