mirror of https://github.com/PCSX2/pcsx2.git
IPC: perform memory checks, virtually no perf hit
This commit is contained in:
parent
89ce774d7e
commit
439ed9617c
|
@ -169,24 +169,24 @@ char* SocketIPC::MakeFailIPC(char* ret_buffer)
|
||||||
SocketIPC::IPCBuffer SocketIPC::ParseCommand(char* buf, char* ret_buffer)
|
SocketIPC::IPCBuffer SocketIPC::ParseCommand(char* buf, char* ret_buffer)
|
||||||
{
|
{
|
||||||
// currently all our instructions require a running VM so we check once
|
// currently all our instructions require a running VM so we check once
|
||||||
// here, will help perf when/if we implement multi-ipc processing in one
|
// here, slightly helps performance
|
||||||
// socket roundtrip.
|
|
||||||
if (!m_vm->HasActiveMachine())
|
if (!m_vm->HasActiveMachine())
|
||||||
return IPCBuffer{1, MakeFailIPC(ret_buffer)};
|
return IPCBuffer{1, MakeFailIPC(ret_buffer)};
|
||||||
|
|
||||||
|
|
||||||
u16 batch = 1;
|
u16 batch = 1;
|
||||||
u32 ret_cnt = 1;
|
u32 ret_cnt = 1;
|
||||||
|
u32 buf_cnt = 0;
|
||||||
if ((IPCCommand)buf[0] == MsgMultiCommand)
|
if ((IPCCommand)buf[0] == MsgMultiCommand)
|
||||||
{
|
{
|
||||||
batch = FromArray<u16>(buf, 1);
|
batch = FromArray<u16>(&buf[buf_cnt], 1);
|
||||||
buf += 3;
|
buf_cnt += 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (u16 i = 0; i < batch; i++)
|
for (u16 i = 0; i < batch; i++)
|
||||||
{
|
{
|
||||||
// YY YY YY YY from schema below
|
// YY YY YY YY from schema below
|
||||||
u32 a = FromArray<u32>(buf, 1);
|
u32 a = FromArray<u32>(&buf[buf_cnt], 1);
|
||||||
|
|
||||||
// IPC Message event (1 byte)
|
// IPC Message event (1 byte)
|
||||||
// | Memory address (4 byte)
|
// | Memory address (4 byte)
|
||||||
|
@ -197,82 +197,87 @@ SocketIPC::IPCBuffer SocketIPC::ParseCommand(char* buf, char* ret_buffer)
|
||||||
// | return value (VLE)
|
// | return value (VLE)
|
||||||
// | |
|
// | |
|
||||||
// reply: XX ZZ ZZ ZZ ZZ
|
// reply: XX ZZ ZZ ZZ ZZ
|
||||||
//
|
switch ((IPCCommand)buf[buf_cnt])
|
||||||
// NB: memory safety checking would be very expansive in our case,
|
|
||||||
// implemented per command and simply a mess. As our threat model is
|
|
||||||
// nonexistant, knowing we can disable the IPC at any time and having
|
|
||||||
// checks client-side it simply makes more sense to not do check
|
|
||||||
// server-side, as bad as this sounds.
|
|
||||||
// Re security threat model: we control the entire emulated memory of
|
|
||||||
// the emulated game, we can DoS our emulator easily by abusing the IPC
|
|
||||||
// features already, and regardless of where you read this there's
|
|
||||||
// probably no sandbox implemented, so simply being able to write to the
|
|
||||||
// game memory code region would make us control the JIT and have probably
|
|
||||||
// full access over the host machine.
|
|
||||||
switch ((IPCCommand)buf[0])
|
|
||||||
{
|
{
|
||||||
case MsgRead8:
|
case MsgRead8:
|
||||||
{
|
{
|
||||||
|
if (!SafetyChecks(buf_cnt, 5, ret_cnt, 1))
|
||||||
|
goto error;
|
||||||
u8 res;
|
u8 res;
|
||||||
res = memRead8(a);
|
res = memRead8(a);
|
||||||
ToArray(ret_buffer, res, ret_cnt);
|
ToArray(ret_buffer, res, ret_cnt);
|
||||||
ret_cnt += 1;
|
ret_cnt += 1;
|
||||||
buf += 5;
|
buf_cnt += 5;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MsgRead16:
|
case MsgRead16:
|
||||||
{
|
{
|
||||||
|
if (!SafetyChecks(buf_cnt, 5, ret_cnt, 2))
|
||||||
|
goto error;
|
||||||
u16 res;
|
u16 res;
|
||||||
res = memRead16(a);
|
res = memRead16(a);
|
||||||
ToArray(ret_buffer, res, ret_cnt);
|
ToArray(ret_buffer, res, ret_cnt);
|
||||||
ret_cnt += 2;
|
ret_cnt += 2;
|
||||||
buf += 5;
|
buf_cnt += 5;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MsgRead32:
|
case MsgRead32:
|
||||||
{
|
{
|
||||||
|
if (!SafetyChecks(buf_cnt, 5, ret_cnt, 4))
|
||||||
|
goto error;
|
||||||
u32 res;
|
u32 res;
|
||||||
res = memRead32(a);
|
res = memRead32(a);
|
||||||
ToArray(ret_buffer, res, ret_cnt);
|
ToArray(ret_buffer, res, ret_cnt);
|
||||||
ret_cnt += 4;
|
ret_cnt += 4;
|
||||||
buf += 5;
|
buf_cnt += 5;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MsgRead64:
|
case MsgRead64:
|
||||||
{
|
{
|
||||||
|
if (!SafetyChecks(buf_cnt, 5, ret_cnt, 8))
|
||||||
|
goto error;
|
||||||
u64 res;
|
u64 res;
|
||||||
memRead64(a, &res);
|
memRead64(a, &res);
|
||||||
ToArray(ret_buffer, res, ret_cnt);
|
ToArray(ret_buffer, res, ret_cnt);
|
||||||
ret_cnt += 8;
|
ret_cnt += 8;
|
||||||
buf += 5;
|
buf_cnt += 5;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MsgWrite8:
|
case MsgWrite8:
|
||||||
{
|
{
|
||||||
memWrite8(a, FromArray<u8>(buf, 5));
|
if (!SafetyChecks(buf_cnt, 6, ret_cnt))
|
||||||
buf += 6;
|
goto error;
|
||||||
|
memWrite8(a, FromArray<u8>(&buf[buf_cnt], 5));
|
||||||
|
buf_cnt += 6;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MsgWrite16:
|
case MsgWrite16:
|
||||||
{
|
{
|
||||||
memWrite16(a, FromArray<u16>(buf, 5));
|
if (!SafetyChecks(buf_cnt, 7, ret_cnt))
|
||||||
buf += 7;
|
goto error;
|
||||||
|
memWrite16(a, FromArray<u16>(&buf[buf_cnt], 5));
|
||||||
|
buf_cnt += 7;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MsgWrite32:
|
case MsgWrite32:
|
||||||
{
|
{
|
||||||
memWrite32(a, FromArray<u32>(buf, 5));
|
if (!SafetyChecks(buf_cnt, 9, ret_cnt))
|
||||||
buf += 9;
|
goto error;
|
||||||
|
memWrite32(a, FromArray<u32>(&buf[buf_cnt], 5));
|
||||||
|
buf_cnt += 9;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MsgWrite64:
|
case MsgWrite64:
|
||||||
{
|
{
|
||||||
memWrite64(a, FromArray<u64>(buf, 5));
|
if (!SafetyChecks(buf_cnt, 13, ret_cnt))
|
||||||
buf += 13;
|
goto error;
|
||||||
|
memWrite64(a, FromArray<u64>(&buf[buf_cnt], 5));
|
||||||
|
buf_cnt += 13;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
|
error:
|
||||||
return IPCBuffer{1, MakeFailIPC(ret_buffer)};
|
return IPCBuffer{1, MakeFailIPC(ret_buffer)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
18
pcsx2/IPC.h
18
pcsx2/IPC.h
|
@ -42,18 +42,17 @@ protected:
|
||||||
int m_sock = 0;
|
int m_sock = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum memory used by an IPC message request.
|
* Maximum memory used by an IPC message request.
|
||||||
* Equivalent to 50,000 Write64 requests.
|
* Equivalent to 50,000 Write64 requests.
|
||||||
*/
|
*/
|
||||||
const unsigned int MAX_IPC_SIZE = 650000;
|
#define MAX_IPC_SIZE 650000
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum memory used by an IPC message reply.
|
* Maximum memory used by an IPC message reply.
|
||||||
* Equivalent to 50,000 Read64 replies.
|
* Equivalent to 50,000 Read64 replies.
|
||||||
*/
|
*/
|
||||||
const unsigned int MAX_IPC_RETURN_SIZE = 450000;
|
#define MAX_IPC_RETURN_SIZE 450000
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IPC return buffer.
|
* IPC return buffer.
|
||||||
|
@ -154,6 +153,19 @@ protected:
|
||||||
return *(T*)(arr + i);
|
return *(T*)(arr + i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures an IPC message isn't too big.
|
||||||
|
* return value: false if checks failed, true otherwise.
|
||||||
|
*/
|
||||||
|
static inline bool SafetyChecks(u32 command_len, int command_size, u32 reply_len, int reply_size = 0)
|
||||||
|
{
|
||||||
|
bool res = ((command_len + command_size) >= MAX_IPC_SIZE ||
|
||||||
|
(reply_len + reply_size) >= MAX_IPC_RETURN_SIZE);
|
||||||
|
if (unlikely(res))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/* Initializers */
|
/* Initializers */
|
||||||
SocketIPC(SysCoreThread* vm);
|
SocketIPC(SysCoreThread* vm);
|
||||||
|
|
Loading…
Reference in New Issue