Bus error crash in the new mg_wakeup() function
jvo203 opened this issue · comments
- My goal is: pass a relatively large buffer to
mg_wakeup()
in the multi-threaded examplemain.c
- My actions were:
size_t len = 1024 * 1024 * sizeof(char); // 1 MB
char *buf = malloc(len);
memset(buf, 0, len);
snprintf(buf, len, "Hello from thread %lu", p->conn_id);
// mg_wakeup(p->mgr, p->conn_id, "hi!", 3); // Respond to parent
mg_wakeup(p->mgr, p->conn_id, buf, len); // Respond to parent
free(buf);
- My expectation was: No segmentation fault, no bus error crash
- The result I saw:
make: *** [all] Bus error: 10
- My question is:
char *extended_buf = (char *) alloca(len + sizeof(conn_id));
inside themongoose.c
mg_wakeup()
function fails to allocate large buffers (i.e. 1048576 bytes) and crashes. It works fine for small buffer sizes. Depending on the OS stack size on a user machine the exact failure threshold may vary. Thealloca()
stack allocation function results in undefined behaviour as per its manual page. Mongoose should not assume users have an unlimited stack size set in their environment.
RETURN VALUE top
The alloca() function returns a pointer to the beginning of the allocated space. If the allocation causes stack overflow, program behavior is undefined.
Related issue: #2532 . This is one of the reasons why I had to do bool sent = mg_wakeup(session->mgr, session->conn_id, &msg, sizeof(struct mg_str));
(i.e. passing a pointer to the payload) instead of bool sent = mg_wakeup(session->mgr, session->conn_id, image_payload, msg_len);
. The other reason was to avoid excessively copying large memory buffers.
Environment
ulimit -a
-t: cpu time (seconds) unlimited
-f: file size (blocks) unlimited
-d: data seg size (kbytes) unlimited
-s: stack size (kbytes) 8176
-c: core file size (blocks) 0
-v: address space (kbytes) unlimited
-l: locked-in-memory size (kbytes) unlimited
-u: processes 10666
-n: file descriptors 256
- mongoose version: 7.12
- Compiler/IDE and SDK: Apple clang version 15.0.0 (clang-1500.1.0.2.5)
- Target hardware/board: AMD/Intel 64-bit CPU, Apple Silicon
- Target RTOS/OS (if applicable): macOS, Linux
Mongoose needs to get that amount of memory from somewhere. If you expect to pass 1MB chunks, then you need at least a 1MB task stack. That function wasn't meant to be used with huge chunks of memory, you can change alloca for alloc and that memory will come out of your heap instead of your task stack. That may be great for your case but a penalty for typical applications passing a simple response.
Yes I agree the memory has to come out of somewhere. As you say, either the stack needs to be made sufficiently large or that memory has to come from the heap.
Personally I don't send 1MB payloads 'as is'. As you are aware I send pointers to large payloads, pointers do not take up much memory so using alloca()
is not a pressing problem.
If you think it's OK to close this issue without changing mongoose then it's absolutely OK with me too. It's just one thing to keep in mind when using mongoose, one needs to be aware of the stack size etc.
@cpq I think we should keep things as they are. I can add a note on stack usage at the function entry in the API docs. IMO, having several allocs for small messages would be detrimental. We already have a note telling users to dimension their stack, they are just missing this new piece of information. WDYT ?
We should not send messages so big.
They are sent over UDP socketpair, which has its internal limit.
I am not sure that we should impose the limit in the code, let's change the documentation.
I've updated the docs, https://mongoose.ws/documentation/#mg_wakeup