malloc(0)返回什么

时间:2022-01-14 03:19:25
最近开发新产品的过程中遇到一个bug,正在调试的web服务器在打开某些页面时,会突然出现段错误导致服务器进程异常终止。经过一番摸索,通过反汇编定位到了出错的语句。(通过反汇编定位段错误
    char *str = cJSON_Print(root);
  4184ec:    8f9981ec     lw    t9,-32276(gp)
  4184f0:    00000000     nop
  4184f4:    0320f809     jalr    t9
  4184f8:    02002021     move    a0,s0
  4184fc:    8fbc0010     lw    gp,16(sp)
    httpnPrintf(reqId, strlen(str) + 1, "%s\n", str);
  418500:    00402021     move    a0,v0
  418504:    8f999fe4     lw    t9,-24604(gp)
  418508:    00000000     nop
  41850c:    0320f809     jalr    t9
  418510:    00408821     move    s1,v0
  418514:    8fbc0010     lw    gp,16(sp)
  418518:    3c060062     lui    a2,0x62
  41851c:    02203821     move    a3,s1
  418520:    8f9981f0     lw    t9,-32272(gp)
  418524:    24450001     addiu    a1,v0,1
  418528:    24c6cdd8     addiu    a2,a2,-12840
  41852c:    0320f809     jalr    t9
  418530:    02402021     move    a0,s2
  418534:    8fbc0010     lw    gp,16(sp)
  418538:    00000000     nop
    free(str);
  ...
如代码所示,出问题的语句在函数httpnPrintf()中,而它传入的参数是由函数cJSON_Print()生成的字符串,cJSON_Print()是开源的cJSON库中的函数,负责将json格式的对象转换为可打印字符串。
进一步跟踪发现,某些页面的列表内容为空,导致cJSON_Print()返回的指针str为NULL,导致httpnPrintf()中访问了不合法的内存地址。但奇怪的一点是,旧版产品使用的cJSON库和新版完全一样,空列表的情况在旧版产品上也很常见,并不会引发这种问题。为了寻找根本原因,只能继续跟进cJSON库的源代码,终于在print_array()中发现了疑点。
static char *print_array(cJSON *item, int depth, int fmt)
{
    char **entries;
    char *out = 0, *ptr, *ret;
    int len = 5;
    cJSON *child = item->child;
    int numentries = 0, i = 0, fail = 0;
    
    /* How many entries in the array? */
    while (child)
        numentries++, child=child->next;
    
    /* Allocate an array to hold the values for each */
    entries = (char**)cJSON_malloc(numentries*sizeof(char*));
    if (!entries)
        return 0;
    memset(entries, 0, numentries*sizeof(char*));
    
    ...
}
问题就出在这里,cJSON_malloc函数调用了C库的malloc函数,当页面列表为空时,传入的参数item为空数组,此时numentries的值为0。所以实际上这里调用的是malloc(0)。出问题时函数就是在这之后返回的。
旧版产品与新版产品使用的cJSON库一模一样,问题只能出在malloc()函数本身。跟进发现旧版产品使用的工具链与新版不同,难道是工具链中的C库在实现上有区别,导致malloc()函数行为不同?在malloc(0)后打印指针的值,果然发现了区别——旧版工具链malloc(0)后仍然返回一个非空指针,而新版工具链malloc(0)则返回0,即空指针。而函数print_array()是被递归调用的,当它返回空指针,最顶层的打印函数cJSON_Print()自然也会返回空指针。至此,问题得以定位。
同样是malloc函数,在不同的C库中行为也会不同,所以在使用第三方提供的库函数时,不能对其做任何的假设,还需要具体问题具体分析才行。