Skip to content

Commit 75b7332

Browse files
committed
fixes and cleanup
1 parent cceb10a commit 75b7332

9 files changed

Lines changed: 89 additions & 52 deletions

File tree

csrc/binding.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ namespace nb = nanobind;
1919
void do_bench(int result_fd, int input_fd, const std::string& kernel_qualname, const nb::object& test_generator,
2020
const nb::dict& test_kwargs, std::uintptr_t stream, bool discard, bool nvtx, bool landlock, bool mseal,
2121
int supervisor_sock_fd) {
22-
std::mt19937 rng(std::random_device{}());
2322
std::vector<char> signature_bytes(32);
2423
auto config = read_benchmark_parameters(input_fd, signature_bytes.data());
2524
auto mgr = make_benchmark_manager(result_fd, signature_bytes, config.Seed, discard, nvtx, landlock, mseal, supervisor_sock_fd);

csrc/landlock.cpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -211,16 +211,6 @@ void setup_seccomp_filter(scmp_filter_ctx ctx) {
211211
check_seccomp(seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(prctl), 1,
212212
SCMP_A0(SCMP_CMP_EQ, PR_SET_PTRACER)),
213213
"block prctl(SET_PTRACER)");
214-
// TODO figure out what else we can and should block
215-
/*
216-
check_seccomp(seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(mprotect), 1,
217-
SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_WRITE, PROT_WRITE)),
218-
"block mprotect+WRITE");
219-
220-
check_seccomp(seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(pkey_mprotect), 1,
221-
SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_WRITE, PROT_WRITE)),
222-
"block pkey_mprotect+WRITE");
223-
*/
224214
}
225215

