我们可以检查传递给函数的指针是否在C中分配了内存?
我在C中使用了自己的函数,它接受了一个字符指针 - buf [指向缓冲区的指针]和大小 - buf_siz [缓冲区大小].实际上在调用此函数之前,用户必须创建一个缓冲区并为其分配buf_siz的内存.
由于用户可能忘记进行内存分配并只是将指针传递给我的函数,我想检查一下.那么有什么方法可以检查我的函数,看看传递的指针是否真的分配了buf_siz的内存量.??
编辑1:似乎没有标准库来检查它..但是有任何脏的黑客来检查它.. ??
编辑2:我知道我的功能将由一个优秀的C程序员使用...但我想知道我们是否可以检查..如果我们可以,我想听听它...
结论:因此无法检查特定指针是否在函数内分配了内存
GMa*_*ckG 29
您无法检查,除了一些特定于实现的黑客攻击.
指针除了指向的地方之外没有任何信息.您可以做的最好的事情是"我知道这个特定的编译器版本如何分配内存,所以我将取消引用内存,将指针移回4个字节,检查大小,确保它匹配......"等等.您无法以标准方式执行此操作,因为内存分配是实现定义的.更不用说他们可能根本没有动态分配它.
你只需假设你的客户知道如何在C中编程.我能想到的唯一解决方案是自己分配内存并返回它,但这几乎不是一个小小的改变.(这是一个更大的设计变化.)
对于特定于平台的解决方案,您可能对Win32函数IsBadReadPtr
(以及其他类似函数)感兴趣.此函数将能够(几乎)预测从特定内存块读取时是否会出现分段错误.
但是,这在一般情况下不能保护您,因为操作系统对C运行时堆管理器一无所知,并且如果调用者传入的缓冲区不是您期望的那么大,那么堆块的其余部分从操作系统的角度来看,它将继续可读.
我总是初始化指向空值的指针。因此,当我分配内存时,它会改变。当我检查内存是否已分配时,我会这样做pointer != NULL
。当我释放内存时,我还将指针设置为空。我想不出任何方法来判断是否分配了足够的内存。
这并不能解决您的问题,但您必须相信,如果有人编写 C 程序,那么他的技能足以正确地完成它。
下面的代码是我用过一次检查某些指针是否试图访问非法内存的代码.该机制是诱导SIGSEGV.SEGV信号更早地被重定向到私有函数,它使用longjmp返回程序.它有点像黑客但它有效.
代码可以改进(使用'sigaction'代替'signal'等),但它只是给出一个想法.它也可以移植到其他Unix版本,对于Windows我不确定.请注意,不应在程序中的其他位置使用SIGSEGV信号.
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <signal.h>
jmp_buf jump;
void segv (int sig)
{
longjmp (jump, 1);
}
int memcheck (void *x)
{
volatile char c;
int illegal = 0;
signal (SIGSEGV, segv);
if (!setjmp (jump))
c = *(char *) (x);
else
illegal = 1;
signal (SIGSEGV, SIG_DFL);
return (illegal);
}
int main (int argc, char *argv[])
{
int *i, *j;
i = malloc (1);
if (memcheck (i))
printf ("i points to illegal memory\n");
if (memcheck (j))
printf ("j points to illegal memory\n");
free (i);
return (0);
}
Run Code Online (Sandbox Code Playgroud)
我曾经在64位Solaris上使用过脏黑客.在64位模式下,堆从0x1 0000 0000开始.通过比较指针,我可以确定它是数据或代码段p < (void*)0x100000000
中的指针,堆中p > (void*)0x100000000
的指针还是内存映射区域中的指针(intptr_t)p < 0
(mmap返回顶部的地址)可寻址区域).这允许我的程序在同一个映射中保存已分配和内存映射的指针,并让我的map模块释放正确的指针.
但是这种技巧非常难以移植,如果你的代码依赖于类似的东西,那么现在是时候重新思考代码的架构了.你可能做错了什么.
我知道这是一个老问题,但在 C 中几乎一切皆有可能。这里已经有一些 hackish 解决方案,但确定内存是否已正确分配的有效方法是使用 oracle 来代替malloc
, calloc
, realloc
,和free
。这与测试框架(例如 cmocka)检测内存问题(段错误、不释放内存等)的方式相同。您可以在分配内存地址时维护一个分配的内存地址列表,并在用户想要使用您的函数时简单地检查该列表。我为自己的测试框架实现了非常相似的东西。一些示例代码:
typedef struct memory_ref {
void *ptr;
int bytes;
memory_ref *next;
}
memory_ref *HEAD = NULL;
void *__wrap_malloc(size_t bytes) {
if(HEAD == NULL) {
HEAD = __real_malloc(sizeof(memory_ref));
}
void *tmpPtr = __real_malloc(bytes);
memory_ref *previousRef = HEAD;
memory_ref *currentRef = HEAD->next;
while(current != NULL) {
previousRef = currentRef;
currentRef = currentRef->next;
}
memory_ref *newRef = (memory_ref *)__real_malloc(sizeof(memory_ref));
*newRef = (memory_ref){
.ptr = tmpPtr,
.bytes = bytes,
.next = NULL
};
previousRef->next = newRef;
return tmpPtr;
}
Run Code Online (Sandbox Code Playgroud)
对于calloc
、realloc
和free
,您将拥有类似的函数,每个包装器都以 为前缀__wrap_
。真实值malloc
可以通过使用来获得__real_malloc
(与您正在包装的其他函数类似)。每当您想检查内存是否实际分配时,只需迭代链表memory_ref
并查找内存地址即可。如果你找到它并且它足够大,你就肯定知道该内存地址不会使你的程序崩溃;否则,返回错误。在您的程序使用的头文件中,您将添加以下行:
extern void *__real_malloc (size_t);
extern void *__wrap_malloc (size_t);
extern void *__real_realloc (size_t);
extern void *__wrap_realloc (size_t);
// Declare all the other functions that will be wrapped...
Run Code Online (Sandbox Code Playgroud)
我的需求相当简单,所以我实现了一个非常基本的实现,但是您可以想象如何扩展它以拥有更好的跟踪系统(例如,创建一个struct
除了大小之外还跟踪内存位置的系统)。然后你只需编译代码
gcc src_files -o dest_file -Wl,-wrap,malloc -Wl,-wrap,calloc -Wl,-wrap,realloc -Wl,-wrap,free
Run Code Online (Sandbox Code Playgroud)
缺点是用户必须使用上述指令编译源代码;然而,这还远不是我见过的最糟糕的情况。分配和释放内存会产生一些开销,但在添加安全性时总会有一些开销。