GCC (trunk) -fanalyzer [CWE-401]假阳性?

问题描述 投票:1回答:1

我最近了解到GCC新的-fanalyzer功能,并决定在我们的一些代码库中试用它。结果相当有趣,但有一个函数我怀疑是假阳性。

这是有问题的函数,GCC 报告了一个 malloc 泄漏 (见此):

char** va_to_argv(va_list args, int32_t* argc)
{
    va_list a;
    char* arg;
    int32_t n;
    int32_t l;
    int32_t sz = 0;
    int32_t cnt = 0;

    va_copy(a, args);
    while ((arg = va_arg(a, char*)) != NULL)
    {
        sz += strlen(arg) + 1;
        ++cnt;
    }
    va_end(a);

    struct s
    {
        char* argv[cnt + 1];
        char  data[sz];
    };

    struct s* tmp = calloc(1, sizeof(*tmp));

    for (n=0, l=0; n<cnt; ++n)
    {
        tmp->argv[n] = &tmp->data[l];
        strcpy(tmp->argv[n], va_arg(args, char*));
        l += strlen(tmp->argv[n]) + 1;
    }

    tmp->argv[cnt++] = NULL;

    if (argc)
    {
        *argc = cnt;
    }

    return &(tmp->argv[0]);
}

它应该是转换一个 va_list (仅含 char*,与 NULL 作为最后一个参数)到等价的argcargv表示。据我所知,这个函数从来没有出现过任何问题,所以当GCC报告说 tmp 在回报表时被泄露。

是的。calloc() 存储在tmp中,而函数并没有回到 tmp 的第一个元素,所以乍一看,这个警告是有道理的。然而,返回的值是一个指向 tmp,而且结构的第一个元素的地址与结构本身的地址相同(所以 free(&(tmp->argv[0])) 应该是合法的)。)

ISOIEC 9899,第 6.7.2.1 节。

指向结构对象的指针,经过适当的转换后,会指向它的初始成员(如果该成员是一个位字段,则指向它所在的单位),反之亦然。在结构对象中可能会有未命名的填充,但不是在结构对象的开头。

不幸的是,这段代码依赖于GCC特有的扩展(结构体中的VLA),而clang并不支持。否则我想交叉检查一下clang的静态分析器是怎么说的。

EDIT:

即使类型不匹配(正如Lundin所指出的),警告仍然存在,即使在这个版本的函数中也是如此(当然,它不再做任何有意义的事情)。

char** va_to_argv(va_list args, int32_t* argc)
{
    struct s
    {
        char** argv;
        char  data[10];
    };

    struct s* tmp = calloc(1, sizeof(*tmp));

    if (tmp == NULL) return NULL;

    return tmp->argv;
}
c gcc static-analysis
1个回答
1
投票

没有VLA的简化版本。


char **va_to_argv(va_list args, int32_t *argc)
{
    va_list a;
    char *arg;
    char *data;
    char **arr;
    int32_t n;
    int32_t ii;
    int32_t sz = 0;
    int32_t cnt = 0;

    va_copy(a, args);
    for(cnt=0;  (arg = va_arg(a, char*)) ; cnt++ )
    {
        sz += 1+ strlen(arg) ;
    }
    va_end(a);


    data = malloc(sz+1);
    arr = calloc(cnt+1, sizeof *arr);

    for (n=ii=0; n<cnt; n++)
    {
        arr[n] = &data[ii];
        strcpy(arr[n], va_arg(args, char*));
        ii += 1 + strlen(arr[n]) ;
    }

    arr[n++] = NULL;

    if (argc) *argc = n;

    return arr;
}

EDIT: 如果你只想分配一个对象, 你可以把char[]数组放在*char[]数组之后, 然后只返回指针数组:


arr = malloc(sz+1 + (cnt+1) * sizeof *arr);
data = (char*) ( &arr[cnt+1] ); // Put data after the last arr[] element
© www.soinside.com 2019 - 2024. All rights reserved.