Skip to content

Commit daef357

Browse files
avagingvisor-bot
authored andcommitted
sentry: don't try to dump/restore unsupported FPU states
If a restore area contains any unsupported state, xrstor triggers a GP exception. With the KVM platform, it crashes the Sentry. PiperOrigin-RevId: 834524036
1 parent d5edeff commit daef357

File tree

6 files changed

+116
-20
lines changed

6 files changed

+116
-20
lines changed

pkg/ring0/entry_amd64.s

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,8 @@
8888
#define PTRACE_FS_BASE 168 // +checkoffset linux PtraceRegs.Fs_base
8989
#define PTRACE_GS_BASE 176 // +checkoffset linux PtraceRegs.Gs_base
9090

91-
// The value for XCR0 is defined to xsave/xrstor everything except for PKRU and
92-
// AMX regions.
93-
// TODO(gvisor.dev/issues/9896): Implement AMX support.
94-
// TODO(gvisor.dev/issues/10087): Implement PKRU support.
95-
#define XCR0_DISABLED_MASK ((1 << 9) | (1 << 17) | (1 << 18))
96-
#define XCR0_EAX (0xffffffff ^ XCR0_DISABLED_MASK)
97-
#define XCR0_EDX 0xffffffff
91+
#define XCR0_EAX 231 // +checkconst fpu SupportedXFeatureStates
92+
#define XCR0_EDX 0
9893

9994
// Saves a register set.
10095
//

pkg/sentry/arch/fpu/fpu_amd64.go

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package fpu
1919

