Skip to content

fix: free cJSON strings printed for custom messages - #2257

Merged
78 merged 1 commit into
78:mainfrom
Dev-next-gen:fix/custom-message-cjson-leak
Sep 14, 2026
Merged

78 merged 1 commit into
78:mainfrom
Dev-next-gen:fix/custom-message-cjson-leak

Conversation

@Dev-next-gen

Copy link
Copy Markdown
Contributor

With CONFIG_RECEIVE_CUSTOM_MESSAGE enabled, the custom branch in Application::InitializeProtocol() calls cJSON_PrintUnformatted() twice, once for the ESP_LOGI and once to build the std::string captured by the scheduled lambda, and never frees either buffer. Every custom message from the server leaks two heap allocations the size of the JSON. If the allocation fails, the second call returns NULL and std::string(NULL) aborts, since the firmware is built without C++ exceptions.

The fix wraps both results in CJsonStringUniquePtr from cjson_utils.h, as mcp_server.h already does, logs an empty string if printing failed, and skips scheduling the chat message when the payload could not be printed.

I checked it on a host by compiling the real #if CONFIG_RECEIVE_CUSTOM_MESSAGE block from application.cc against cJSON 1.7.18 with -fno-exceptions, counting allocations through cJSON_InitHooks and handling the same message 1000 times:

before: normal: display="{"text":"hi"}" leaked cJSON buffers after 1000 messages: 2000
before: oom:    terminate called after throwing an instance of 'std::logic_error'
                  what():  basic_string: construction from null is not valid
after:  normal: display="{"text":"hi"}" leaked cJSON buffers after 1000 messages: 0
after:  oom:    display="" leaked cJSON buffers after 1000 messages: 0

The displayed message is unchanged on the normal path. I haven't flashed a board with this option enabled. #2040 carries a similar change inside a board support PR; this one only touches these lines so it can go in on its own.

AI tools used

The custom message handler passed the result of cJSON_PrintUnformatted()
straight to ESP_LOGI and to std::string, so both buffers leaked on every
custom message, and a failed allocation built a std::string from NULL.
@78
78 merged commit df6b31d into 78:main Sep 14, 2026
181 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants