【发布时间】:2020-11-25 02:34:25
【问题描述】:
我正在努力加深我在 C 方面的知识和经验,所以我正在编写一些小实用程序。
我正在复制内存,根据memcpy(3) 的手册页:
注意事项
未能遵守内存区域不重叠的要求 真正的错误的来源。 (POSIX 和 C 标准明确表示采用 具有重叠区域的 memcpy() 会产生未定义的行为。)最值得注意的是,在 glibc 2.13 memcpy() 在某些平台上的性能优化(包括 x86-64) 包括更改字节从 src 复制到 dest 的顺序。
显然,传递给memcpy(3) 的重叠内存区域可以cause a lot of problems。
我正在尝试编写一个安全的包装器作为学习 C 的一部分,以确保这些内存区域不会重叠:
int safe_memcpy(void *dest, void *src, size_t length);
我试图实现的逻辑是:
- 检查
NULL的源指针和目标指针。 - 使用长度参数为源和目标建立指针“范围”。
- 确定源范围是否与目标范围相交,反之亦然。
到目前为止我的实现:
#define SAFE_MEMCPY_ERR_NULL 1
#define SAFE_MEMCPY_ERR_SRC_OVERLAP 2
#define SAFE_MEMCPY_ERR_DEST_OVERLAP 3
int safe_memcpy(void *dest, void *src, size_t length) {
if (src == NULL || dest == NULL) {
return SAFE_MEMCPY_ERR_NULL;
}
void *dest_end = &dest[length - 1];
void *src_end = &src[length - 1];
if ((&src >= &dest && &src <= &dest_end) ||
(&src_end >= &dest && &src_end <= &dest_end)) {
// the start of src falls within dest..dest_end OR
// the end of src falls within dest..dest_end
return SAFE_MEMCPY_ERR_SRC_OVERLAP;
}
if ((&dest >= &src && &dest <= &src_end) ||
(&dest_end >= &src && &dest_end <= &src_end)) {
// the start of dest falls within src..src_end
// the end of dest falls within src..src_end
return SAFE_MEMCPY_ERR_DEST_OVERLAP;
}
// do the thing
memcpy(dest, src, length);
return 0;
}
可能有更好的方法来处理错误,但这就是我现在所拥有的。
我很确定我在这段代码中触发了一些未定义的行为,因为我在不重叠的内存区域上点击了SAFE_MEMCPY_ERR_DEST_OVERLAP。当我使用调试器检查状态时,我看到(例如)以下值:
-
src:0x7ffc0b75c5fb -
src_end:0x7ffc0b75c617 -
dest:0x1d05420 -
dest_end:0x1d0543c
显然,这些地址甚至没有远程重叠,因此我认为我正在触发 UB,并且编译器警告表明如下:
piper.c:68:27: warning: dereferencing ‘void *’ pointer
void *dest_end = &dest[length - 1];
It seems 我需要将指针转换为不同的类型,但我不确定要使用哪种类型:内存是无类型的,所以我应该使用char * 将内存“查看”为字节?如果是这样,我应该将所有内容都转换为char *吗?我应该改用intptr_t 还是uintptr_t?
给定两个指针和每个指针的长度,我如何安全地检查这些区域是否相互重叠?
【问题讨论】:
标签: c