mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-23 08:00:20 +00:00
2d4d465206
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 #341
119 lines
3.4 KiB
C++
119 lines
3.4 KiB
C++
#include <Kernel/SharedBuffer.h>
|
|
#include <Kernel/Process.h>
|
|
|
|
Lockable<HashMap<int, OwnPtr<SharedBuffer>>>& shared_buffers()
|
|
{
|
|
static Lockable<HashMap<int, OwnPtr<SharedBuffer>>>* map;
|
|
if (!map)
|
|
map = new Lockable<HashMap<int, OwnPtr<SharedBuffer>>>;
|
|
return *map;
|
|
}
|
|
|
|
bool SharedBuffer::is_shared_with(pid_t peer_pid)
|
|
{
|
|
LOCKER(shared_buffers().lock());
|
|
for (auto& ref : m_refs) {
|
|
if (ref.pid == peer_pid) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
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);
|
|
}
|
|
return ref.region->vaddr().as_ptr();
|
|
}
|
|
}
|
|
ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
void SharedBuffer::share_with(pid_t peer_pid)
|
|
{
|
|
LOCKER(shared_buffers().lock());
|
|
for (auto& ref : m_refs) {
|
|
if (ref.pid == peer_pid) {
|
|
// don't increment the reference count yet; let them get_shared_buffer it first.
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_refs.append(Reference(peer_pid));
|
|
}
|
|
|
|
void SharedBuffer::deref_for_process(Process& process)
|
|
{
|
|
LOCKER(shared_buffers().lock());
|
|
for (int i = 0; i < m_refs.size(); ++i) {
|
|
auto& ref = m_refs[i];
|
|
if (ref.pid == process.pid()) {
|
|
if (--ref.count == 0) {
|
|
#ifdef SHARED_BUFFER_DEBUG
|
|
dbgprintf("Releasing shared buffer reference on %d of size %d by PID %d\n", m_shared_buffer_id, size(), process.pid());
|
|
#endif
|
|
process.deallocate_region(*ref.region);
|
|
m_refs.remove(i);
|
|
#ifdef SHARED_BUFFER_DEBUG
|
|
dbgprintf("Released shared buffer reference on %d of size %d by PID %d\n", m_shared_buffer_id, size(), process.pid());
|
|
#endif
|
|
destroy_if_unused();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SharedBuffer::disown(pid_t pid)
|
|
{
|
|
LOCKER(shared_buffers().lock());
|
|
for (int i = 0; i < m_refs.size(); ++i) {
|
|
auto& ref = m_refs[i];
|
|
if (ref.pid == pid) {
|
|
#ifdef SHARED_BUFFER_DEBUG
|
|
dbgprintf("Disowning shared buffer %d of size %d by PID %d\n", m_shared_buffer_id, size(), pid);
|
|
#endif
|
|
m_refs.remove(i);
|
|
#ifdef SHARED_BUFFER_DEBUG
|
|
dbgprintf("Disowned shared buffer %d of size %d by PID %d\n", m_shared_buffer_id, size(), pid);
|
|
#endif
|
|
destroy_if_unused();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SharedBuffer::destroy_if_unused()
|
|
{
|
|
LOCKER(shared_buffers().lock());
|
|
if (m_total_refs == 0) {
|
|
#ifdef SHARED_BUFFER_DEBUG
|
|
kprintf("Destroying unused SharedBuffer{%p} id: %d\n", this, m_shared_buffer_id);
|
|
#endif
|
|
auto count_before = shared_buffers().resource().size();
|
|
shared_buffers().resource().remove(m_shared_buffer_id);
|
|
ASSERT(count_before != shared_buffers().resource().size());
|
|
}
|
|
}
|
|
|
|
void SharedBuffer::seal()
|
|
{
|
|
LOCKER(shared_buffers().lock());
|
|
m_writable = false;
|
|
for (auto& ref : m_refs) {
|
|
if (ref.region) {
|
|
ref.region->set_writable(false);
|
|
MM.remap_region(*ref.region->page_directory(), *ref.region);
|
|
}
|
|
}
|
|
}
|