Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/unix/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在C中取消对空指针的引用而不使程序崩溃?_C_Unix_Crash_Powerpc - Fatal编程技术网

如何在C中取消对空指针的引用而不使程序崩溃?

如何在C中取消对空指针的引用而不使程序崩溃?,c,unix,crash,powerpc,C,Unix,Crash,Powerpc,我需要一个真正的C专家的帮助来分析代码中的崩溃。不是为了修复撞车事故;我可以很容易地修复它,但在这样做之前,我想了解这个崩溃是如何可能的,因为对我来说它似乎完全不可能 此崩溃只发生在客户机器上,我无法在本地复制它(因此我无法使用调试器单步执行代码),因为我无法获取此用户数据库的副本。我的公司也不允许我只更改代码中的几行,并为该客户定制构建(因此我不能添加一些printf行并让他再次运行代码),当然,该客户有一个没有调试符号的构建。换句话说,我的除虫能力非常有限。尽管如此,我还是可以确定崩溃并获得

我需要一个真正的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崩溃的原因就相当明显了。它试图从空指针读取一个字符,这当然会崩溃。由于最后一个参数的值为1,因此原始长度必须为0。我的代码显然有一个bug,它无法检查名称数据是否为空。我可以解决这个问题,没问题

我的问题是:
这段代码怎么可能首先到达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值都是一个不同类型的结构);但这一切在这里并不重要

我很感激这里的任何想法。如果这不仅仅是一个理论,而是如果有一种方法可以证明这个理论对客户来说是正确的,我会更加感激

解决方案 我已经接受了正确的答案,但为了防止有人在谷歌上发现这个问题,下面是实际发生的情况:

指针指向已经释放的内存。释放内存不会使其全部为零,也不会导致进程立即将其返回给系统。因此,即使内存被错误地释放,它仍然包含正确的值。执行“如果检查”时,相关指针不为空

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


因此,导致这种奇怪行为的真正bug位于我的代码中完全不同的位置。永远不要忘记:释放的内存保持它的值,但它超出了您的控制多久。要检查应用程序是否存在访问已释放内存的内存错误,只需确保释放的内存在释放前始终归零即可。在OSX中,您可以通过在运行时设置环境变量来实现这一点(无需重新编译任何内容)。当然,这会大大降低程序的速度,但您会更早地发现这些错误。

您可能遇到堆栈损坏。您所引用的代码行可能根本没有被执行。

据我所知,取消引用空指针的效果未被标准定义

根据C标准6.5.3.2/4:

如果为指针指定了无效值,则一元*运算符的行为将被取消定义


因此,可能会发生崩溃,也可能不会。标准未定义取消引用空指针的行为。它不能保证崩溃,而且通常不会崩溃,除非您确实尝试写入内存

哇,真奇怪。有一件事在我看来确实有点可疑,尽管它可能不会起作用:

如果信息和数据是好的指针(非null),但information.kind.name为null,会发生什么情况。直到strlcpy行,您才解除对该指针的引用,因此,如果该指针为null,则在此之前它可能不会崩溃。当然,在您取消引用数据[1]以将其设置为\0之前,它也会崩溃,但由于某种侥幸,您的程序可能恰好具有对0x01的写访问权限,而不是0x00


另外,我看到您在一个位置使用信息->name.length,而在另一个位置使用信息->kind.name.length,不确定这是打字错误还是需要的。首先,取消引用空指针是未定义的行为。它可以崩溃,而不是崩溃,或设置你的墙
if (information->kind.name->data[information->kind.name->length] != '\0') {
if (*(information->kind.name->data + 100) != '\0') {
if (information->kind.name->data[information->kind.name->length] != '\0') {
char * p = NULL;
p += i;
int a = 0xffff0000;
short b = (short) a; //b could be 0 if lower bits are used
if (information->kind.name->data[information->kind.name->length] != '\0') {
if (*result == NULL) 
    freeParsedData(information);
    return -1;
}
if (*result == NULL)
{ 
     freeParsedData(information);
     return -1;
}
if (*result == NULL) { 
    freeParsedData(information);
    return -1;
}
if(false)
{
    if(something == stuff)
    {
        doStuff();

    .. snip ..

    if(monkey == blah)
        some->garbage= nothing;
        return -1;
    }
}
crash();
if (information->kind.name->data[information->kind.name->length] != '\0') {
    if (information == NULL) {
      return -1; 
    }
    if (information->kind == NULL) {
      return -1; 
    }