-
Notifications
You must be signed in to change notification settings - Fork 35
Expand file tree
/
Copy pathucontext.h
More file actions
175 lines (156 loc) · 7.07 KB
/
ucontext.h
File metadata and controls
175 lines (156 loc) · 7.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
// Copyright 2022 The SiliFuzz Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIRD_PARTY_SILIFUZZ_UTIL_UCONTEXT_UCONTEXT_H_
#define THIRD_PARTY_SILIFUZZ_UTIL_UCONTEXT_UCONTEXT_H_
#include <cstdint>
#include "./util/arch.h"
#include "./util/ucontext/ucontext_types.h"
// Saves current CPU register state into *ucontext.
//
// Very similar to getcontext() from libc, but unlike it:
// * We use a datastruct that (unlike ucontext_t) has only the saved
// register state.
// * We save eflags.
// * We save all registers including rax.
// * We save all segment registers.
// Note: We can't write CS and SS segment registers in RestoreUContext();
// in practice, cs and ss have non-0 values and the others (ds,es,fs,gs)
// have 0 values.
// * We fully restore the register state and eflags before exiting
// SaveUContext() inlcuding rax.
// Thus calling SaveUContext() results in a very minimal register changes
// for the caller: `ucontext` gets written to rdi as part of calling
// SaveUContext() and rip gets advanced.
//
// CAVEAT: SaveUContext() must aways be followed by ZeroOut(FP)RegsPadding()
// or FixUp(FP)RegsPadding() if one cares about correctness of everything in
// ucontext->fpregs.
//
// CAVEAT: SaveUContext() does not set all the bytes in UContext.
// If you need that guarantee for certain portions of UContext,
// either 0-initialize UContext or call ZeroOutRegsPadding() after calling
// SaveUContext().
extern "C" void SaveUContext(silifuzz::UContext<silifuzz::Host>* ucontext)
__attribute__((__returns_twice__));
// Similar to above but does not make any syscalls. On x86_64, FS_BASE, GS_BASE
// are not saved.
extern "C" void SaveUContextNoSyscalls(
silifuzz::UContext<silifuzz::Host>* ucontext)
__attribute__((__returns_twice__));
// Restores CPU register state from `view`
// This never returns, instead execution continues with the *exact same*
// register state as described by `view`.
//
// Very similar to setcontext() from libc, but unlike it:
// * We use a different datastruct.
// * We restore eflags.
// * We restore all registers including rax.
// * We do not restore CS and SS segment registers.
//
// REQUIRES: the %rsp value set by `view` points to a writable memory
// region that has at least 32 bytes. Those 32 bytes get overwritten as part of
// executing RestoreUContextView() with values of eflags and %rip.
//
// Note that this writing is not a problem for matching end-state expectations
// during snapshot execution since we capture the snapshot's expected memory
// end-state with the effects of those 16 bytes written present (note that
// snapshot execution itself may overwrite those bytes - they are in the free
// portion of its stack).
extern "C" void RestoreUContextView(
const silifuzz::UContextView<silifuzz::Host>& view)
__attribute__((__noreturn__));
// Similar to above but does not make any syscalls. On x86_64, FS_BASE, GS_BASE
// and are not restored.
//
// * CAVEAT * This restores FS and GS selectors. For user mode, the only
// allowed values are the null selectors, which cause the segement bases to
// be reset. If the callee depends on either FS or GS, e.g. TLS pointer in
// FS base, callee needs to set the segment bases separately.
extern "C" void RestoreUContextViewNoSyscalls(
const silifuzz::UContextView<silifuzz::Host>& view)
__attribute__((__noreturn__));
// Convenient functions that restore from a UContext object instead of
// a view.
static inline void __attribute__((__noreturn__)) RestoreUContext(
const silifuzz::UContext<silifuzz::Host>* ucontext) {
RestoreUContextView(silifuzz::UContextView<silifuzz::Host>(*ucontext));
}
static inline void __attribute__((__noreturn__)) RestoreUContextNoSyscalls(
const silifuzz::UContext<silifuzz::Host>* ucontext) {
RestoreUContextViewNoSyscalls(
silifuzz::UContextView<silifuzz::Host>(*ucontext));
}
// ========================================================================= //
namespace silifuzz {
// Zeroes-our padding/unused portions of the register portions of UContext
// (ucontext->gregs and ucontext->fpregs) w.r.t. SaveUContext() -- the latter
// does not write those areas.
// Each is async-signal-safe.
template <typename Arch>
void ZeroOutGRegsPadding(GRegSet<Arch>* gregs);
template <typename Arch>
void ZeroOutFPRegsPadding(FPRegSet<Arch>* fpregs);
template <typename Arch>
void ZeroOutRegsPadding(UContext<Arch>* ucontext) {
ZeroOutGRegsPadding(&ucontext->gregs);
ZeroOutFPRegsPadding(&ucontext->fpregs);
}
// Part of ZeroOutFPRegsPadding() that needs to happen after SaveUContext()
// or ConvertFPRegsFromLibC() from ./convert.h (of course ZeroOutFPRegsPadding()
// or ZeroOutRegsPadding() themselves can be done after SaveUContext() instead).
// It fixes up a part of what SaveUContext() and ucontext_t-creation code
// in the kernel actually write.
template <typename Arch>
void FixUpGRegsPadding(GRegSet<Arch>* gregs);
template <typename Arch>
void FixUpFPRegsPadding(FPRegSet<Arch>* fpregs);
template <typename Arch>
void FixUpRegsPadding(UContext<Arch>* ucontext) {
FixUpGRegsPadding(&ucontext->gregs);
FixUpFPRegsPadding(&ucontext->fpregs);
}
// Returns true iff corresponding zeroing has been done on the arg.
// Has simple, not most-efficient impl: meant for (D)CHECK-s.
template <typename Arch>
bool HasZeroGRegsPadding(const GRegSet<Arch>& gregs) {
GRegSet<Arch> copy = gregs;
ZeroOutGRegsPadding(©);
return copy == gregs;
}
template <typename Arch>
bool HasZeroFPRegsPadding(const FPRegSet<Arch>& fpregs) {
FPRegSet<Arch> copy = fpregs;
ZeroOutFPRegsPadding(©);
return copy == fpregs;
}
template <typename Arch>
bool HasZeroRegsPadding(const UContext<Arch>& ucontext) {
return HasZeroGRegsPadding(ucontext.gregs) &&
HasZeroFPRegsPadding(ucontext.fpregs);
}
// RestoreUContext may not restore every register to its original state for
// architecture-specific reasons. Some of these registers may cause difficulties
// if they have changed between the time the context was saved and the time it
// was restored. Check that these registers have not changed and that it is safe
// to restore the context.
// Returns true iff specific registers in `actual` have the same values as the
// corresponding registers in `expected`.
template <typename Arch>
bool CriticalUnrestoredRegistersAreSame(const GRegSet<Arch>& actual,
const GRegSet<Arch>& expected);
// Returns the instruction pointer that points right after the
// call into CurrentInstructionPointer(). Test-only uses so far.
int64_t CurrentInstructionPointer();
} // namespace silifuzz
#endif // THIRD_PARTY_SILIFUZZ_UTIL_UCONTEXT_UCONTEXT_H_