nrf_cloud (MQTT): one cJSON node leaked per connection by nrf_cloud_obj_shadow_transform_decode()

Affected: nRF Connect SDK v3.4.0; the same code is on main as checked on 2026-11-11 (subsys/net/lib/nrf_cloud/common/src/nrf_cloud_codec_internal.c, subsys/net/lib/nrf_cloud/mqtt/src/nrf_cloud_fsm.c).

What happens: on every MQTT connection the library requests a shadow transform (nrf_cloud_fsm.c, nrf_cloud_shadow_transform_request(cc_req_transform_topic_pairing, 0)). cc_rx_data_handler() parses the response into shadow_obj and calls nrf_cloud_obj_shadow_transform_decode(&shadow_obj, &shadow_tf). In the success branch that function detaches the result object and then calls nrf_cloud_obj_reset(shadow_obj), which sets shadow_obj->json = NULL without freeing it. The caller's following nrf_cloud_obj_free(&shadow_obj) therefore frees nothing, and the emptied root object (one cJSON node, 40 B on a 32-bit target) is lost. nrf_cloud_obj_shadow_transform_free() frees only tf->result.obj.

In the error branch the root is referenced by tf->error.err_obj, and nothing frees it either (nrf_cloud_obj_shadow_transform_free() does nothing when is_err).

Observed: nRF9151 DK (PCA10171), modem firmware mfw_nrf91x1_2.0.4, CONFIG_NRF_CLOUD_MQTT=y with the connection poll thread, system heap (CONFIG_HEAP_MEM_POOL_SIZE=16384) read with sys_heap_runtime_stats_get() at READY after each connection: 904, 948, 992, 1036, 1080, 1124, 1168 and 1212 B allocated, 44 B per connection; sending messages and receiving commands did not change it, and nrf_cloud_uninit()/nrf_cloud_init() did not recover it.

Suggested fix: in the success branch, free the remaining root instead of resetting the reference (for example nrf_cloud_obj_free(shadow_obj) after the detach); in the error branch, let nrf_cloud_obj_shadow_transform_free() free tf->error.err_obj (or document that the caller owns the root).

Workaround used: link with -Wl,--wrap=nrf_cloud_obj_shadow_transform_decode; the wrapper saves *shadow_obj, calls the real function and, after a successful non-error decode whose input was reset, calls nrf_cloud_obj_free() on the saved root. DK result with the workaround: the heap at READY stayed at 860 B at baseline and after each of four reconnects (two transport drops, an LTE off/on and a stop/start).

Parents Reply Children
No Data
Related