关于在偏移处访问字节的 GCC 警告:GCC 在抱怨什么?

问题描述

我有一个旧的 C 库,我必须在现代/当前系统上构建它。这 图书馆至少已经有 5 年或 6 年(也许甚至更长)没有被开发出来。所以 自然地,使用现代 C 编译器构建它会引发很多警告。所以我 开始检查警告并修复代码。

但是有一个警告我真的不知道它是什么意思。我正在编译 使用 GCC 9.3.0 和 CFLAGS 中的 -O2 -m32 -Wall

[ 17%] Building C object cu_datastructs/CMakeFiles/cu_datastructs.dir/cu_nlist.c.o
In file included from /usr/include/string.h:519,from /home/shaoran/projects/cu_utils/remus_installer/cu_utils/cu_datastructs/cu_nlist.c:3:
In function ‘strcpy’,inlined from ‘cu_nlist_swap’ at /home/shaoran/projects/cu_utils/remus_installer/cu_utils/cu_datastructs/cu_nlist.c:389:2:
/usr/include/bits/string_fortified.h:90:10: warning: ‘__builtin_strcpy’ accessing 1 byte at offsets [-1073741824,1073741823] and [-1073741824,1073741823] may overlap 1 byte at offset -1073741824 [-Wrestrict]
   90 |   return __builtin___strcpy_chk (__dest,__src,__bos (__dest));
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

有问题的代码是:

int cu_nlist_swap(struct cu_nlist *nl,int id1,int id2)
{
    unsigned char *c_tmp;
    cu_nlist_names n_tmp;

    if(nl == NULL || id1 < 0 || id2 < 0)
        return 0;

    if(id1 >= nl->size || id2 >= nl->size) /* out of bounds */
        return 0;

    if(id1 == id2)
        return 1;

    c_tmp = nl->items[id1];
    nl->items[id1] = nl->items[id2];
    nl->items[id2] = c_tmp;

    strcpy(n_tmp,nl->names[id1]);
    strcpy(nl->names[id1],nl->names[id2]);   // <--- line with the warning
    strcpy(nl->names[id2],n_tmp);

    return 1;
}

其中 cu_nlist 定义为:

// on a different .h file
#define CU_NAMELEN 1024

// ...

#define CU_NLIST_NAME_LEN   CU_NAMELEN
#define CU_NLIST_STARTCAP   2

typedef unsigned char*  (*create_f)();
typedef void    (*free_f)(unsigned char*);
typedef int     (*copy_f)(unsigned char*,unsigned char*);

typedef char    cu_nlist_names[CU_NLIST_NAME_LEN];

typedef struct cu_nlist {
    create_f         ocreate;
    free_f           ofree;
    copy_f           ocopy;
    int              cap;
    int              size;
    unsigned char**  items;  //< holds the items in the named list,initialized with NULL
    cu_nlist_names*  names;  //< holds the names for the items,initialized with NULL
} cu_nlist;

我瞎了吗?为什么 cu_nlist_swap 会产生这个警告,以及什么 那是什么意思?如果我删除 -O2 则不会发出警告,我无法理解 它。 GCC 到底在告诉我什么?

所以,这个结构的想法是有一个命名的对象列表,当你 追加一个新值,追加函数检查大小。如果数量 元素 (size) 达到最大值 (cap),然后我们调整缓冲区的大小以容纳 将之前的内存量翻倍。这是通过这个函数完成的:

static  int nlist_resize(struct cu_nlist* nl)
{
    void *tmp;
    int sz;
    if (nl->cap == 0)
        sz = CU_NLIST_STARTCAP;
    else
        sz = 2*nl->cap;

    tmp = realloc(nl->items,sz * sizeof(unsigned char*));
    if(tmp == NULL)
        return 0;
    nl->items = tmp;

    tmp = realloc(nl->names,sz*sizeof(cu_nlist_names));
    if(tmp == NULL)
        return 0;
    nl->names = tmp;

    nl->cap = sz;
    return 1;
}

所以nl->names指向一个1024*sz字节的内存块,nl->names[0] 指向前 1024 个字节,nl->names[1] 指向下一个 1024 个字节, 等等。所以我不明白为什么可能会有重叠的记忆。我是什么 这里不见了?

我知道将名称声明为 char** name 并分配 CU_NLIST_NAME_LEN 调用 cu_nlist_append 时每个新名称的字节数会更好,但我 将不得不重写更多的代码,这是目前我 不行。我知道这不是漂亮的代码,并且可以更有效地使用 内存使用,但我没有写这段代码,我只是在维护它(这是遗留的 来自我公司不再开发的旧项目的代码)。我想要 了解 GCC 警告我什么。

解决方法

strcpy() 的要求之一是源和目标不能重叠。不幸的是,在工作时,它也不关心您如何在内存中定义缓冲区。它继续耕作,直到遇到零终止符。因此,当从一个相邻缓冲区复制到另一个缓冲区时,如果找不到终止零,它可能最终会溢出。当您的缓冲区相邻时(如 -O2 中的成员一样),nl->names[] 中的内联可能会以某种方式暴露这种威胁。

无论如何,我会切换到 strncpy() - 你知道你的缓冲区长度,这将是一个微不足道的改变,可能会关闭 gcc。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...