Skip to content
This repository was archived by the owner on Feb 15, 2025. It is now read-only.

Commit f9e7bb5

Browse files
committed
Add a new RunWithFaultHandler utility
This avoids the need for a global `afterspeculation` label. Use to rewrite the Meltdown example.
1 parent b2091cc commit f9e7bb5

6 files changed

Lines changed: 169 additions & 9 deletions

File tree

ci/test

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,5 @@ run_test() {
3838

3939
run_test timing_array_test
4040
run_test spectre_v1_pht_sa
41+
run_test faults_test
42+

demos/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ add_library(safeside
5050
utils.cc
5151
)
5252

53+
if(UNIX)
54+
target_sources(safeside PRIVATE faults.cc)
55+
endif()
56+
5357
# Configure the assembler. Set ASM_EXT (extension for assembly files) and
5458
# ASM_PLATFORM (target CPU), which we'll use to add the right assembly
5559
# implementation.
@@ -96,6 +100,11 @@ target_sources(
96100
add_executable(timing_array_test timing_array_test.cc)
97101
target_link_libraries(timing_array_test safeside)
98102

103+
if(UNIX)
104+
add_executable(faults_test faults_test.cc)
105+
target_link_libraries(faults_test safeside)
106+
endif()
107+
99108
# Defines an executable target named `demo_name` built from `demo_name.cc` and
100109
# linked against the Safeside support library. The caller can also use the
101110
# SYSTEMS and PROCESSORS keywords to restrict when the target should be

demos/faults.cc

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2020 Google LLC
3+
*
4+
* Licensed under both the 3-Clause BSD License and the GPLv2, found in the
5+
* LICENSE and LICENSE.GPL-2.0 files, respectively, in the root directory.
6+
*
7+
* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
8+
*/
9+
10+
#include "faults.h"
11+
12+
#include <setjmp.h>
13+
14+
#include <cstring>
15+
16+
namespace {
17+
18+
// The sigjmp context we should jump to after catching the fault.
19+
sigjmp_buf signal_handler_jmpbuf;
20+
21+
extern "C"
22+
void SignalHandler(int signal, siginfo_t *info, void *ucontext) {
23+
siglongjmp(signal_handler_jmpbuf, 1);
24+
}
25+
26+
} // namespace
27+
28+
bool RunWithFaultHandler(std::function<void()> inner) {
29+
struct sigaction sa = {};
30+
sa.sa_sigaction = SignalHandler;
31+
32+
struct sigaction oldsa;
33+
// This sets the signal handler for the entire process.
34+
sigaction(SIGSEGV, &sa, &oldsa);
35+
36+
// Use sigsetjmp/siglongjmp to save and restore signal mask. Otherwise we
37+
// will jump out of the signal handler and leave the currently-being-handled
38+
// signal blocked. The result of a SIGSEGV being raised while blocked is
39+
// "undefined"[1], but in practice leads to killing the process.
40+
//
41+
// [1] https://www.man7.org/linux/man-pages/man2/sigprocmask.2.html#NOTES
42+
bool handled_fault = true;
43+
if (sigsetjmp(signal_handler_jmpbuf, 1) == 0) {
44+
inner();
45+
handled_fault = false;
46+
}
47+
48+
sigaction(SIGSEGV, &oldsa, nullptr);
49+
50+
return handled_fault;
51+
}

demos/faults.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2020 Google LLC
3+
*
4+
* Licensed under both the 3-Clause BSD License and the GPLv2, found in the
5+
* LICENSE and LICENSE.GPL-2.0 files, respectively, in the root directory.
6+
*
7+
* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
8+
*/
9+
10+
#ifndef DEMOS_FAULTS_H_
11+
#define DEMOS_FAULTS_H_
12+
13+
#include <signal.h>
14+
15+
#include <functional>
16+
17+
// Run `inner` with a signal handler installed to catch failure signals,
18+
// currently defined as `SIGSEGV`. If such a signal is raised, the execution
19+
// of `inner` is aborted.
20+
//
21+
// Not thread-safe. Don't use from more than one thread at a time.
22+
//
23+
// Returns true iff a signal was handled.
24+
bool RunWithFaultHandler(std::function<void()> inner);
25+
26+
#endif // DEMOS_FAULTS_H_

demos/faults_test.cc

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright 2020 Google LLC
3+
*
4+
* Licensed under both the 3-Clause BSD License and the GPLv2, found in the
5+
* LICENSE and LICENSE.GPL-2.0 files, respectively, in the root directory.
6+
*
7+
* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
8+
*/
9+
10+
#include "faults.h"
11+
12+
#include <iostream>
13+
14+
// Tests that a SIGSEGV is successfully caught and the handler runs.
15+
bool TestHandlesSigsegv() {
16+
bool pass = true;
17+
18+
bool ran_body = false;
19+
bool saw_fault = RunWithFaultHandler([&]() {
20+
ran_body = true;
21+
raise(SIGSEGV);
22+
});
23+
24+
if (!ran_body) {
25+
std::cerr << "Didn't run expected function" << std::endl;
26+
pass = false;
27+
}
28+
if (!saw_fault) {
29+
std::cerr << "Didn't see expected fault" << std::endl;
30+
pass = false;
31+
}
32+
33+
return pass;
34+
}
35+
36+
// Test that the handler doesn't run when no fault occurs.
37+
bool TestNoFault() {
38+
bool pass = true;
39+
40+
bool ran_body = false;
41+
bool saw_fault = RunWithFaultHandler([&]() {
42+
ran_body = true;
43+
});
44+
45+
if (!ran_body) {
46+
std::cerr << "Didn't run expected function" << std::endl;
47+
pass = false;
48+
}
49+
if (saw_fault) {
50+
std::cerr << "Saw unexpected fault" << std::endl;
51+
pass = false;
52+
}
53+
54+
return pass;
55+
}
56+
57+
int main(int argc, char* argv[]) {
58+
bool pass = true;
59+
60+
// Run this test twice to check we reset signal masks correctly.
61+
pass = pass && TestHandlesSigsegv();
62+
pass = pass && TestHandlesSigsegv();
63+
64+
pass = pass && TestNoFault();
65+
66+
std::cout << (pass ? "pass" : "fail") << std::endl;
67+
68+
return !pass;
69+
}

demos/meltdown.cc

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@
2222
#include <fstream>
2323
#include <iostream>
2424

25-
#include <signal.h>
26-
2725
#include "cache_sidechannel.h"
26+
#include "faults.h"
2827
#include "instr.h"
2928
#include "local_content.h"
3029
#include "meltdown_local_content.h"
@@ -54,14 +53,19 @@ static char LeakByte(const char *data, size_t offset) {
5453
// value of the in-bounds access is usually different from the secret value
5554
// we want to leak via out-of-bounds speculative access.
5655
size_t safe_offset = run % strlen(public_data);
57-
ForceRead(oracle.data() + static_cast<size_t>(data[safe_offset]));
5856

59-
// Access attempt to the kernel memory. This does not succeed
60-
// architecturally and kernel sends SIGSEGV instead.
61-
ForceRead(oracle.data() + static_cast<size_t>(data[offset]));
57+
bool handled_fault = RunWithFaultHandler([&]() {
58+
ForceRead(oracle.data() + static_cast<size_t>(data[safe_offset]));
59+
60+
// Access attempt to the kernel memory. This does not succeed
61+
// architecturally and kernel sends SIGSEGV instead.
62+
ForceRead(oracle.data() + static_cast<size_t>(data[offset]));
63+
});
6264

63-
// SIGSEGV signal handler moves the instruction pointer to this label.
64-
asm volatile("afterspeculation:");
65+
if (!handled_fault) {
66+
std::cerr << "Read didn't yield expected fault" << std::endl;
67+
exit(EXIT_FAILURE);
68+
}
6569

6670
std::pair<bool, char> result =
6771
sidechannel.RecomputeScores(data[safe_offset]);
@@ -91,7 +95,6 @@ int main() {
9195
in >> std::dec >> private_length;
9296
in.close();
9397

94-
OnSignalMoveRipToAfterspeculation(SIGSEGV);
9598
std::cout << "Leaking the string: ";
9699
std::cout.flush();
97100
const size_t private_offset =

0 commit comments

Comments
 (0)