Skip to content

Commit 6ef3252

Browse files
authored
Detect when 5 level paging was used during recording but is not available during replay. (#3900)
Support (or lack thereof) for 56 bit addresses is a trace portability hazard that we want to detect up front. We record the maximum width of virtual addresses used in the trace, which is trivially observable from the addresses that pass through AddressSpace::map. Then during replay of a trace that uses 5 level paging we attempt to map something above the standard 47 bit address space, and if it fails, we die with a useful error message.
1 parent 867f95b commit 6ef3252

File tree

13 files changed

+157
-1
lines changed

13 files changed

+157
-1
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,6 +1134,7 @@ set(BASIC_TESTS
11341134
kill_newborn
11351135
kill_ptracee
11361136
landlock
1137+
x86/la57
11371138
large_hole
11381139
large_write_deadlock
11391140
legacy_ugid

src/AddressSpace.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,17 @@ KernelMapping AddressSpace::map(Task* t, remote_ptr<void> addr,
818818
return m;
819819
}
820820

821+
if (t->session().is_recording()) {
822+
uint8_t bits = virtual_address_size(t->arch(), addr + num_bytes - 1);
823+
// NB: Ignore addresses with no leading zeroes on 64 bit architectures
824+
// (where they are in the kernel) but not on 32 bit x86 (where the
825+
// 2-3GB range is available to userspace).
826+
if (bits != 64) {
827+
static_cast<RecordTask*>(t)->
828+
trace_writer().note_virtual_address_size(bits);
829+
}
830+
}
831+
821832
remove_range(dont_fork, MemoryRange(addr, num_bytes));
822833
remove_range(wipe_on_fork, MemoryRange(addr, num_bytes));
823834

src/ReplaySession.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,8 @@ ReplaySession::ReplaySession(const std::string& dir, const Flags& flags)
270270
}
271271

272272
set_intel_pt_enabled(flags.intel_pt_start_checking_event >= 0);
273+
274+
check_virtual_address_size();
273275
}
274276

275277
ReplaySession::ReplaySession(const ReplaySession& other)
@@ -303,6 +305,22 @@ ReplaySession::~ReplaySession() {
303305
DEBUG_ASSERT(emufs().size() == 0);
304306
}
305307

