gpt4 book ai didi

c - 如何在 C 中取消引用 NULL 指针不会使程序崩溃?

转载 作者:太空狗 更新时间:2023-10-29 16:36:56 25 4
gpt4 key购买 nike

我需要真正的 C 大师的帮助来分析我的代码中的崩溃。不是为了修复崩溃;我可以轻松修复它,但在此之前我想了解这种崩溃是如何可能的,因为对我来说这似乎是完全不可能的。

此崩溃仅发生在客户计算机上,我无法在本地重现它(因此我无法使用调试器单步执行代码),因为我无法获得该用户数据库的副本。我的公司也不允许我只更改代码中的几行并为此客户进行自定义构建(因此我无法添加一些 printf 行并让他再次运行代码),当然客户的构建没有调试符号。换句话说,我的调试能力非常有限。尽管如此,我还是可以确定崩溃并获得一些调试信息。但是,当我查看该信息然后查看代码时,我无法理解程序流如何到达相关行。代码应该在到达该行之前很久就崩溃了。我完全迷失在这里。

让我们从相关代码开始。这是很少的代码:

// ... code above skipped, not relevant ...

if (data == NULL) return -1;

information = parseData(data);

if (information == NULL) return -1;

/* Check if name has been correctly \0 terminated */
if (information->kind.name->data[information->kind.name->length] != '\0') {
freeParsedData(information);
return -1;
}

/* Copy the name */
realLength = information->kind.name->length + 1;
*result = malloc(realLength);
if (*result == NULL) {
freeParsedData(information);
return -1;
}
strlcpy(*result, (char *)information->kind.name->data, realLength);

// ... code below skipped, not relevant ...

已经是这样了。它在 strlcpy 中崩溃。我什至可以告诉你 strlcpy 在运行时是如何真正调用的。 strlcpy 实际上是使用以下参数调用的:
strlcpy ( 0x341000, 0x0, 0x1 );

知道这一点很明显为什么 strlcpy 崩溃。它尝试从 NULL 指针读取一个字符,这当然会崩溃。而且由于最后一个参数的值为1,所以原来的长度一定是0。我的代码这里显然有一个错误,它没有检查名称数据是否为NULL。我可以解决这个问题,没问题。

我的问题是:
这段代码如何首先到达 strlcpy ?
为什么这段代码不会在 if 语句中崩溃?

我在我的机器上本地尝试过:
int main (
int argc,
char ** argv
) {
char * nullString = malloc(10);
free(nullString);
nullString = NULL;

if (nullString[0] != '\0') {
printf("Not terminated\n");
exit(1);
}
printf("Can get past the if-clause\n");

char xxx[10];
strlcpy(xxx, nullString, 1);
return 0;
}

这段代码永远不会通过 if 语句。它在 if 语句中崩溃,这绝对是意料之中的。

因此,如果 name->data 真的为 NULL,那么任何人都可以想到为什么第一个代码可以通过该 if 语句而不会崩溃的任何原因?这对我来说是完全神秘的。这似乎不是确定性的。

重要的额外信息:
两条注释之间的代码真的是 完整 ,没有遗漏任何东西。进一步的申请是 单线程 ,因此没有其他线程可以意外更改后台的任何内存。发生这种情况的平台是 PPC CPU(G4,以防万一)。如果有人对“kind”感到疑惑,这是因为“information”包含一个名为“kind”的“union ”,而 name 又是一个结构体(kind 是一个 union 体,每个可能的 union 体值都是不同类型的结构体);但这一切在这里都不重要。

我很感激这里的任何想法。如果这不仅仅是一种理论,我会更加感激,但如果有一种方法可以验证这个理论对客户确实适用。

解决方案

我已经接受了正确答案,但以防万一有人在 Google 上发现这个问题,以下是真实情况:

指针指向已经被释放的内存。释放内存不会使其全部为零或导致进程立即将其返回给系统。因此,即使内存被错误地释放,它也包含正确的值。在执行“if 检查”时,有问题的指针不是 NULL。

在检查之后,我分配了一些新内存,调用 malloc。不确定 malloc 在这里到底做了什么,但是每次调用 malloc 或 free 都会对进程的虚拟地址空间的所有动态内存产生深远的影响。在 malloc 调用之后,指针实际上是 NULL。不知何故 malloc (或某些系统调用 malloc 使用)将指针本身所在的已释放内存归零(不是它指向的数据,指针本身在动态内存中)。将该内存归零,指针现在的值为 0x0,这在我的系统上等于 NULL,当调用 strlcpy 时,它当然会崩溃。

所以导致这种奇怪行为的真正错误在我的代码中完全不同的位置。永远不要忘记:释放的内存会保留它的值,但它无法控制多长时间。要检查您的应用程序是否存在访问已释放内存的内存错误,只需确保释放的内存在释放之前始终为零。在 OS X 中,您可以通过在运行时设置环境变量来完成此操作(无需重新编译任何内容)。当然,这会大大减慢程序的速度,但您会更早地发现这些错误。

最佳答案

首先,取消引用空指针是未定义的行为。它可以崩溃,而不是崩溃,或者将您的壁纸设置为海绵宝宝的图片。

也就是说,取消引用空指针通常会导致崩溃。所以你的问题可能与内存损坏有关,例如从写入超过您的一个字符串的末尾。这可能会导致延迟效果崩溃。我特别怀疑,因为 malloc(1) 不太可能除非您的程序遇到可用虚拟内存的尽头,否则将失败,如果是这种情况,您可能会注意到。

编辑:OP 指出结果不是 null 而是 information->kind.name->data .这是一个潜在的问题:

没有检查是否information->kind.name->data一片空白。唯一的检查是

if (information->kind.name->data[information->kind.name->length] != '\0') {

让我们假设 information->kind.name->data为空,但信息->种类.名称->长度是,比如说,100。那么这个语句相当于:
if (*(information->kind.name->data + 100) != '\0') {

它不会取消引用 NULL 而是取消引用地址 100。如果这不会崩溃,并且地址 100 恰好包含 0,那么此测试将通过。

关于c - 如何在 C 中取消引用 NULL 指针不会使程序崩溃?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1334929/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com