summaryrefslogtreecommitdiff
path: root/extmod/modwebsocket.c
diff options
context:
space:
mode:
authorJeff Epler <jepler@gmail.com>2025-08-04 10:05:28 -0500
committerDamien George <damien@micropython.org>2025-10-05 15:41:54 +1100
commitb94162b5dfe19ae1083a0bdf0611e07262fe8fe8 (patch)
tree438422297ee322155ee7363bcb8f46eb1afc30f7 /extmod/modwebsocket.c
parentd921dd6d61be29866e62b923d3e37859829188bd (diff)
extmod/modwebsocket: Enable split frames and test them.
This fixes several assertion errors that were found in fuzz testing, for unimplemented portions of the websocket spec. The assertions were either turned into Python exceptions, or the missing functionality was implemented. Split frames are now enabled and work, enabling reception of frames up to 64kB (assuming they are encoded with a 16-bit size field). Frames with a 64-bit size fields remain unsupported but no longer result in an assertion error. Instead, Initial reception of such a frame will result in OSError(EIO) and subsequent operations on the same websocket will fail because framing has been lost. Transmitting frames larger than 64kB is unsupported. Attempting to transmit such a frame will result in OSError(ENOBUFS). Subsequent operations on the websocket are possible. Signed-off-by: Jeff Epler <jepler@gmail.com>
Diffstat (limited to 'extmod/modwebsocket.c')
-rw-r--r--extmod/modwebsocket.c22
1 files changed, 15 insertions, 7 deletions
diff --git a/extmod/modwebsocket.c b/extmod/modwebsocket.c
index d31d00160..6640dbe42 100644
--- a/extmod/modwebsocket.c
+++ b/extmod/modwebsocket.c
@@ -76,6 +76,7 @@ static mp_obj_t websocket_make_new(const mp_obj_type_t *type, size_t n_args, siz
static mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
mp_obj_websocket_t *self = MP_OBJ_TO_PTR(self_in);
+
const mp_stream_p_t *stream_p = mp_get_stream(self->sock);
while (1) {
if (self->to_recv != 0) {
@@ -93,9 +94,6 @@ static mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int
switch (self->state) {
case FRAME_HEADER: {
- // TODO: Split frame handling below is untested so far, so conservatively disable it
- assert(self->buf[0] & 0x80);
-
// "Control frames MAY be injected in the middle of a fragmented message."
// So, they must be processed before data frames (and not alter
// self->ws_flags)
@@ -120,14 +118,15 @@ static mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int
// Msg size is next 2 bytes
to_recv += 2;
} else if (sz == 127) {
- // Msg size is next 8 bytes
- assert(0);
+ // Msg size is next 8 bytes (unsupported, no way to recover)
+ mp_stream_close(self->sock);
+ *errcode = MP_EIO;
+ return MP_STREAM_ERROR;
}
if (self->buf[1] & 0x80) {
// Next 4 bytes is mask
to_recv += 4;
}
-
self->buf_pos = 0;
self->to_recv = to_recv;
self->msg_sz = sz; // May be overridden by FRAME_OPT
@@ -146,6 +145,8 @@ static mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int
case FRAME_OPT: {
if ((self->buf_pos & 3) == 2) {
// First two bytes are message length
+ // First two bytes are message length. Technically the size must be at least 126 per RFC6455
+ // but MicroPython skips checking that.
self->msg_sz = (self->buf[0] << 8) | self->buf[1];
}
if (self->buf_pos >= 4) {
@@ -218,9 +219,16 @@ static mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int
static mp_uint_t websocket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
mp_obj_websocket_t *self = MP_OBJ_TO_PTR(self_in);
- assert(size < 0x10000);
+ if (size >= 0x10000) {
+ *errcode = MP_ENOBUFS;
+ return MP_STREAM_ERROR;
+ }
byte header[4] = {0x80 | (self->opts & FRAME_OPCODE_MASK)};
int hdr_sz;
+ // "Note that in all cases, the minimal number of bytes MUST be used to
+ // encode the length, for example, the length of a 124-byte-long string
+ // can't be encoded as the sequence 126, 0, 124."
+ // -- https://www.rfc-editor.org/rfc/rfc6455.html
if (size < 126) {
header[1] = size;
hdr_sz = 2;