308+
void ReplaySession::check_virtual_address_size() const
309+
{
310+
uint8_t virtual_address_size_needed = trace_in.max_virtual_address_size();
311+
if (virtual_address_size_supported(virtual_address_size_needed)) {
312+
return;
313+
}
314+
315+
if (rr::Flags::get().force_things) {
316+
LOG(warn) << "Virtual address size is unsupported but forcing anyways.";
317+
return;
318+
}
319+
320+
CLEAN_FATAL() << "The trace uses " << (uint32_t)virtual_address_size_needed <<
321+
" bit virtual addresses but this system does not support that size.";
322+
}
323+
306324
ReplaySession::shr_ptr ReplaySession::clone() {
307325
LOG(debug) << "Deepforking ReplaySession " << this << " ...";
308326

src/ReplaySession.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,8 @@ class ReplaySession final : public Session {
380380
ReplaySession(const std::string& dir, const Flags& flags);
381381
ReplaySession(const ReplaySession& other);
382382

383+
void check_virtual_address_size() const;
384+
383385
ReplayTask* revive_task_for_exec();
384386
ReplayTask* setup_replay_one_trace_frame(ReplayTask* t);
385387
void advance_to_next_trace_frame();

src/TraceInfoCommand.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ static int dump_trace_info(const string& trace_dir, FILE* out) {
9797
}
9898
}
9999

100+
uint8_t max_virtual_address_size = trace.max_virtual_address_size();
101+
if (max_virtual_address_size > 0) {
102+
fprintf(out, " \"maxVirtualAddressSize\":%d,\n", max_virtual_address_size);
103+
}
104+
100105
if (!trace.uname().sysname.empty()) {
101106
const auto& uname = trace.uname();
102107
fputs(" \"uname\":{", out);

src/TraceStream.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1366,13 +1366,14 @@ TraceWriter::TraceWriter(const std::string& file_name,
13661366
1),
13671367
ticks_semantics_(ticks_semantics_),
13681368
mmap_count(0),
1369+
max_virtual_address_size(0),
13691370
has_cpuid_faulting_(false),
13701371
xsave_fip_fdp_quirk_(false),
13711372
fdp_exception_only_quirk_(false),
13721373
clear_fip_fdp_(false),
13731374
supports_file_data_cloning_(false),
13741375
chaos_mode(false)
1375-
{
1376+
{
13761377
this->ticks_semantics_ = ticks_semantics_;
13771378

13781379
for (Substream s = SUBSTREAM_FIRST; s < SUBSTREAM_COUNT; ++s) {
@@ -1490,6 +1491,7 @@ void TraceWriter::close(CloseStatus status, const TraceUuid* uuid) {
14901491
header.setExclusionRangeEnd(exclusion_range.end().as_int());
14911492
header.setRuntimePageSize(page_size());
14921493
header.setPreloadLibraryPageSize(PRELOAD_LIBRARY_PAGE_SIZE);
1494+
header.setMaxVirtualAddressSize(max_virtual_address_size);
14931495

14941496
{
14951497
struct utsname uname_buf;
@@ -1651,6 +1653,7 @@ TraceReader::TraceReader(const string& dir)
16511653
preload_thread_locals_recorded_ = header.getPreloadThreadLocalsRecorded();
16521654
ticks_semantics_ = from_trace_ticks_semantics(header.getTicksSemantics());
16531655
rrcall_base_ = header.getRrcallBase();
1656+
max_virtual_address_size_ = header.getMaxVirtualAddressSize();
16541657
syscallbuf_fds_disabled_size_ = header.getSyscallbufFdsDisabledSize();
16551658
syscallbuf_hdr_size_ = header.getSyscallbufHdrSize();
16561659
required_forward_compatibility_version_ = header.getRequiredForwardCompatibilityVersion();
@@ -1745,6 +1748,7 @@ TraceReader::TraceReader(const TraceReader& other)
17451748
xcr0_ = other.xcr0_;
17461749
preload_thread_locals_recorded_ = other.preload_thread_locals_recorded_;
17471750
rrcall_base_ = other.rrcall_base_;
1751+
max_virtual_address_size_ = other.max_virtual_address_size_;
17481752
arch_ = other.arch_;
17491753
chaos_mode_ = other.chaos_mode_;
17501754
chaos_mode_known_ = other.chaos_mode_known_;

src/TraceStream.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <unistd.h>
77

8+
#include <algorithm>
89
#include <map>
910
#include <memory>
1011
#include <set>
@@ -276,6 +277,10 @@ class TraceWriter : public TraceStream {
276277
void set_clear_fip_fdp(bool value) { clear_fip_fdp_ = value; }
277278
bool clear_fip_fdp() const { return clear_fip_fdp_; }
278279
void set_chaos_mode(bool value) { chaos_mode = value; }
280+
void note_virtual_address_size(uint8_t value) {
281+
DEBUG_ASSERT(value < 64);
282+
max_virtual_address_size = std::max(max_virtual_address_size, value);
283+
}
279284

280285
enum CloseStatus {
281286
/**
@@ -330,6 +335,7 @@ class TraceWriter : public TraceStream {
330335
// rename it, so our flock() lock stays held on it.
331336
ScopedFd version_fd;
332337
uint32_t mmap_count;
338+
uint8_t max_virtual_address_size;
333339
bool has_cpuid_faulting_;
334340
bool xsave_fip_fdp_quirk_;
335341
bool fdp_exception_only_quirk_;
@@ -497,6 +503,9 @@ class TraceReader : public TraceStream {
497503
MemoryRange exclusion_range() const {
498504
return exclusion_range_;
499505
}
506+
uint8_t max_virtual_address_size() const {
507+
return max_virtual_address_size_;
508+
}
500509

501510
enum TraceQuirks {
502511
// Whether the /proc/<pid>/mem calls were explicitly recorded in this trace
@@ -537,6 +546,7 @@ class TraceReader : public TraceStream {
537546
bool chaos_mode_known_;
538547
bool chaos_mode_;
539548
int rrcall_base_;
549+
uint8_t max_virtual_address_size_;
540550
uint32_t syscallbuf_fds_disabled_size_;
541551
uint32_t syscallbuf_hdr_size_;
542552
int required_forward_compatibility_version_;

src/kernel_abi.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,4 +385,21 @@ template <typename Arch> static size_t user_fpregs_struct_size_arch() {
385385
size_t user_fpregs_struct_size(SupportedArch arch) {
386386
RR_ARCH_FUNCTION(user_fpregs_struct_size_arch, arch)
387387
}
388+
389+
template <typename Arch> static uint8_t virtual_address_size_arch(remote_ptr<void> ptr) {
390+
return sizeof(typename Arch::unsigned_word) * 8 - Arch::clz_ptr(ptr);
391+
}
392+
393+
uint8_t virtual_address_size(SupportedArch arch, remote_ptr<void> ptr) {
394+
RR_ARCH_FUNCTION(virtual_address_size_arch, arch, ptr)
395+
}
396+
397+
template <typename Arch> static uint8_t default_virtual_address_size_arch() {
398+
return Arch::default_virtual_address_size;
399+
}
400+
401+
uint8_t default_virtual_address_size(SupportedArch arch) {
402+
RR_ARCH_FUNCTION(default_virtual_address_size_arch, arch)
403+
}
404+
388405
}

src/kernel_abi.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,10 @@ struct WordSize32Defs {
291291
uint32_t n_type;
292292
} ElfNhdr;
293293
RR_VERIFY_TYPE_ARCH(RR_NATIVE_ARCH, ::Elf32_Nhdr, ElfNhdr);
294+
295+
static uint8_t clz_ptr(remote_ptr<void> ptr) {
296+
return __builtin_clz(ptr.as_int());
297+
}
294298
};
295299

296300
struct WordSize64Defs {
@@ -387,6 +391,10 @@ struct WordSize64Defs {
387391
uint32_t n_type;
388392
} ElfNhdr;
389393
RR_VERIFY_TYPE_ARCH(RR_NATIVE_ARCH, ::Elf64_Nhdr, ElfNhdr);
394+
395+
static uint8_t clz_ptr(remote_ptr<void> ptr) {
396+
return __builtin_clzl(ptr.as_int());
397+
}
390398
};
391399

392400
/**
@@ -2002,6 +2010,8 @@ struct BaseArch : public wordsize,
20022010
struct X64Arch : public BaseArch<SupportedArch::x86_64, WordSize64Defs> {
20032011
typedef X64Arch Arch64;
20042012

2013+
static const uint8_t default_virtual_address_size = 47;
2014+
20052015
static const size_t elfmachine = EM::X86_64;
20062016
static const size_t elfendian = ELFENDIAN::DATA2LSB;
20072017

@@ -2206,6 +2216,8 @@ struct X64Arch : public BaseArch<SupportedArch::x86_64, WordSize64Defs> {
22062216
struct X86Arch : public BaseArch<SupportedArch::x86, WordSize32Defs> {
22072217
typedef X64Arch Arch64;
22082218

2219+
static const uint8_t default_virtual_address_size = 32;
2220+
22092221
static const size_t elfmachine = EM::I386;
22102222
static const size_t elfendian = ELFENDIAN::DATA2LSB;
22112223

@@ -2462,6 +2474,8 @@ struct GenericArch : public BaseArch<arch_, wordsize> {
24622474
struct ARM64Arch : public GenericArch<SupportedArch::aarch64, WordSize64Defs> {
24632475
typedef ARM64Arch Arch64;
24642476

2477+
static const uint8_t default_virtual_address_size = 47;
2478+
24652479
static const size_t elfmachine = EM::AARCH64;
24662480
static const size_t elfendian = ELFENDIAN::DATA2LSB;
24672481

@@ -2649,6 +2663,13 @@ size_t sigaction_sigset_size(SupportedArch arch);
26492663
size_t user_regs_struct_size(SupportedArch arch);
26502664
size_t user_fpregs_struct_size(SupportedArch arch);
26512665

2666+
/* Returns the number of bits necessary for this particular virtual address. */
2667+
uint8_t virtual_address_size(SupportedArch arch, remote_ptr<void> ptr);
2668+
/* Returns the number of bits supported by default on this architecture for
2669+
* *userspace* virtual addresses.
2670+
*/
2671+
uint8_t default_virtual_address_size(SupportedArch arch);
2672+
26522673
#if defined(__i386__)
26532674
typedef X86Arch NativeArch;
26542675
#elif defined(__x86_64__)

src/rr_trace.capnp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,13 @@ struct Header {
145145
syscallbufHdrSize @26 :UInt32 = 30;
146146
# Result of uname(2). Possibly useful for diagnostics or LLDB qHostInfo.
147147
uname @27 :UtsName;
148+
# The highest virtual address size (in bits) needed to replay this trace. Some
149+
# platforms (e.g. x86-64) have multiple possible virtual address sizes, and
150+
# trace portability requires that traces that require a higher virtual address
151+
# size are not replayed on systems that only support a lower virtual address
152+
# size. A value of 0, only present for traces recorded before this was added,
153+
# means the default value for the relevant arch.
154+
maxVirtualAddressSize @28 :UInt8 = 0;
148155
}
149156

150157
# A file descriptor belonging to a task

0 commit comments

Comments
 (0)