【问题标题】:Why a code that used to work fine can crash in C? [closed]为什么以前运行良好的代码会在 C 中崩溃? [关闭]
【发布时间】:2023-04-04 10:47:01
【问题描述】:

我编写了一个代码来帮助从特定字符串中提取数据。 示例:如果字符串是“ABCD*” 该代码将帮助我定义“*”之前最后一个字符的索引

char *magicchar;
int IndexofMagicchar =0;
magicchar=strchr(InputData,"*");
IndexofMagicchar = (int)(magicchar - InputData);

现在代码可以正常工作,直到您使用不包含“*”的 InputData,然后服务将崩溃。 解决方法只是在 magicchar 变量上添加一个简单的测试:

char *magicchar;
int IndexofMagicchar =0;
magicchar=strchr(InputData,"*");
if (magicchar!=NULL)            
    IndexofMagicchar = (int)(magicchar - InputData);

我的问题是,即使输入不包含“*”,代码也能正常工作,为什么崩溃会变得系统化?

【问题讨论】:

  • edit您的问题提供一个minimal reproducible example,可用于重现问题。
  • 首先strchr(InputData, "*")是错误的。 strchr 的第二个参数是 int。正确的调用应该是strchr(InputData, '*')
  • 您是否更改了编译器?编译开关?操作系统?程序中的其他代码?许多事情都会影响这一点。如果程序布局发生变化,IndexOfMagicchar 的计算可能会导致较大的正值,而不是您测试的负值。如果编译器发生更改,则在返回空指针时未定义 magicchar - InputData 的事实可能会导致优化器以导致崩溃的方式转换您的程序。
  • 当您将指针不是 char 传递给 strchr 时,该程序一直无法正常工作
  • 您没有收到magicchar=strchr(InputData,"*"); 的编译器警告?警告是否开启?

标签: c string compiler-errors crash segmentation-fault


【解决方案1】:

现在代码可以正常工作,直到您使用不包含“*”的 InputData

不可信。正如其他人评论的那样,strchr() 的第二个参数是int,您可以通过它直接传递要搜索的char 的值。相反,您正在传递一个指向包含该值的对象的指针,试图间接传递该值,除非完全意外,否则这将无法按预期工作。但是,这样的程序可能不会崩溃,而是会产生错误的结果。

但是,为了争论,我们假设实际代码在这方面实际上是正确的:

char *magicchar;
int IndexofMagicchar = 0;
magicchar = strchr(InputData, '*');
IndexofMagicchar = (int) (magicchar - InputData);

该代码片段仍然有缺陷,因为strchr在找不到指定字符的情况下返回一个空指针,并且当任一操作数为空指针时未定义指针差异.实际上,仅当两个操作数都指向或刚刚超过同一数组的末尾时才定义它。程序崩溃是由于计算该差异而导致的 UB 的最佳可能表现之一。然而,我倾向于猜测,它实际上并不是计算崩溃的差异,而是稍后使用 IndexofMagicchar 的值。

解决方法只是在 magicchar 变量上添加一个简单的测试:

char *magicchar;
int IndexofMagicchar =0;
magicchar=strchr(InputData,"*");
if (magicchar!=NULL)            
    IndexofMagicchar = (int)(magicchar - InputData);

这是一个合适的解决方案,以错误引号问题为模。如果magicchar 被计算为空指针,则不应将其用作指针差异操作数。它还为IndexofMagicchar 留下一个值,该值是任何字符串的有效索引,这可能是避免崩溃的关键效果。

但是请注意,它仍然存在程序在以后做错事的风险,因为如果strchr 返回一个空指针,那么IndexOfMagicchar 的结果值不是 '*' 的出现。这可能是一个健壮性问题——例如,它可能仅在程序接收到格式错误的输入时才会出现。这就是制造安全漏洞的原因,尽管您的特定程序的风险可能很小。

我的问题是,即使输入不包含“*”,代码也能正常工作,为什么崩溃会变得系统化?

我不接受原始代码“工作正常”。它可能没有崩溃,但那是另一回事。未定义的行为可能表现为看似工作正常,但任何事情都可能发生,无论出于何种原因或没有明显的原因。

实际上,我倾向于猜测原始错误代码导致程序执行越界数组访问,但碰巧碰到了可访问内存,以及程序中其他地方的更改,或者在编译选项、编译器或运行时上下文中,导致这些 OOB 访问开始访问不可访问的内存。然而细节并不重要:代码是错误的,需要修复。呈现的第二个版本仍然需要修复,即使它没有崩溃。

【讨论】:

    【解决方案2】:
    1. 使用正确的类型(不是int
    2. 写安全strnchr函数
    3. strchr 的第二个参数是char 而不是char *。字符串字面量衰减为 char * 指针
    char *mystrnchr(const char *restrict str, size_t size, int ch)
    {
        char *result = NULL;
    
        while(size && *str != ch && *str) {str++;size--;}
        if(size && *str) result = (char *)str;
        return result;
    }
    
    /* somewhere in another function */
    
    char *magicchar;
    ptrdiff_t IndexofMagicchar =0;
    magicchar=mystrnchr(InputData, size_of_InputData, '*');
    if(magicchar)
        IndexofMagicchar = magicchar - InputData;
    else {/* handle not found*/ }
    

    【讨论】:

    • 要像其他 str...() 函数一样执行,*str != ch 应该是 (unsigned char) *str != (unsigned char) ch 或类似的。
    • @chux 显然是的 len
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-04
    • 1970-01-01
    相关资源
    最近更新 更多