226216
void install_seccomp_filter() {

csrc/manager.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ BenchmarkManager::BenchmarkManager(std::byte* arena, std::size_t arena_size,
196196
throw std::runtime_error("Could not open output pipe");
197197
}
198198

199+
if (signature.size() != 32) {
200+
throw std::invalid_argument("Invalid signature length");
201+
}
202+
199203
mNVTXEnabled = nvtx;
200204
mLandlock = landlock;
201205
mSeal = mseal;
@@ -469,7 +473,7 @@ void BenchmarkManager::do_bench_py(
469473
randomize_before_test(actual_calls, rng, stream);
470474
// from this point on, even the benchmark thread won't write to the arena anymore
471475
PROTECT_RANGE(mArena, BenchmarkManagerArenaSize, PROT_READ);
472-
PROTECT_RANGE(mSignature.data(), 4096, PROT_READ); // make the key fully inaccessible
476+
PROTECT_RANGE(mSignature.page_ptr(), 4096, PROT_NONE); // make the key fully inaccessible
473477

474478
std::uniform_int_distribution<unsigned> check_seed_generator(0, 0xffffffff);
475479

@@ -519,9 +523,11 @@ void BenchmarkManager::send_report() {
519523
error_count -= mErrorCountShift;
520524

521525
std::string message = build_result_message(mTestOrder, error_count, mMedianEventTime);
522-
PROTECT_RANGE(mSignature.data(), 4096, PROT_READ);
526+
PROTECT_RANGE(mSignature.page_ptr(), 4096, PROT_READ);
523527
message = encrypt_message(mSignature.data(), 32, message);
524-
PROTECT_RANGE(mSignature.data(), 4096, PROT_NONE);
528+
PROTECT_RANGE(mSignature.page_ptr(), 4096, PROT_WRITE);
529+
cleanse(mSignature.data(), 32);
530+
PROTECT_RANGE(mSignature.page_ptr(), 4096, PROT_NONE);
525531
fwrite(message.data(), 1, message.size(), mOutputPipe);
526532
fflush(mOutputPipe);
527533
}

csrc/obfuscate.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <memory_resource>
99
#include <string>
1010
#include <random>
11+
#include <cstdint>
1112

1213

1314
class ObfuscatedHexDigest {

csrc/protect.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
//
44

55
#pragma once
6+
#include <cstdint>
7+
#include <cstddef>
8+
#include <system_error>
69
#include <unistd.h>
10+
#include <sys/mman.h>
711
#include <sys/syscall.h>
8-
#include <system_error>
912

1013
// Generates an inline mprotect syscall, rounding ptr..ptr+size out to page boundaries,
1114
// and registers the exact address of the syscall instruction in the linker section
@@ -18,6 +21,10 @@
1821
// Because numeric labels are reusable, each expansion of this macro gets its own
1922
// independent "1:" with no clashes, making it safe to use at multiple call sites.
2023
//
24+
// We need to insert to a writeable segment "aw", because the actual instruction
25+
// addresses might only be resolved during load time. The whitelist of locations
26+
// is sent to the supervisor process before any user code runs, so writability
27+
// does not compromise security.
2128
#define PROTECT_RANGE_MARKED(ptr, size, prot) \
2229
do { \
2330
const uintptr_t _page = static_cast<uintptr_t>(getpagesize()); \
@@ -80,5 +87,7 @@
8087
} \
8188
} while(0)
8289

83-
extern unsigned long __start___allowed_mprotect[];
84-
extern unsigned long __stop___allowed_mprotect[];
90+
extern "C" {
91+
extern uintptr_t __start___allowed_mprotect[]; // NOLINT(bugprone-reserved-identifier)
92+
extern uintptr_t __stop___allowed_mprotect[]; // NOLINT(bugprone-reserved-identifier)
93+
}

csrc/protocol.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#pragma once
99
#include <cstdint>
1010

11+
constexpr int MAX_ALLOWED_SITES = 32;
12+
1113
// Sent as regular data before the SCM_RIGHTS message.
1214
// Followed immediately by n_allowed_sites * sizeof(uintptr_t) bytes,
1315
// each being the exact address of an allowed mprotect syscall instruction.
@@ -19,5 +21,5 @@ struct SupervisorSetupMsg {
1921

2022
// Each allowed site is a single pointer — the address of a `syscall` instruction
2123
// registered via PROTECT_RANGE. The supervisor allows mprotect only when the
22-
// instruction pointer falls in [site, site+2).
24+
// instruction pointer is at site+2 (instruction past the syscall?)
2325
using AllowedSite = std::uintptr_t;

csrc/seccomp.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@ static inline void check_seccomp(int rc, const char* what) {
1515
throw std::system_error(-rc, std::generic_category(), what);
1616
}
1717

18+
static void send_all(int sock, const void* buf, size_t len) {
19+
const auto* p = static_cast<const char*>(buf);
20+
while (len > 0) {
21+
ssize_t n = send(sock, p, len, MSG_NOSIGNAL);
22+
if (n < 0) {
23+
if (errno == EINTR) continue;
24+
throw std::system_error(errno, std::system_category(), "send");
25+
}
26+
p += n;
27+
len -= n;
28+
}
29+
}
30+
1831
// ---------------------------------------------------------------------------
1932
// Install a seccomp filter on the calling thread that sends all memory-range
2033
// syscalls to the supervisor via SCMP_ACT_NOTIFY.
@@ -65,21 +78,19 @@ static int install_memory_notify_filter() {
6578
// Send the unotify fd + range to the supervisor over the socketpair.
6679
// ---------------------------------------------------------------------------
6780

68-
struct RangeMsg { uintptr_t lo, hi; };
69-
7081
static void send_unotify_fd(int sock, int unotify_fd,
7182
uintptr_t sensitive_lo, uintptr_t sensitive_hi) {
7283
uint32_t n = __stop___allowed_mprotect - __start___allowed_mprotect;
84+
if (n > MAX_ALLOWED_SITES)
85+
throw std::runtime_error("too many allowed sites");
7386

7487
SupervisorSetupMsg hdr { sensitive_lo, sensitive_hi, n };
7588

7689
// Send header + site array as regular data
77-
if (send(sock, &hdr, sizeof(hdr), MSG_NOSIGNAL) != sizeof(hdr))
78-
throw std::system_error(errno, std::system_category(), "send SupervisorSetupMsg");
90+
send_all(sock, &hdr, sizeof(hdr));
7991

8092
size_t sites_sz = n * sizeof(AllowedSite);
81-
if (send(sock, __start___allowed_mprotect, sites_sz, MSG_NOSIGNAL) != (ssize_t)sites_sz)
82-
throw std::system_error(errno, std::system_category(), "send AllowedSite[]");
93+
send_all(sock, __start___allowed_mprotect, sites_sz);
8394

8495
// Send unotify_fd via SCM_RIGHTS
8596
char dummy = 0;

csrc/supervisor.cpp

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,49 @@
1515
#include "protocol.h"
1616
#include <sys/mman.h>
1717

18+
#ifdef DEBUG_SUPERVISOR
19+
#define dbgprint(...) fprintf(stdout, __VA_ARGS__)
20+
#else
21+
#define dbgprint(...)
22+
#endif
23+
1824
struct Config {
1925
uintptr_t sensitive_lo;
2026
uintptr_t sensitive_hi;
2127
std::vector<AllowedSite> allowed;
2228
};
2329

30+
static void recv_all(int sock, void* buf, size_t len) {
31+
auto* p = static_cast<char*>(buf);
32+
while (len > 0) {
33+
ssize_t n = recv(sock, p, len, MSG_WAITALL);
34+
if (n < 0) {
35+
if (errno == EINTR) continue;
36+
throw std::system_error(errno, std::system_category(), "recv");
37+
}
38+
if (n == 0)
39+
throw std::runtime_error("supervisor: connection closed unexpectedly");
40+
p += n;
41+
len -= n;
42+
}
43+
}
44+
2445
static int recv_setup(int sock, Config& cfg) {
2546
SupervisorSetupMsg setup;
26-
ssize_t n = recv(sock, &setup, sizeof(setup), MSG_WAITALL);
27-
if (n != sizeof(setup)) {
28-
fprintf(stderr, "supervisor: short read on SupervisorSetupMsg\n");
29-
return -1;
30-
}
47+
recv_all(sock, &setup, sizeof(setup));
3148

3249
cfg.sensitive_lo = setup.sensitive_lo;
3350
cfg.sensitive_hi = setup.sensitive_hi;
34-
cfg.allowed.resize(setup.n_allowed_sites);
51+
if (setup.n_allowed_sites > MAX_ALLOWED_SITES)
52+
throw std::runtime_error("supervisor: too many allowed sites");
53+
54+
if (cfg.sensitive_lo >= cfg.sensitive_hi)
55+
throw std::runtime_error("supervisor: invalid sensitive range");
3556

57+
cfg.allowed.resize(setup.n_allowed_sites);
3658
if (setup.n_allowed_sites > 0) {
3759
size_t sites_sz = setup.n_allowed_sites * sizeof(AllowedSite);
38-
n = recv(sock, cfg.allowed.data(), sites_sz, MSG_WAITALL);
39-
if ((size_t)n != sites_sz) {
40-
fprintf(stderr, "supervisor: short read on AllowedSite[]\n");
41-
return -1;
42-
}
60+
recv_all(sock, cfg.allowed.data(), sites_sz);
4361
}
4462

4563
char dummy;
@@ -56,15 +74,16 @@ static int recv_setup(int sock, Config& cfg) {
5674
msg.msg_control = cmsg_buf.buf;
5775
msg.msg_controllen = sizeof(cmsg_buf.buf);
5876

59-
n = recvmsg(sock, &msg, MSG_CMSG_CLOEXEC);
60-
if (n < 0) { perror("supervisor: recvmsg"); return -1; }
77+
ssize_t n = recvmsg(sock, &msg, MSG_CMSG_CLOEXEC);
78+
if (n < 0) {
79+
throw std::system_error(errno, std::system_category(), "recvmsg");
80+
}
6181

6282
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
6383
if (!cmsg || cmsg->cmsg_level != SOL_SOCKET ||
6484
cmsg->cmsg_type != SCM_RIGHTS ||
6585
cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
66-
fprintf(stderr, "supervisor: missing SCM_RIGHTS\n");
67-
return -1;
86+
throw std::runtime_error("supervisor: invalid SCM_RIGHTS");
6887
}
6988

7089
int unotify_fd;
@@ -75,7 +94,10 @@ static int recv_setup(int sock, Config& cfg) {
7594
static bool overlaps(uintptr_t addr, uintptr_t size, uintptr_t lo, uintptr_t hi) {
7695
uintptr_t end = addr + size;
7796
bool wrapped = (end < addr);
78-
return (wrapped || end > lo) && (addr < hi);
97+
if (wrapped) {
98+
return (addr < hi) || (end > lo);
99+
}
100+
return (end > lo) && (addr < hi);
79101
}
80102

81103
static bool ip_is_allowed(uintptr_t ip, const Config& cfg) {
@@ -110,10 +132,7 @@ static bool handle_notification(int unotify_fd, const Config& cfg) {
110132
int prot = (int)req.data.args[2];
111133

112134
bool ip_ok = ip_is_allowed(ip, cfg);
113-
bool contained = (addr >= cfg.sensitive_lo)
114-
&& (len <= cfg.sensitive_hi - cfg.sensitive_lo)
115-
&& (addr + len <= cfg.sensitive_hi)
116-
&& (addr + len >= addr);
135+
bool contained = overlaps(addr, len, cfg.sensitive_lo, cfg.sensitive_hi);
117136
bool prot_safe = prot == PROT_NONE;
118137

119138
if (!contained) {
@@ -124,12 +143,11 @@ static bool handle_notification(int unotify_fd, const Config& cfg) {
124143
// touches our memory, but either makes it PROT_NONE or is from a whitelisted instruction
125144
resp.error = 0;
126145
resp.flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE;
127-
fprintf(stdout, "Allowed mprotect from ip=0x%lx addr=[0x%lx,0x%lx) prot=%d\n",ip, addr, addr + len, prot);
146+
dbgprint("Allowed mprotect from ip=0x%lx addr=[0x%lx,0x%lx) prot=%d\n",ip, addr, addr + len, prot);
128147
} else {
129-
fprintf(stderr,
130-
"supervisor: DENIED syscall %d from ip=0x%lx addr=[0x%lx,0x%lx) prot=%d "
131-
"(ip_ok=%d contained=%d)\n",
132-
req.data.nr, ip, addr, addr + len, prot, ip_ok, contained);
148+
dbgprint("supervisor: DENIED syscall %d from ip=0x%lx addr=[0x%lx,0x%lx) prot=%d "
149+
"(ip_ok=%d contained=%d)\n",
150+
req.data.nr, ip, addr, addr + len, prot, ip_ok, contained);
133151
}
134152

135153
if (ioctl(unotify_fd, SECCOMP_IOCTL_NOTIF_SEND, &resp) < 0)
@@ -151,10 +169,10 @@ int supervisor_main(int sock_fd) {
151169

152170
if (unotify_fd < 0) return 1;
153171

154-
fprintf(stderr, "supervisor: sensitive=[0x%lx, 0x%lx), %zu allowed sites\n",
172+
dbgprint("supervisor: sensitive=[0x%lx, 0x%lx), %zu allowed sites\n",
155173
cfg.sensitive_lo, cfg.sensitive_hi, cfg.allowed.size());
156174
for (AllowedSite site : cfg.allowed)
157-
fprintf(stderr, "supervisor: allowed site: 0x%lx\n", site);
175+
dbgprint("supervisor: allowed site: 0x%lx\n", site);
158176

159177
while (handle_notification(unotify_fd, cfg))
160178
;

exploits/submission_aes_key_recovery.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
EXPLOIT: AES Key Recovery via ObfuscatedHexDigest Arena Scan
33
=============================================================
44
5-
Scans the BenchmarkManager arena (128MiB rw-p anonymous region) for the
5+
Scans the BenchmarkManager arena for the
66
ObfuscatedHexDigest struct. Page pointer is stored as slow_hash(ptr), offset
77
and length as slow_hash(offset) and slow_hash(size ^ offset). Discriminator:
88
slow_unhash(w0) must be page-aligned and canonical user-space (~1/4096 FP rate).
@@ -33,6 +33,7 @@
3333
_PROT_READ = 1
3434
_PAGE_SIZE = 4096
3535
#_ARENA_SIZE = 128 * 1024 * 1024
36+
# ARENA may be split into smaller mappings due to different protection settings for different parts
3637
_F_GETPIPE_SZ = 1025
3738

3839
def _mprotect(addr, prot):

0 commit comments

Comments
 (0)