Skip to content

Commit

Permalink
SharedBuffer: Fix a denial of service
Browse files Browse the repository at this point in the history
It's a very bad idea to increment the refcount on behalf of another
process. That process may (for either benign or evil reasons) not
reference the SharedBuffer, and then we'll be stuck with loads of
SharedBuffers until we OOM.

Instead, increment the refcount when the buffer is mapped. That way, a
buffer is only kept if *someone* has explicitly requested it via
get_shared_buffer.

Fixes SerenityOS#341
  • Loading branch information
rburchell authored and awesomekling committed Jul 19, 2019
1 parent f8beb0f commit 2d4d465
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 10 deletions.
6 changes: 3 additions & 3 deletions Kernel/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2408,7 +2408,7 @@ int Process::sys$create_shared_buffer(int size, void** buffer)
int shared_buffer_id = ++s_next_shared_buffer_id;
auto shared_buffer = make<SharedBuffer>(shared_buffer_id, size);
shared_buffer->share_with(m_pid);
*buffer = shared_buffer->get_address(*this);
*buffer = shared_buffer->ref_for_process_and_get_address(*this);
ASSERT((int)shared_buffer->size() >= size);
#ifdef SHARED_BUFFER_DEBUG
kprintf("%s(%u): Created shared buffer %d @ %p (%u bytes, vmo is %u)\n", name().characters(), pid(), shared_buffer_id, *buffer, size, shared_buffer->size());
Expand Down Expand Up @@ -2447,7 +2447,7 @@ int Process::sys$release_shared_buffer(int shared_buffer_id)
#ifdef SHARED_BUFFER_DEBUG
kprintf("%s(%u): Releasing shared buffer %d, buffer count: %u\n", name().characters(), pid(), shared_buffer_id, shared_buffers().resource().size());
#endif
shared_buffer.release(*this);
shared_buffer.deref_for_process(*this);
return 0;
}

Expand All @@ -2463,7 +2463,7 @@ void* Process::sys$get_shared_buffer(int shared_buffer_id)
#ifdef SHARED_BUFFER_DEBUG
kprintf("%s(%u): Retaining shared buffer %d, buffer count: %u\n", name().characters(), pid(), shared_buffer_id, shared_buffers().resource().size());
#endif
return shared_buffer.get_address(*this);
return shared_buffer.ref_for_process_and_get_address(*this);
}

int Process::sys$seal_shared_buffer(int shared_buffer_id)
Expand Down
10 changes: 6 additions & 4 deletions Kernel/SharedBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ bool SharedBuffer::is_shared_with(pid_t peer_pid)
return false;
}

void* SharedBuffer::get_address(Process& process)
void* SharedBuffer::ref_for_process_and_get_address(Process& process)
{
LOCKER(shared_buffers().lock());
ASSERT(is_shared_with(process.pid()));
for (auto& ref : m_refs) {
if (ref.pid == process.pid()) {
ref.count++;
m_total_refs++;
if (ref.region == nullptr) {
ref.region = process.allocate_region_with_vmo(VirtualAddress(), size(), m_vmo, 0, "SharedBuffer", PROT_READ | (m_writable ? PROT_WRITE : 0));
ref.region->set_shared(true);
Expand All @@ -42,15 +44,15 @@ void SharedBuffer::share_with(pid_t peer_pid)
LOCKER(shared_buffers().lock());
for (auto& ref : m_refs) {
if (ref.pid == peer_pid) {
ref.count++;
// don't increment the reference count yet; let them get_shared_buffer it first.
return;
}
}

m_refs.append(Reference(peer_pid));
}

void SharedBuffer::release(Process& process)
void SharedBuffer::deref_for_process(Process& process)
{
LOCKER(shared_buffers().lock());
for (int i = 0; i < m_refs.size(); ++i) {
Expand Down Expand Up @@ -94,7 +96,7 @@ void SharedBuffer::disown(pid_t pid)
void SharedBuffer::destroy_if_unused()
{
LOCKER(shared_buffers().lock());
if (m_refs.size() == 0) {
if (m_total_refs == 0) {
#ifdef SHARED_BUFFER_DEBUG
kprintf("Destroying unused SharedBuffer{%p} id: %d\n", this, m_shared_buffer_id);
#endif
Expand Down
7 changes: 4 additions & 3 deletions Kernel/SharedBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ struct SharedBuffer {
}

pid_t pid;
unsigned count { 1 };
unsigned count { 0 };
Region* region { nullptr };
};
public:
Expand All @@ -33,9 +33,9 @@ struct SharedBuffer {
}

bool is_shared_with(pid_t peer_pid);
void* get_address(Process& process);
void* ref_for_process_and_get_address(Process& process);
void share_with(pid_t peer_pid);
void release(Process& process);
void deref_for_process(Process& process);
void disown(pid_t pid);
size_t size() const { return m_vmo->size(); }
void destroy_if_unused();
Expand All @@ -45,6 +45,7 @@ struct SharedBuffer {
bool m_writable { true };
NonnullRefPtr<VMObject> m_vmo;
Vector<Reference, 2> m_refs;
unsigned m_total_refs { 0 };
};

Lockable<HashMap<int, OwnPtr<SharedBuffer>>>& shared_buffers();

0 comments on commit 2d4d465

Please sign in to comment.