Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_network_ws_limits.c

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.

Author
Castagnier Mickael
Version
1.0
Date
24/08/2026
/*
* Nilorea Library
* Copyright (C) 2005-2026 Castagnier Mickael
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "nilorea/n_log.h"
#include "nilorea/n_str.h"
#include <string.h>
#define WS_LIMITS_PORT "27431"
static int send_hostile_header(NETWORK* peer, uint64_t len) {
unsigned char hdr[10];
size_t i;
hdr[0] = 0x81; /* FIN + text */
hdr[1] = 0x7F; /* 64-bit extended length, not masked */
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;
}
static int check_refused(const char* label, uint64_t len) {
NETWORK* server = NULL;
NETWORK* peer = NULL;
NETWORK* client = NULL;
N_WS_CONN conn;
int rc;
int failed = 1;
memset(&conn, 0, sizeof(conn));
memset(&msg, 0, sizeof(msg));
if (netw_make_listening(&server, "127.0.0.1", (char*)WS_LIMITS_PORT, 1, NETWORK_IPV4) == FALSE) {
n_log(LOG_ERR, "FAIL(%s): could not listen on " WS_LIMITS_PORT, label);
return 1;
}
if (netw_connect_to(&client, "127.0.0.1", (char*)WS_LIMITS_PORT, NETWORK_IPV4, 2000) == FALSE) {
n_log(LOG_ERR, "FAIL(%s): could not connect to the listener", label);
return 1;
}
if (!peer) {
n_log(LOG_ERR, "FAIL(%s): listener did not accept", label);
netw_close(&client);
return 1;
}
if (send_hostile_header(peer, len) != 0) {
n_log(LOG_ERR, "FAIL(%s): could not send the frame header", label);
goto cleanup;
}
conn.netw = client;
conn.connected = 1;
rc = n_ws_recv(&conn, &msg);
if (rc == 0) {
n_log(LOG_ERR, "FAIL(%s): n_ws_recv ACCEPTED a declared payload of %llu bytes", label, (unsigned long long)len);
if (msg.payload)
} 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:
netw_close(&peer);
netw_close(&client);
return failed;
}
int main(void) {
int errors = 0;
n_log(LOG_INFO, "n_ws_recv frame-length bound: N_WS_MAX_PAYLOAD is %zu bytes", (size_t)N_WS_MAX_PAYLOAD);
/* the wrapping case: length + 1 for the terminator becomes 0, so the
allocation would come back a single byte and the payload read would run
off the end of it */
errors += check_refused("terminator wrap", (uint64_t)SIZE_MAX - 1);
/* the memory-exhaustion case: representable, but far past anything a real
peer sends */
errors += check_refused("over the cap", (uint64_t)N_WS_MAX_PAYLOAD + 1);
if (errors > 0) {
n_log(LOG_ERR, "ex_network_ws_limits: %d test(s) failed", errors);
exit(1);
}
n_log(LOG_INFO, "ex_network_ws_limits: all checks passed");
exit(0);
}
int main(void)
Definition ex_atlas.c:117
NETWORK * server
#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.
Definition n_log.h:113
#define LOG_ERR
error conditions
Definition n_log.h:100
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
Definition n_log.c:123
#define LOG_INFO
informational
Definition n_log.h:106
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
Definition n_str.h:203
NETWORK * netw
underlying network connection
Definition n_network.h:1073
int connected
1 if handshake completed
Definition n_network.h:1074
N_STR * payload
message payload
Definition n_network.h:1067
#define N_WS_MAX_PAYLOAD
Largest WebSocket payload n_ws_recv() will accept from a peer, in bytes.
Definition n_network.h:1062
int netw_make_listening(NETWORK **netw, char *addr, char *port, int nbpending, int ip_version)
Make a NETWORK be a Listening network.
Definition n_network.c:3493
#define NETWORK_IPV4
Flag to force IPV4.
Definition n_network.h:50
ssize_t send_data(void *netw, char *buf, uint32_t n)
send data onto the socket
Definition n_network.c:4824
NETWORK * netw_accept_from(NETWORK *from)
make a normal blocking 'accept' .
Definition n_network.c:4178
int netw_close(NETWORK **netw)
Closing a specified Network, destroy queues, free the structure.
Definition n_network.c:3258
int n_ws_recv(N_WS_CONN *conn, N_WS_MESSAGE *msg_out)
Receive one WebSocket frame.
Definition n_network.c:7157
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.
Definition n_network.c:2803
WebSocket connection.
Definition n_network.h:1072
WebSocket message.
Definition n_network.h:1065
Structure of a NETWORK.
Definition n_network.h:375
Generic log system.
Network Engine.
N_STR and string function declaration.