cJSON icon indicating copy to clipboard operation
cJSON copied to clipboard

cJSON_Print memory leak in ESP-IDF

Open law-ko opened this issue 1 year ago • 1 comments

Hello,

With further checking, cJSON_Print will cause an internal heap leak, which is unknown. The current testing environment is under ESP-IDF v4.4.

The create_monitor example is used.

ESP32 code:

/* Hello World Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"

#include "cJSON.h"

//create a monitor with a list of supported resolutions
//NOTE: Returns a heap allocated string, you are required to free it after use.
char *create_monitor(void)
{
    /* Checking heap */
    printf("\nENTERS CREATE_MONITOR: free heap size = %d \t  min_free_heap_size = %d \n",esp_get_free_heap_size(),esp_get_minimum_free_heap_size());
    const unsigned int resolution_numbers[3][2] = {
        {1280, 720},
        {1920, 1080},
        {3840, 2160}
    };
    char *string = NULL;
    cJSON *name = NULL;
    cJSON *resolutions = NULL;
    cJSON *resolution = NULL;
    cJSON *width = NULL;
    cJSON *height = NULL;
    size_t index = 0;

    /* Checking heap */
    printf("\nBEFORE cJSON_CREATEOBJECT: free heap size = %d \t  min_free_heap_size = %d \n",esp_get_free_heap_size(),esp_get_minimum_free_heap_size());

    cJSON *monitor = cJSON_CreateObject();
    if (monitor == NULL)
    {
        goto end;
    }

    name = cJSON_CreateString("Awesome 4K");
    if (name == NULL)
    {
        goto end;
    }
    /* after creation was successful, immediately add it to the monitor,
     * thereby transferring ownership of the pointer to it */
    cJSON_AddItemToObject(monitor, "name", name);

    resolutions = cJSON_CreateArray();
    if (resolutions == NULL)
    {
        goto end;
    }
    cJSON_AddItemToObject(monitor, "resolutions", resolutions);

    for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index)
    {
        resolution = cJSON_CreateObject();
        if (resolution == NULL)
        {
            goto end;
        }
        cJSON_AddItemToArray(resolutions, resolution);

        width = cJSON_CreateNumber(resolution_numbers[index][0]);
        if (width == NULL)
        {
            goto end;
        }
        cJSON_AddItemToObject(resolution, "width", width);

        height = cJSON_CreateNumber(resolution_numbers[index][1]);
        if (height == NULL)
        {
            goto end;
        }
        cJSON_AddItemToObject(resolution, "height", height);
    }
    /* Checking heap */
    printf("\nBEFORE cJSON PRINT: free heap size = %d \t  min_free_heap_size = %d \n",esp_get_free_heap_size(),esp_get_minimum_free_heap_size());
    string = cJSON_Print(monitor);
    /* Checking heap */
    printf("\nAFTER cJSON PRINT: free heap size = %d \t  min_free_heap_size = %d \n",esp_get_free_heap_size(),esp_get_minimum_free_heap_size());
    if (string == NULL)
    {
        fprintf(stderr, "Failed to print monitor.\n");
    }

end:
    /* Checking heap */
    printf("\nBEFORE cJSON DELETE: free heap size = %d \t  min_free_heap_size = %d \n",esp_get_free_heap_size(),esp_get_minimum_free_heap_size());
    cJSON_Delete(monitor);
    /* Checking heap */
    printf("\nAFTER cJSON DELETE: free heap size = %d \t  min_free_heap_size = %d \n",esp_get_free_heap_size(),esp_get_minimum_free_heap_size());
    return string;
}

void app_main(void)
{
    printf("Hello world!\n");

    /* Print chip information */
    esp_chip_info_t chip_info;
    esp_chip_info(&chip_info);
    printf("This is %s chip with %d CPU core(s), WiFi%s%s, ",
            CONFIG_IDF_TARGET,
            chip_info.cores,
            (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
            (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");

    printf("silicon revision %d, ", chip_info.revision);

    printf("%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024),
            (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");

    printf("Minimum free heap size: %d bytes\n", esp_get_minimum_free_heap_size());

    /* HEAP Managment Checking */
    /* Checking heap */
    printf("\n free heap size = %d \t  min_free_heap_size = %d \n",esp_get_free_heap_size(),esp_get_minimum_free_heap_size());
    char *monitor = create_monitor();
    printf("Monitor: %s\n", monitor);
    /* Checking heap */
    printf("\n free heap size = %d \t  min_free_heap_size = %d \n",esp_get_free_heap_size(),esp_get_minimum_free_heap_size());
    free(monitor);
    /* Checking heap */
    printf("\n free heap size = %d \t  min_free_heap_size = %d \n",esp_get_free_heap_size(),esp_get_minimum_free_heap_size());
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    /* Checking heap */
    printf("\n free heap size = %d \t  min_free_heap_size = %d \n",esp_get_free_heap_size(),esp_get_minimum_free_heap_size());
}

Many heap checks are made during the program for easier debugging.

Output:

Hello world!
This is esp32 chip with 2 CPU core(s), WiFi/BT/BLE, silicon revision 3, 2MB external flash
Minimum free heap size: 295096 bytes

 free heap size = 295096          min_free_heap_size = 295096 

ENTERS CREATE_MONITOR: free heap size = 295096    min_free_heap_size = 295096 

BEFORE cJSON_CREATEOBJECT: free heap size = 295096        min_free_heap_size = 295096 

BEFORE cJSON PRINT: free heap size = 294424       min_free_heap_size = 294424 

AFTER cJSON PRINT: free heap size = 294032        min_free_heap_size = 293948 

BEFORE cJSON DELETE: free heap size = 294032      min_free_heap_size = 293948 

AFTER cJSON DELETE: free heap size = 294704       min_free_heap_size = 293948 
Monitor: {
        "name": "Awesome 4K",
        "resolutions":  [{
                        "width":        1280,
                        "height":       720
                }, {
                        "width":        1920,
                        "height":       1080
                }, {
                        "width":        3840,
                        "height":       2160
                }]
}

 free heap size = 294704          min_free_heap_size = 293948 

 free heap size = 294880          min_free_heap_size = 293948 

 free heap size = 294880          min_free_heap_size = 293948 

With the calculation of how much heap is used: 294424-294032=392. And when free(monitor) is called: 294880-294704=176, where 392-176=216 is leaked.

The system started with 295096, where if the 216 was added back into 294880 then there will be no leaks.

Is there any reason why cJSON_Print will cause internal leaks even after freeing the char *?

Thank you

law-ko avatar Oct 27 '22 02:10 law-ko

You should close this issue, since the problem is not with cJSON, and track it in the IDF issue. In the future, avoid escalating things without any need whatsoever.

KaeLL avatar Oct 27 '22 18:10 KaeLL