IPC: return an error when packet is too big

This commit is contained in:
Gauvain 'GovanifY' Roussel-Tarbouriech 2020-08-31 05:53:40 +02:00 committed by tellowkrinkle
parent a0aaad7ff8
commit ad1cdc9122
3 changed files with 109 additions and 112 deletions

View File

@ -45,7 +45,6 @@ SocketIPC::SocketIPC(SysCoreThread* vm)
WSADATA wsa; WSADATA wsa;
SOCKET new_socket; SOCKET new_socket;
struct sockaddr_in server, client; struct sockaddr_in server, client;
int c;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
@ -120,6 +119,20 @@ SocketIPC::SocketIPC(SysCoreThread* vm)
Start(); Start();
} }
char* SocketIPC::MakeOkIPC(char* ret_buffer, uint32_t size = 5)
{
ToArray<uint32_t>(ret_buffer, size, 0);
ret_buffer[4] = IPC_OK;
return ret_buffer;
}
char* SocketIPC::MakeFailIPC(char* ret_buffer, uint32_t size = 5)
{
ToArray<uint32_t>(ret_buffer, size, 0);
ret_buffer[4] = IPC_FAIL;
return ret_buffer;
}
void SocketIPC::ExecuteTaskInThread() void SocketIPC::ExecuteTaskInThread()
{ {
m_end = false; m_end = false;
@ -168,55 +181,49 @@ void SocketIPC::ExecuteTaskInThread()
// either int or ssize_t depending on the platform, so we have to // either int or ssize_t depending on the platform, so we have to
// use a bunch of auto // use a bunch of auto
auto receive_length = read_portable(m_msgsock, m_ipc_buffer, MAX_IPC_SIZE); auto receive_length = 0;
if (receive_length > 0) auto end_length = 4;
// while we haven't received the entire packet, maybe due to
// socket datagram splittage, we continue to read
while (receive_length < end_length)
{ {
// if we already received at least the length then we read it auto tmp_length = read_portable(m_msgsock, &m_ipc_buffer[receive_length], MAX_IPC_SIZE - receive_length);
auto end_length = 4;
if (receive_length >= end_length) // we close the connection if an error happens
if (tmp_length <= 0)
{ {
end_length = FromArray<u32>(m_ipc_buffer, 0); receive_length = 0;
if (end_length > MAX_IPC_SIZE) break;
end_length = MAX_IPC_SIZE;
} }
// while we haven't received the entire packet, maybe due to receive_length += tmp_length;
// socket datagram splittage, we continue to read
while (receive_length < end_length)
{
auto tmp_length = read_portable(m_msgsock, &m_ipc_buffer[receive_length], MAX_IPC_SIZE - receive_length);
// we close the connection if an error happens // if we got at least the final size then update
if (tmp_length <= 0) if (end_length == 4 && receive_length >= 4)
{
end_length = FromArray<u32>(m_ipc_buffer, 0);
// we'd like to avoid a client trying to do OOB
if (end_length > MAX_IPC_SIZE)
{ {
receive_length = 0; receive_length = 0;
break; break;
} }
receive_length += tmp_length;
// if we got at least the final size then update
if (end_length == 4 && receive_length >= 4)
{
end_length = FromArray<u32>(m_ipc_buffer, 0);
// we'd like to avoid a client trying to do OOB
if (end_length > MAX_IPC_SIZE)
end_length = MAX_IPC_SIZE;
}
} }
}
SocketIPC::IPCBuffer res;
if (receive_length == 0) // we remove 4 bytes to get the message size out of the IPC command
break; // size in ParseCommand
if (receive_length == 0)
res = IPCBuffer{5, MakeFailIPC(m_ret_buffer)};
else
res = ParseCommand(&m_ipc_buffer[4], m_ret_buffer, (u32)receive_length - 4);
// we remove 4 bytes to get the message size out of the IPC command // we don't care about the error value as we will reset the
// size // connection after that anyways
SocketIPC::IPCBuffer res = ParseCommand(&m_ipc_buffer[4], m_ret_buffer, (u32)receive_length - 4); if (write_portable(m_msgsock, res.buffer, res.size) < 0)
{
// we don't care about the error value as we will reset the
// connection after that anyways
if (write_portable(m_msgsock, res.buffer, res.size) < 0)
{
}
} }
} }
close_portable(m_msgsock); close_portable(m_msgsock);
@ -244,20 +251,6 @@ SocketIPC::~SocketIPC()
DESTRUCTOR_CATCHALL DESTRUCTOR_CATCHALL
} }
char* SocketIPC::MakeOkIPC(char* ret_buffer, uint32_t size = 5)
{
ToArray<uint32_t>(ret_buffer, size, 0);
ret_buffer[4] = IPC_OK;
return ret_buffer;
}
char* SocketIPC::MakeFailIPC(char* ret_buffer, uint32_t size = 5)
{
ToArray<uint32_t>(ret_buffer, size, 0);
ret_buffer[4] = IPC_FAIL;
return ret_buffer;
}
SocketIPC::IPCBuffer SocketIPC::ParseCommand(char* buf, char* ret_buffer, u32 buf_size) SocketIPC::IPCBuffer SocketIPC::ParseCommand(char* buf, char* ret_buffer, u32 buf_size)
{ {
// currently all our instructions require a running VM so we check once // currently all our instructions require a running VM so we check once
@ -278,7 +271,7 @@ SocketIPC::IPCBuffer SocketIPC::ParseCommand(char* buf, char* ret_buffer, u32 bu
// YY YY YY YY from schema below // YY YY YY YY from schema below
// curently always used by implemented commands so it is out of the // curently always used by implemented commands so it is out of the
// loop // loop
u32 a = FromArray<u32>(&buf[buf_cnt], 1); const 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)
@ -295,8 +288,7 @@ SocketIPC::IPCBuffer SocketIPC::ParseCommand(char* buf, char* ret_buffer, u32 bu
{ {
if (!SafetyChecks(buf_cnt, 5, ret_cnt, 1, buf_size)) if (!SafetyChecks(buf_cnt, 5, ret_cnt, 1, buf_size))
goto error; goto error;
u8 res; const u8 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_cnt += 5; buf_cnt += 5;
@ -306,8 +298,7 @@ SocketIPC::IPCBuffer SocketIPC::ParseCommand(char* buf, char* ret_buffer, u32 bu
{ {
if (!SafetyChecks(buf_cnt, 5, ret_cnt, 2, buf_size)) if (!SafetyChecks(buf_cnt, 5, ret_cnt, 2, buf_size))
goto error; goto error;
u16 res; const u16 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_cnt += 5; buf_cnt += 5;
@ -317,8 +308,7 @@ SocketIPC::IPCBuffer SocketIPC::ParseCommand(char* buf, char* ret_buffer, u32 bu
{ {
if (!SafetyChecks(buf_cnt, 5, ret_cnt, 4, buf_size)) if (!SafetyChecks(buf_cnt, 5, ret_cnt, 4, buf_size))
goto error; goto error;
u32 res; const u32 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_cnt += 5; buf_cnt += 5;
@ -328,7 +318,7 @@ SocketIPC::IPCBuffer SocketIPC::ParseCommand(char* buf, char* ret_buffer, u32 bu
{ {
if (!SafetyChecks(buf_cnt, 5, ret_cnt, 8, buf_size)) if (!SafetyChecks(buf_cnt, 5, ret_cnt, 8, buf_size))
goto error; goto error;
u64 res; u64 res = 0;
memRead64(a, &res); memRead64(a, &res);
ToArray(ret_buffer, res, ret_cnt); ToArray(ret_buffer, res, ret_cnt);
ret_cnt += 8; ret_cnt += 8;

View File

@ -47,36 +47,36 @@ protected:
/** /**
* 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.
*/ */
#define 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.
*/ */
#define MAX_IPC_RETURN_SIZE 450000 #define MAX_IPC_RETURN_SIZE 450000
/** /**
* IPC return buffer. * IPC return buffer.
* A preallocated buffer used to store all IPC replies. * A preallocated buffer used to store all IPC replies.
* to the size of 50.000 MsgWrite64 IPC calls. * to the size of 50.000 MsgWrite64 IPC calls.
*/ */
char* m_ret_buffer; char* m_ret_buffer;
/** /**
* IPC messages buffer. * IPC messages buffer.
* A preallocated buffer used to store all IPC messages. * A preallocated buffer used to store all IPC messages.
*/ */
char* m_ipc_buffer; char* m_ipc_buffer;
/** /**
* IPC Command messages opcodes. * IPC Command messages opcodes.
* A list of possible operations possible by the IPC. * A list of possible operations possible by the IPC.
* Each one of them is what we call an "opcode" and is the first * Each one of them is what we call an "opcode" and is the first
* byte sent by the IPC to differentiate between commands. * byte sent by the IPC to differentiate between commands.
*/ */
enum IPCCommand : unsigned char enum IPCCommand : unsigned char
{ {
MsgRead8 = 0, /**< Read 8 bit value to memory. */ MsgRead8 = 0, /**< Read 8 bit value to memory. */
@ -92,9 +92,9 @@ protected:
/** /**
* IPC message buffer. * IPC message buffer.
* A list of all needed fields to store an IPC message. * A list of all needed fields to store an IPC message.
*/ */
struct IPCBuffer struct IPCBuffer
{ {
int size; /**< Size of the buffer. */ int size; /**< Size of the buffer. */
@ -102,11 +102,11 @@ protected:
}; };
/** /**
* IPC result codes. * IPC result codes.
* A list of possible result codes the IPC can send back. * A list of possible result codes the IPC can send back.
* Each one of them is what we call an "opcode" or "tag" and is the * Each one of them is what we call an "opcode" or "tag" and is the
* first byte sent by the IPC to differentiate between results. * first byte sent by the IPC to differentiate between results.
*/ */
enum IPCResult : unsigned char enum IPCResult : unsigned char
{ {
IPC_OK = 0, /**< IPC command successfully completed. */ IPC_OK = 0, /**< IPC command successfully completed. */
@ -119,28 +119,33 @@ protected:
// Thread used to relay IPC commands. // Thread used to relay IPC commands.
void ExecuteTaskInThread(); void ExecuteTaskInThread();
/* Internal function, Parses an IPC command. /**
* buf: buffer containing the IPC command. * Internal function, Parses an IPC command.
* buf_size: size of the buffer announced. * buf: buffer containing the IPC command.
* ret_buffer: buffer that will be used to send the reply. * buf_size: size of the buffer announced.
* return value: IPCBuffer containing a buffer with the result * ret_buffer: buffer that will be used to send the reply.
* of the command and its size. */ * return value: IPCBuffer containing a buffer with the result
* of the command and its size.
*/
IPCBuffer ParseCommand(char* buf, char* ret_buffer, u32 buf_size); IPCBuffer ParseCommand(char* buf, char* ret_buffer, u32 buf_size);
/* Formats an IPC buffer /**
* ret_buffer: return buffer to use. * Formats an IPC buffer
* size: size of the IPC buffer. * ret_buffer: return buffer to use.
* return value: buffer containing the status code allocated of size * size: size of the IPC buffer.
* size */ * return value: buffer containing the status code allocated of size
*/
static inline char* MakeOkIPC(char* ret_buffer, uint32_t size); static inline char* MakeOkIPC(char* ret_buffer, uint32_t size);
static inline char* MakeFailIPC(char* ret_buffer, uint32_t size); static inline char* MakeFailIPC(char* ret_buffer, uint32_t size);
/* Converts an uint to an char* in little endian /**
* res_array: the array to modify * Converts an uint to an char* in little endian
* res: the value to convert * res_array: the array to modify
* i: when to insert it into the array * res: the value to convert
* return value: res_array * i: when to insert it into the array
* NB: implicitely inlined */ * return value: res_array
* NB: implicitely inlined
*/
template <typename T> template <typename T>
static char* ToArray(char* res_array, T res, int i) static char* ToArray(char* res_array, T res, int i)
{ {
@ -148,11 +153,13 @@ protected:
return res_array; return res_array;
} }
/* Converts a char* to an uint in little endian /**
* arr: the array to convert * Converts a char* to an uint in little endian
* i: when to load it from the array * arr: the array to convert
* return value: the converted value * i: when to load it from the array
* NB: implicitely inlined */ * return value: the converted value
* NB: implicitely inlined
*/
template <typename T> template <typename T>
static T FromArray(char* arr, int i) static T FromArray(char* arr, int i)
{ {
@ -160,9 +167,9 @@ protected:
} }
/** /**
* Ensures an IPC message isn't too big. * Ensures an IPC message isn't too big.
* return value: false if checks failed, true otherwise. * 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, u32 buf_size = MAX_IPC_SIZE - 1) static inline bool SafetyChecks(u32 command_len, int command_size, u32 reply_len, int reply_size = 0, u32 buf_size = MAX_IPC_SIZE - 1)
{ {
bool res = ((command_len + command_size) > buf_size || bool res = ((command_len + command_size) > buf_size ||

View File

@ -244,7 +244,7 @@ void SysCoreThread::GameStartingInThread()
#ifdef USE_SAVESLOT_UI_UPDATES #ifdef USE_SAVESLOT_UI_UPDATES
UI_UpdateSysControls(); UI_UpdateSysControls();
#endif #endif
if(EmuConfig.EnableIPC && m_IpcState == OFF) if (EmuConfig.EnableIPC && m_IpcState == OFF)
{ {
m_IpcState = ON; m_IpcState = ON;
m_socketIpc = std::make_unique<SocketIPC>(this); m_socketIpc = std::make_unique<SocketIPC>(this);