-
Notifications
You must be signed in to change notification settings - Fork 571
Description
Description
Japronto’s HTTP parser (cffiparser.py) will accept a Content-Length or chunked payload of arbitrary size, copy the entire request body into a Python buffer, and then hand it off in one piece to the C “gather” logic (cprotocol.c). That logic uses a fixed-size allocation (malloc(sizeof(PyBytesObject) + GATHER_MAX_LEN)) but does not bound the incoming length. When the body length (or the total decoded chunked length) exceeds GATHER_MAX_LEN, the subsequent memcpy overruns the heap buffer, leading to a crash or potential arbitrary‐write.
# cffiparser.py
# 1. Parse a large Content-Length header
raw = ffi.buffer(header.value, header.value_len)
self.content_length = int(raw[:])
# 2. When the body is complete (non-chunked)
body = memoryview(self.buffer)[: self.content_length]
self.on_body(ffi.from_buffer(body))
# 3. Or, after decoding chunked
if self.transfer == 'chunked' and decode_complete:
body = memoryview(self.buffer)[: self.chunked_offset[0]]
self.on_body(ffi.from_buffer(body))/* cprotocol.c */
// Allocate a fixed buffer regardless of requested size
static inline PyBytesObject*
Bytes_FromSize(size_t size) {
PyBytesObject* result;
if (!(result = malloc(sizeof(PyBytesObject) + GATHER_MAX_LEN)))
return (PyBytesObject*)PyErr_NoMemory();
result->ob_size = (Py_ssize_t)size;
return result;
}
// Copy incoming data chunk – overflow if len > GATHER_MAX_LEN
static Py_ssize_t
Protocol_on_body(Protocol *self, const char *data, size_t len, size_t tail_len) {
PyBytesObject *response = Bytes_FromSize(len);
memcpy(PyBytes_AS_STRING(response), data, len);
// ...
}
// Later, merging all pieces – overflow if total > GATHER_MAX_LEN
static PyObject*
Gather_flush(Gather *gather) {
PyBytesObject *buf = Bytes_FromSize(gather->len);
size_t offset = 0;
for (size_t i = 0; i < gather->responses_end; i++) {
PyObject *item = gather->responses[i];
memcpy(buf->ob_sval + offset,
PyBytes_AS_STRING(item),
Py_SIZE(item));
offset += Py_SIZE(item);
Py_DECREF(item);
}
return (PyObject*)buf;
}Steps to Reproduce
Use a simple Python script or curl to send a request with a very large Content-Length
Expected Results
The server should enforce a maximum body size (e.g., reject with 413 Payload Too Large) or stream chunks without accumulating everything in RAM.
Under no circumstances should an attacker‐controlled length lead to a buffer overflow in C.
Actual Results
The process crashes with a segmentation fault (heap corruption) when the body length (or total chunked length) exceeds GATHER_MAX_LEN.
Version
commit hash: e73b76e