Skip to content

Commit fd1138d

Browse files
authored
Use MemoryManager to allocate plugin memory (#1322)
In particular, we want it to know about allocated regions so that it doesn't warn when we try to access them.
1 parent b69c0b9 commit fd1138d

7 files changed

Lines changed: 121 additions & 53 deletions

File tree

src/main/bindings/c/bindings-opaque.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ typedef enum QDiscMode {
2525
Q_DISC_MODE_ROUND_ROBIN,
2626
} QDiscMode;
2727

28+
// Memory allocated by Shadow, in a remote address space.
29+
typedef struct AllocdMem_u8 AllocdMem_u8;
30+
2831
// A queue of byte chunks.
2932
typedef struct ByteQueue ByteQueue;
3033

src/main/bindings/c/bindings.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
#include "main/host/thread.h"
2424
#include "main/host/tracker.h"
2525

26+
// Memory allocated by Shadow, in a remote address space.
27+
typedef struct AllocdMem_u8 AllocdMem_u8;
28+
2629
// A queue of byte chunks.
2730
typedef struct ByteQueue ByteQueue;
2831

@@ -250,6 +253,12 @@ struct MemoryManager *memorymanager_new(pid_t pid);
250253
// * `mm` must point to a valid object.
251254
void memorymanager_free(struct MemoryManager *mm);
252255

256+
struct AllocdMem_u8 *allocdmem_new(uintptr_t len);
257+
258+
void allocdmem_free(struct AllocdMem_u8 *allocd_mem);
259+
260+
PluginPtr allocdmem_pluginPtr(const struct AllocdMem_u8 *allocd_mem);
261+
253262
// Initialize the MemoryMapper if it isn't already initialized. `thread` must
254263
// be running and ready to make native syscalls.
255264
void memorymanager_initMapperIfNeeded(struct MemoryManager *memory_manager, Thread *thread);

src/main/bindings/rust/wrapper.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -373,12 +373,6 @@ extern "C" {
373373
...
374374
) -> ::std::os::raw::c_long;
375375
}
376-
extern "C" {
377-
pub fn thread_mallocPluginPtr(thread: *mut Thread, size: size_t) -> PluginPtr;
378-
}
379-
extern "C" {
380-
pub fn thread_freePluginPtr(thread: *mut Thread, ptr: PluginPtr, size: size_t);
381-
}
382376
extern "C" {
383377
pub fn thread_isRunning(thread: *mut Thread) -> bool;
384378
}

src/main/host/memory_manager.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1536,6 +1536,11 @@ impl MemoryManager {
15361536
}
15371537
}
15381538

1539+
/// Which process's address space this MemoryManager manages.
1540+
pub fn pid(&self) -> nix::unistd::Pid {
1541+
self.pid
1542+
}
1543+
15391544
/// Initialize the MemoryMapper, allowing for more efficient access. Needs a
15401545
/// running thread.
15411546
pub fn init_mapper(&mut self, thread: &mut impl Thread) {
@@ -1802,6 +1807,90 @@ impl MemoryManager {
18021807
}
18031808
}
18041809

