n_ws_recv frame-length bound: a peer-declared payload length is read straight off the wire, so it is bounded BEFORE it sizes an allocation.
n_ws_recv frame-length bound: a peer-declared payload length is read straight off the wire, so it is bounded BEFORE it sizes an allocation. A frame header carries its payload length in up to 64 bits. Near the maximum, the terminator arithmetic (length + 1) wraps to zero and the allocation comes back a single byte, after which reading the declared payload writes past it. Below that, a merely enormous length is an invitation to exhaust memory. This connects a client to a local listener, has the listener send each hostile header, and checks that n_ws_recv refuses rather than allocates.
#include <string.h>
#define WS_LIMITS_PORT "27431"
unsigned char hdr[10];
size_t i;
hdr[0] = 0x81;
hdr[1] = 0x7F;
for (i = 0; i < 8; i++)
hdr[2 + i] = (unsigned char)((len >> (56 - 8 * i)) & 0xFF);
if (
send_data(peer, (
char*)hdr, (uint32_t)
sizeof(hdr)) != (ssize_t)
sizeof(hdr))
return -1;
return 0;
}
int rc;
int failed = 1;
memset(&conn, 0, sizeof(conn));
memset(&msg, 0, sizeof(msg));
return 1;
}
n_log(
LOG_ERR,
"FAIL(%s): could not connect to the listener", label);
return 1;
}
if (!peer) {
return 1;
}
n_log(
LOG_ERR,
"FAIL(%s): could not send the frame header", label);
goto cleanup;
}
if (rc == 0) {
n_log(
LOG_ERR,
"FAIL(%s): n_ws_recv ACCEPTED a declared payload of %llu bytes", label, (
unsigned long long)len);
} else {
n_log(
LOG_INFO,
"PASS(%s): n_ws_recv refused a declared payload of %llu bytes", label, (
unsigned long long)len);
failed = 0;
}
cleanup:
return failed;
}
int errors = 0;
errors +=
check_refused(
"terminator wrap", (uint64_t)SIZE_MAX - 1);
if (errors > 0) {
n_log(
LOG_ERR,
"ex_network_ws_limits: %d test(s) failed", errors);
exit(1);
}
exit(0);
}
#define WS_LIMITS_PORT
Port the local listener binds for this test.
static int send_hostile_header(NETWORK *peer, uint64_t len)
Send a raw WebSocket frame header naming a payload length of len.
static int check_refused(const char *label, uint64_t len)
Drive one hostile length through n_ws_recv and report the outcome.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_ERR
error conditions
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
#define LOG_INFO
informational
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
NETWORK * netw
underlying network connection
int connected
1 if handshake completed
N_STR * payload
message payload
#define N_WS_MAX_PAYLOAD
Largest WebSocket payload n_ws_recv() will accept from a peer, in bytes.
int netw_make_listening(NETWORK **netw, char *addr, char *port, int nbpending, int ip_version)
Make a NETWORK be a Listening network.
#define NETWORK_IPV4
Flag to force IPV4.
ssize_t send_data(void *netw, char *buf, uint32_t n)
send data onto the socket
NETWORK * netw_accept_from(NETWORK *from)
make a normal blocking 'accept' .
int netw_close(NETWORK **netw)
Closing a specified Network, destroy queues, free the structure.
int n_ws_recv(N_WS_CONN *conn, N_WS_MESSAGE *msg_out)
Receive one WebSocket frame.
int netw_connect_to(NETWORK **netw, char *host, char *port, int ip_version, int connect_timeout_ms)
Connect a NETWORK with a bounded connection-establishment time.
N_STR and string function declaration.