cesanta / mongoose

Embedded Web Server

Home Page:https://mongoose.ws

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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 example main.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 the mongoose.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. The alloca() 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 ?

@jvo203 @scaprile

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