x86/vdso: Prevent segfaults due to hoisted vclock reads [Linux 4.14.137]

This Linux kernel change "x86/vdso: Prevent segfaults due to hoisted vclock reads" is included in the Linux 4.14.137 release. This change is authored by Andy Lutomirski <luto [at] kernel.org> on Fri Jun 21 08:43:04 2019 -0700. The commit for this change in Linux stable tree is 2c04e80 (patch) which is from upstream commit ff17bbe. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream ff17bbe.

x86/vdso: Prevent segfaults due to hoisted vclock reads

commit ff17bbe0bb405ad8b36e55815d381841f9fdeebc upstream.

GCC 5.5.0 sometimes cleverly hoists reads of the pvclock and/or hvclock
pages before the vclock mode checks.  This creates a path through
vclock_gettime() in which no vclock is enabled at all (due to disabled
TSC on old CPUs, for example) but the pvclock or hvclock page
nevertheless read.  This will segfault on bare metal.

This fixes commit 459e3a21535a ("gcc-9: properly declare the
{pv,hv}clock_page storage") in the sense that, before that commit, GCC
didn't seem to generate the offending code.  There was nothing wrong
with that commit per se, and -stable maintainers should backport this to
all supported kernels regardless of whether the offending commit was
present, since the same crash could just as easily be triggered by the
phase of the moon.

On GCC 9.1.1, this doesn't seem to affect the generated code at all, so
I'm not too concerned about performance regressions from this fix.

Cc: [email protected]
Cc: [email protected]
Cc: Borislav Petkov <[email protected]>
Reported-by: Duncan Roe <[email protected]>
Signed-off-by: Andy Lutomirski <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>

There are 15 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 arch/x86/entry/vdso/vclock_gettime.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/arch/x86/entry/vdso/vclock_gettime.c b/arch/x86/entry/vdso/vclock_gettime.c
index 4925593..9f4b108 100644
--- a/arch/x86/entry/vdso/vclock_gettime.c
+++ b/arch/x86/entry/vdso/vclock_gettime.c
@@ -191,13 +191,24 @@ notrace static inline u64 vgetsns(int *mode)

    if (gtod->vclock_mode == VCLOCK_TSC)
        cycles = vread_tsc();
+
+   /*
+    * For any memory-mapped vclock type, we need to make sure that gcc
+    * doesn't cleverly hoist a load before the mode check.  Otherwise we
+    * might end up touching the memory-mapped page even if the vclock in
+    * question isn't enabled, which will segfault.  Hence the barriers.
+    */
 #ifdef CONFIG_PARAVIRT_CLOCK
-   else if (gtod->vclock_mode == VCLOCK_PVCLOCK)
+   else if (gtod->vclock_mode == VCLOCK_PVCLOCK) {
+       barrier();
        cycles = vread_pvclock(mode);
+   }
 #endif
 #ifdef CONFIG_HYPERV_TSCPAGE
-   else if (gtod->vclock_mode == VCLOCK_HVCLOCK)
+   else if (gtod->vclock_mode == VCLOCK_HVCLOCK) {
+       barrier();
        cycles = vread_hvclock(mode);
+   }
 #endif
    else
        return 0;

Leave a Reply

Your email address will not be published. Required fields are marked *