问题描述
我有一个旧的 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。