2020
import (
21+
"errors"
2122
"fmt"
2223
"io"
2324

@@ -60,12 +61,37 @@ const (
6061
const (
6162
// XFEATURE_MASK_FPSSE is xsave features that are always enabled in
6263
// signal frame fpstate.
63-
XFEATURE_MASK_FPSSE = 0x3
64+
XFEATURE_MASK_FPSSE = cpuid.XSAVEFeatureX87 | cpuid.XSAVEFeatureSSE
6465

6566
// FXSAVE_AREA_SIZE is the size of the FXSAVE area.
6667
FXSAVE_AREA_SIZE = 512
6768
)
6869

70+
// LINT.IfChange
71+
const (
72+
// SupportedXFeatureStates contains all xfeatures supported right now.
73+
//
74+
// TODO(gvisor.dev/issues/9896): Implement AMX support.
75+
// TODO(gvisor.dev/issues/10087): Implement PKRU support.
76+
SupportedXFeatureStates = cpuid.XSAVEFeatureX87 |
77+
cpuid.XSAVEFeatureSSE |
78+
cpuid.XSAVEFeatureAVX |
79+
cpuid.XSAVEFeatureAVX512op |
80+
cpuid.XSAVEFeatureAVX512zmm0 |
81+
cpuid.XSAVEFeatureAVX512zmm16
82+
83+
// ignoredXFeatureStates contains those xfeatures that may be
84+
// present in a buffer but are safe to skip during restore.
85+
//
86+
// Intel MPX deprecated in the Linux kernel.
87+
// PKRU states could be leaked. This issue was fixed in the 6.6 kernel.
88+
ignoredXFeatureStates = cpuid.XSAVEFeatureBNDREGS |
89+
cpuid.XSAVEFeatureBNDCSR |
90+
cpuid.XSAVEFeaturePKRU
91+
)
92+
93+
// LINT.ThenChange(../../platform/systrap/sysmsg/sysmsg_offsets.h)
94+
6995
// initX86FPState (defined in asm files) sets up initial state.
7096
func initX86FPState(data *byte, useXsave bool)
7197

@@ -261,8 +287,13 @@ func (s *State) PtraceSetXstateRegs(src io.Reader, maxlen int, featureSet cpuid.
261287
return n, nil
262288
}
263289

290+
// ErrUnsupportedStateCleared is reported if an userspace state contains any
291+
// unsupported features.
292+
var ErrUnsupportedStateCleared = errors.New("contains unsupported states")
293+
264294
// SanitizeUser mutates s to ensure that restoring it is safe.
265-
func (s *State) SanitizeUser(featureSet cpuid.FeatureSet) {
295+
func (s *State) SanitizeUser(featureSet cpuid.FeatureSet) error {
296+
var err error
266297
f := *s
267298

268299
// Force reserved bits in MXCSR to 0. This is consistent with Linux.
@@ -271,12 +302,17 @@ func (s *State) SanitizeUser(featureSet cpuid.FeatureSet) {
271302
if len(f) >= minXstateBytes {
272303
// Users can't enable *more* XCR0 bits than what we, and the CPU, support.
273304
xstateBV := hostarch.ByteOrder.Uint64(f[xstateBVOffset:])
274-
xstateBV &= featureSet.ValidXCR0Mask()
305+
xcr0Mask := featureSet.ValidXCR0Mask() & (SupportedXFeatureStates | ignoredXFeatureStates)
306+
if xstateBV&xcr0Mask != xstateBV {
307+
err = ErrUnsupportedStateCleared
308+
}
309+
xstateBV &= featureSet.ValidXCR0Mask() & SupportedXFeatureStates
275310
hostarch.ByteOrder.PutUint64(f[xstateBVOffset:], xstateBV)
276311
// Force XCOMP_BV and reserved bytes in the XSAVE header to 0.
277312
reserved := f[xsaveHeaderZeroedOffset : xsaveHeaderZeroedOffset+xsaveHeaderZeroedBytes]
278313
clear(reserved)
279314
}
315+
return err
280316
}
281317

282318
var (
@@ -357,7 +393,7 @@ func (s *State) AfterLoad() {
357393
supportedBV := fxsaveBV
358394
hostFeatureSet := cpuid.HostFeatureSet()
359395
if hostFeatureSet.UseXsave() {
360-
supportedBV = hostFeatureSet.ValidXCR0Mask()
396+
supportedBV = hostFeatureSet.ValidXCR0Mask() & SupportedXFeatureStates
361397
}
362398

363399
// What was in use?
@@ -367,7 +403,7 @@ func (s *State) AfterLoad() {
367403
}
368404

369405
// Supported features must be a superset of saved features.
370-
if savedBV&^supportedBV != 0 {
406+
if savedBV&^(supportedBV|ignoredXFeatureStates) != 0 {
371407
panic(ErrLoadingState{supportedFeatures: supportedBV, savedFeatures: savedBV})
372408
}
373409

pkg/sentry/arch/signal_amd64.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,10 @@ func (c *Context64) SignalRestore(st *Stack, rt bool, featureSet cpuid.FeatureSe
299299
c.fpState.Reset()
300300
return 0, linux.SignalStack{}, err
301301
}
302-
c.fpState.SanitizeUser(featureSet)
302+
if err := c.fpState.SanitizeUser(featureSet); err != nil {
303+
c.fpState.Reset()
304+
return 0, linux.SignalStack{}, linuxerr.EFAULT
305+
}
303306
}
304307

305308
return uc.Sigset, uc.Stack, nil

pkg/sentry/platform/systrap/sysmsg/sysmsg_offsets.h

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,11 @@
2020
// for the pkg/sentry/platform/systrap/usertrap package.
2121
#define FAULT_OPCODE 0x06
2222

23-
// The value for XCR0 is defined to xsave/xrstor everything except for PKRU and
24-
// AMX regions.
25-
// TODO(gvisor.dev/issues/9896): Implement AMX support.
26-
// TODO(gvisor.dev/issues/10087): Implement PKRU support.
27-
#define XCR0_DISABLED_MASK ((1 << 9) | (1 << 17) | (1 << 18))
28-
#define XCR0_EAX (0xffffffff ^ XCR0_DISABLED_MASK)
29-
#define XCR0_EDX 0xffffffff
23+
// LINT.IfChange
24+
#define XCR0_EAX 0xe7UL
25+
#define XCR0_EDX 0
26+
#define XCR0_DISABLED_MASK (~XCR0_EAX)
27+
// LINT.ThenChange(../../../arch/fpu/fpu_amd64.go)
3028

3129
// LINT.IfChange
3230
#define MAX_FPSTATE_LEN 3584

test/syscalls/linux/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,7 @@ cc_binary(
10011001
"//test/util:test_main",
10021002
"//test/util:test_util",
10031003
"//test/util:thread_util",
1004+
"@com_google_absl//absl/log",
10041005
],
10051006
)
10061007

test/syscalls/linux/fpsig_mut_amd64.cc

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,16 @@
1515
// This program verifies that application floating point state is visible in
1616
// signal frames, and that changes to said state is visible after the signal
1717
// handler returns.
18+
#include <signal.h>
19+
#include <stdint.h>
1820
#include <sys/time.h>
1921
#include <sys/ucontext.h>
22+
#include <ucontext.h>
23+
24+
#include <cstdint>
2025

2126
#include "gtest/gtest.h"
27+
#include "absl/log/log.h"
2228
#include "test/util/test_util.h"
2329
#include "test/util/thread_util.h"
2430

@@ -56,6 +62,19 @@ void sigusr1(int s, siginfo_t* siginfo, void* _uc) {
5662
uc_xmm0->element[0] = static_cast<uint32_t>(kNewFPRegValue);
5763
}
5864

65+
volatile uint64_t testXsaveFeature;
66+
void sigusr2(int s, siginfo_t* siginfo, void* _uc) {
67+
ucontext_t* uc = reinterpret_cast<ucontext_t*>(_uc);
68+
*(uint64_t*)((uint8_t*)uc->uc_mcontext.fpregs + 512) |= 1 << testXsaveFeature;
69+
}
70+
71+
ucontext_t safe_ctx;
72+
volatile bool segvTriggered;
73+
void sigsegv(int s, siginfo_t* siginfo, void* _uc) {
74+
segvTriggered = true;
75+
setcontext(&safe_ctx);
76+
}
77+
5978
TEST(FPSigTest, StateInFrame) {
6079
pid = getpid();
6180
tid = gettid();
@@ -105,6 +124,50 @@ TEST(FPSigTest, StateInFrame) {
105124
EXPECT_EQ(got, kNewFPRegValue);
106125
}
107126

127+
TEST(FPSigTest, XFeaturesInFrame) {
128+
pid = getpid();
129+
tid = gettid();
130+
131+
struct sigaction sa = {};
132+
sigemptyset(&sa.sa_mask);
133+
sa.sa_flags = SA_SIGINFO;
134+
sa.sa_sigaction = sigusr2;
135+
ASSERT_THAT(sigaction(SIGUSR2, &sa, nullptr), SyscallSucceeds());
136+
sa.sa_sigaction = sigsegv;
137+
ASSERT_THAT(sigaction(SIGSEGV, &sa, nullptr), SyscallSucceeds());
138+
139+
for (testXsaveFeature = 0; testXsaveFeature < 64; testXsaveFeature++) {
140+
getcontext(&safe_ctx);
141+
if (segvTriggered) {
142+
LOG(INFO) << testXsaveFeature << " triggered SEGV";
143+
segvTriggered = false;
144+
} else {
145+
asm volatile(
146+
"movl %[pid], %%edi;"
147+
"movl %[tid], %%esi;"
148+
"movl %[sig], %%edx;"
149+
"movl %[killnr], %%eax;"
150+
"syscall;"
151+
:
152+
: [killnr] "i"(__NR_tgkill), [pid] "rm"(pid), [tid] "rm"(tid),
153+
[sig] "i"(SIGUSR2)
154+
: "rax", "rdi", "rsi", "rdx",
155+
// Clobbered by syscall.
156+
"rcx", "r11");
157+
switch (testXsaveFeature) {
158+
case 8: // PT (Processor Trace)
159+
case 13: // HDC (Hardware Debug Controls)
160+
case 17: // LBR (Last Branch Record)
161+
case 18: // CET (Control-flow Enforcement Technology)
162+
case 19: // HRESET (Host Reset)
163+
FAIL() << testXsaveFeature << " didn't trigger a fault";
164+
default:
165+
LOG(INFO) << testXsaveFeature << " has been accepted";
166+
}
167+
}
168+
}
169+
}
170+
108171
} // namespace
109172

110173
} // namespace testing

0 commit comments

Comments
 (0)