fix: prevent IPC slot reuse during object teardown

This commit is contained in:
2026-07-30 17:46:38 +01:00
parent 471903bafb
commit a965e67c93
3 changed files with 56 additions and 11 deletions
+1 -1
View File
@@ -12,4 +12,4 @@
#pragma once #pragma once
#define MONTAUK_BUILD_NUMBER 24 #define MONTAUK_BUILD_NUMBER 27
+49 -9
View File
@@ -44,6 +44,7 @@ namespace Ipc {
struct Object { struct Object {
HandleType type; HandleType type;
bool active; bool active;
bool destroying;
uint32_t refs; uint32_t refs;
kcp::Mutex lock; kcp::Mutex lock;
}; };
@@ -267,9 +268,38 @@ namespace Ipc {
static void InitObject(Object& object, HandleType type) { static void InitObject(Object& object, HandleType type) {
object.type = type; object.type = type;
object.active = true; object.active = true;
object.destroying = false;
object.refs = 0; object.refs = 0;
} }
static kcp::Mutex* PoolLockForType(HandleType type) {
switch (type) {
case HandleType::Stream: return &g_streamPoolLock;
case HandleType::Mailbox: return &g_mailboxPoolLock;
case HandleType::File: return &g_filePoolLock;
case HandleType::Socket: return &g_socketPoolLock;
case HandleType::Surface: return &g_surfacePoolLock;
case HandleType::Process: return &g_processPoolLock;
case HandleType::Waitset: return &g_waitsetPoolLock;
default: return nullptr;
}
}
// An object is hidden from users as soon as its last reference disappears,
// but its pool slot must not become reusable until the type-specific
// destructor has finished. Otherwise a concurrent allocator can install a
// new backend in the slot and the old destructor will tear down the new
// object (observed as open() succeeding immediately before getsize() saw
// an invalid file handle).
static void FinishDestroy(Object* object, HandleType type) {
kcp::Mutex* poolLock = PoolLockForType(type);
if (poolLock) poolLock->Acquire();
object->lock.Acquire();
object->destroying = false;
object->lock.Release();
if (poolLock) poolLock->Release();
}
static void RetainRawObject(Object* object) { static void RetainRawObject(Object* object) {
if (object == nullptr) return; if (object == nullptr) return;
object->lock.Acquire(); object->lock.Acquire();
@@ -376,6 +406,7 @@ namespace Ipc {
destroy = true; destroy = true;
type = object->type; type = object->type;
object->active = false; object->active = false;
object->destroying = true;
} }
object->lock.Release(); object->lock.Release();
@@ -405,6 +436,7 @@ namespace Ipc {
default: default:
break; break;
} }
FinishDestroy(object, type);
} }
static uint32_t CurrentSignalsForSnapshot(HandleType type, Object* object, uint32_t rights); static uint32_t CurrentSignalsForSnapshot(HandleType type, Object* object, uint32_t rights);
@@ -540,11 +572,15 @@ namespace Ipc {
if (stream->refs == 0) { if (stream->refs == 0) {
destroy = true; destroy = true;
stream->active = false; stream->active = false;
stream->destroying = true;
} }
stream->lock.Release(); stream->lock.Release();
if (notify) NotifyObjectChanged((Object*)stream); if (notify) NotifyObjectChanged((Object*)stream);
if (destroy) DestroyStream(stream); if (destroy) {
DestroyStream(stream);
FinishDestroy((Object*)stream, HandleType::Stream);
}
break; break;
} }
case HandleType::Mailbox: { case HandleType::Mailbox: {
@@ -562,11 +598,15 @@ namespace Ipc {
if (mailbox->refs == 0) { if (mailbox->refs == 0) {
destroy = true; destroy = true;
mailbox->active = false; mailbox->active = false;
mailbox->destroying = true;
} }
mailbox->lock.Release(); mailbox->lock.Release();
if (notify) NotifyObjectChanged((Object*)mailbox); if (notify) NotifyObjectChanged((Object*)mailbox);
if (destroy) DestroyMailbox(mailbox); if (destroy) {
DestroyMailbox(mailbox);
FinishDestroy((Object*)mailbox, HandleType::Mailbox);
}
break; break;
} }
default: default:
@@ -660,7 +700,7 @@ namespace Ipc {
g_streamPoolLock.Acquire(); g_streamPoolLock.Acquire();
for (int i = 0; i < MaxStreams; i++) { for (int i = 0; i < MaxStreams; i++) {
if (g_streams[i].active) continue; if (g_streams[i].active || g_streams[i].destroying) continue;
InitObject(g_streams[i], HandleType::Stream); InitObject(g_streams[i], HandleType::Stream);
g_streams[i].buffer = (uint8_t*)buffer; g_streams[i].buffer = (uint8_t*)buffer;
g_streams[i].capacity = (uint32_t)numPages * 0x1000u; g_streams[i].capacity = (uint32_t)numPages * 0x1000u;
@@ -841,7 +881,7 @@ namespace Ipc {
Mailbox* CreateMailbox() { Mailbox* CreateMailbox() {
g_mailboxPoolLock.Acquire(); g_mailboxPoolLock.Acquire();
for (int i = 0; i < MaxMailboxes; i++) { for (int i = 0; i < MaxMailboxes; i++) {
if (g_mailboxes[i].active) continue; if (g_mailboxes[i].active || g_mailboxes[i].destroying) continue;
InitObject(g_mailboxes[i], HandleType::Mailbox); InitObject(g_mailboxes[i], HandleType::Mailbox);
g_mailboxes[i].head = 0; g_mailboxes[i].head = 0;
g_mailboxes[i].tail = 0; g_mailboxes[i].tail = 0;
@@ -1106,7 +1146,7 @@ namespace Ipc {
g_filePoolLock.Acquire(); g_filePoolLock.Acquire();
for (int i = 0; i < MaxFiles; i++) { for (int i = 0; i < MaxFiles; i++) {
if (g_files[i].active) continue; if (g_files[i].active || g_files[i].destroying) continue;
InitObject(g_files[i], HandleType::File); InitObject(g_files[i], HandleType::File);
g_files[i].backend = backend; g_files[i].backend = backend;
g_filePoolLock.Release(); g_filePoolLock.Release();
@@ -1173,7 +1213,7 @@ namespace Ipc {
static Socket* AllocateSocketObject(int type) { static Socket* AllocateSocketObject(int type) {
g_socketPoolLock.Acquire(); g_socketPoolLock.Acquire();
for (int i = 0; i < MaxSockets; i++) { for (int i = 0; i < MaxSockets; i++) {
if (g_sockets[i].active) continue; if (g_sockets[i].active || g_sockets[i].destroying) continue;
InitObject(g_sockets[i], HandleType::Socket); InitObject(g_sockets[i], HandleType::Socket);
g_sockets[i].socketType = type; g_sockets[i].socketType = type;
g_sockets[i].tcpConn = nullptr; g_sockets[i].tcpConn = nullptr;
@@ -1505,7 +1545,7 @@ namespace Ipc {
g_surfacePoolLock.Acquire(); g_surfacePoolLock.Acquire();
int slot = -1; int slot = -1;
for (int i = 0; i < MaxSurfaces; i++) { for (int i = 0; i < MaxSurfaces; i++) {
if (!g_surfaces[i].active) { if (!g_surfaces[i].active && !g_surfaces[i].destroying) {
slot = i; slot = i;
break; break;
} }
@@ -1927,7 +1967,7 @@ namespace Ipc {
g_processPoolLock.Acquire(); g_processPoolLock.Acquire();
for (int i = 0; i < MaxProcessObjects; i++) { for (int i = 0; i < MaxProcessObjects; i++) {
if (g_processObjects[i].active) continue; if (g_processObjects[i].active || g_processObjects[i].destroying) continue;
InitObject(g_processObjects[i], HandleType::Process); InitObject(g_processObjects[i], HandleType::Process);
g_processObjects[i].pid = pid; g_processObjects[i].pid = pid;
g_processObjects[i].exited = false; g_processObjects[i].exited = false;
@@ -2127,7 +2167,7 @@ namespace Ipc {
g_waitsetPoolLock.Acquire(); g_waitsetPoolLock.Acquire();
for (int i = 0; i < MaxWaitsets; i++) { for (int i = 0; i < MaxWaitsets; i++) {
if (g_waitsets[i].active) continue; if (g_waitsets[i].active || g_waitsets[i].destroying) continue;
InitObject(g_waitsets[i], HandleType::Waitset); InitObject(g_waitsets[i], HandleType::Waitset);
for (int j = 0; j < MaxWaitsetEntries; j++) { for (int j = 0; j < MaxWaitsetEntries; j++) {
g_waitsets[i].entries[j].used = false; g_waitsets[i].entries[j].used = false;
+6 -1
View File
@@ -121,8 +121,13 @@ struct TrueTypeFont {
return false; return false;
} }
montauk::read(fd, data, 0, size); int read_result = montauk::read(fd, data, 0, size);
montauk::close(fd); montauk::close(fd);
if (read_result < 0 || (uint64_t)read_result != size) {
montauk::free(data);
data = nullptr;
return false;
}
// stbtt_GetFontOffsetForIndex returns -1 for non-font data; passing // stbtt_GetFontOffsetForIndex returns -1 for non-font data; passing
// that into stbtt_InitFont makes it dereference data + (uint32)-1. // that into stbtt_InitFont makes it dereference data + (uint32)-1.