ASA-2019-00654 – Linux kernel: Memory corruption due to the use of cached fpu_fpregs_owner_ctx


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

CVE-2019-19602 in Ubuntu
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

We are not responsible for any data loss, device corruption or any other type of issue due to the use of any information mentioned in our security alerts.