1810+
/// Memory allocated by Shadow, in a remote address space.
1811+
pub struct AllocdMem<T>
1812+
where
1813+
T: Pod,
1814+
{
1815+
ptr: TypedPluginPtr<T>,
1816+
}
1817+
1818+
impl<T> AllocdMem<T>
1819+
where
1820+
T: Pod,
1821+
{
1822+
/// Allocate memory in the current active process.
1823+
pub fn new(len: usize) -> Self {
1824+
let bytes_len = len * std::mem::size_of::<T>();
1825+
let prot = libc::PROT_READ | libc::PROT_WRITE;
1826+
1827+
// Allocate the memory in the plugin of the active thread. We don't
1828+
// bother with trying to mmap it into Shadow as well; since the memory
1829+
// will typically only be accessed once by Shadow (to write to it),
1830+
// doing so isn't worth the overhead or complexity.
1831+
let ptr = Worker::with_active_thread_mut(|thread| {
1832+
thread
1833+
.native_mmap(
1834+
PluginPtr::from(0usize),
1835+
bytes_len,
1836+
prot,
1837+
libc::MAP_ANONYMOUS | libc::MAP_PRIVATE,
1838+
-1,
1839+
0,
1840+
)
1841+
.unwrap()
1842+
});
1843+
// Add region to known mappings.
1844+
Worker::with_active_process_memory_mut(|mem| {
1845+
if let Some(mapper) = &mut mem.memory_mapper {
1846+
let base = usize::from(ptr);
1847+
let mutations = mapper.regions.insert(
1848+
base..(base + bytes_len),
1849+
Region {
1850+
shadow_base: std::ptr::null_mut(),
1851+
prot,
1852+
sharing: Sharing::Private,
1853+
original_path: None,
1854+
},
1855+
);
1856+
// Shouldn't have overwritten any previous known mappings.
1857+
debug_assert_eq!(mutations.len(), 0);
1858+
}
1859+
});
1860+
Self {
1861+
ptr: TypedPluginPtr::<T>::new(ptr, len).unwrap(),
1862+
}
1863+
}
1864+
1865+
/// Pointer to the allocated memory.
1866+
pub fn ptr(&self) -> TypedPluginPtr<T> {
1867+
self.ptr
1868+
}
1869+
}
1870+
1871+
impl<T> Drop for AllocdMem<T>
1872+
where
1873+
T: Pod,
1874+
{
1875+
fn drop(&mut self) {
1876+
Worker::with_active_thread_mut(|thread| {
1877+
thread
1878+
.native_munmap(self.ptr.ptr(), self.ptr.len())
1879+
.unwrap()
1880+
});
1881+
// Add region to known mappings.
1882+
Worker::with_active_process_memory_mut(|mem| {
1883+
if let Some(mapper) = &mut mem.memory_mapper {
1884+
let base = usize::from(self.ptr.ptr());
1885+
let bytes_len = self.ptr.len() * std::mem::size_of::<T>();
1886+
let mutations = mapper.regions.clear(base..(base + bytes_len));
1887+
// Should've dropped exactly one entry.
1888+
debug_assert_eq!(mutations.len(), 1);
1889+
}
1890+
});
1891+
}
1892+
}
1893+
18051894
mod export {
18061895
use super::*;
18071896

@@ -1821,6 +1910,23 @@ mod export {
18211910
mm.as_mut().map(|mm| Box::from_raw(mm));
18221911
}
18231912

1913+
#[no_mangle]
1914+
pub unsafe extern "C" fn allocdmem_new(len: usize) -> *mut AllocdMem<u8> {
1915+
Box::into_raw(Box::new(AllocdMem::new(len)))
1916+
}
1917+
1918+
#[no_mangle]
1919+
pub unsafe extern "C" fn allocdmem_free(allocd_mem: *mut AllocdMem<u8>) {
1920+
allocd_mem
1921+
.as_mut()
1922+
.map(|allocd_mem| Box::from_raw(allocd_mem));
1923+
}
1924+
1925+
#[no_mangle]
1926+
pub unsafe extern "C" fn allocdmem_pluginPtr(allocd_mem: *const AllocdMem<u8>) -> c::PluginPtr {
1927+
allocd_mem.as_ref().unwrap().ptr().ptr().into()
1928+
}
1929+
18241930
/// Initialize the MemoryMapper if it isn't already initialized. `thread` must
18251931
/// be running and ready to make native syscalls.
18261932
#[no_mangle]

src/main/host/syscall/mman.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,8 @@ static int _syscallhandler_openPluginFile(SysCallHandler* sys, File* file) {
137137
debug("Opening path '%s' in plugin.", mmap_path);
138138

139139
/* Get some memory in the plugin to write the path of the file to open. */
140-
PluginPtr pluginBufPtr = thread_mallocPluginPtr(sys->thread, maplen);
140+
AllocdMem_u8 *allocdMem = allocdmem_new(maplen);
141+
PluginPtr pluginBufPtr = allocdmem_pluginPtr(allocdMem);
141142

142143
/* Get a writeable pointer that can be flushed to the plugin. */
143144
char* pluginBuf = process_getWriteablePtr(sys->process, pluginBufPtr, maplen);
@@ -163,7 +164,7 @@ static int _syscallhandler_openPluginFile(SysCallHandler* sys, File* file) {
163164
}
164165

165166
/* Release the PluginPtr memory. */
166-
thread_freePluginPtr(sys->thread, pluginBufPtr, maplen);
167+
allocdmem_free(allocdMem);
167168
free(mmap_path);
168169

169170
return result;

src/main/host/thread.c

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -139,37 +139,6 @@ long thread_nativeSyscall(Thread* thread, long n, ...) {
139139
return rv;
140140
}
141141

142-
PluginPtr thread_mallocPluginPtr(Thread* thread, size_t size) {
143-
// For now we just implement in terms of thread_nativeSyscall.
144-
// TODO: We might be able to do something more efficient by delegating to
145-
// the specific thread implementation, and/or keeping a persistent
146-
// mmap'd area that we allocate from.
147-
MAGIC_ASSERT(thread);
148-
long ptr = thread_nativeSyscall(thread, SYS_mmap, NULL, size,
149-
PROT_READ | PROT_WRITE,
150-
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
151-
int err = syscall_rawReturnValueToErrno(ptr);
152-
if (err) {
153-
error("thread_nativeSyscall(mmap): %s", strerror(err));
154-
abort();
155-
}
156-
157-
// Should be page-aligned.
158-
utility_assert((ptr % sysconf(_SC_PAGE_SIZE)) == 0);
159-
160-
return (PluginPtr){.val = ptr};
161-
}
162-
163-
void thread_freePluginPtr(Thread* thread, PluginPtr ptr, size_t size) {
164-
MAGIC_ASSERT(thread);
165-
int err =
166-
syscall_rawReturnValueToErrno(thread_nativeSyscall(thread, SYS_munmap, ptr.val, size));
167-
if (err) {
168-
error("thread_nativeSyscall(munmap): %s", strerror(err));
169-
abort();
170-
}
171-
}
172-
173142
int thread_getID(Thread* thread) {
174143
MAGIC_ASSERT(thread);
175144
return thread->tid;

src/main/host/thread.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,6 @@ int thread_getReturnCode(Thread* thread);
3434
// You can map to a corresponding errno value with syscall_rawReturnValueToErrno.
3535
long thread_nativeSyscall(Thread* thread, long n, ...);
3636

37-
// Allocate some memory in the plugin's address space. The returned pointer
38-
// should be freed with `thread_free`.
39-
PluginPtr thread_mallocPluginPtr(Thread* thread, size_t size);
40-
41-
// Free memory allocated with `thread_mallocPluginPtr`. `size` should be the
42-
// original size passed to `thread_mallocPluginPtr`.
43-
//
44-
// TODO: It's a bit unfortunate to have to require the size here, but at this
45-
// time the underlying implementation (based on mmap) needs it. The alternatives
46-
// to this API awkwardness is either for thread_mallocPluginPtr to return an
47-
// opaque struct where this can be squirreled away (a different kind of API
48-
// awkwardness and more boilerplate), or keeping an internal map of ptr->size.
49-
void thread_freePluginPtr(Thread* thread, PluginPtr ptr, size_t size);
50-
5137
bool thread_isRunning(Thread* thread);
5238

5339
uint32_t thread_getProcessId(Thread* thread);

0 commit comments

Comments
 (0)