Elusive Stack Smashing error: why does my string_to_float function sometimes crash?

Elusive Stack Smashing error: why does my string_to_float function sometimes crash?

我正在 Raspberry Pi(我认为是 3)运行 架构 linux 上使用一些遗留 C 代码 运行。 作为应用程序启动的一部分,它逐行读取文件并将每一行存储到某个自定义结构中。

100 次有 99 次,这很好用,我们从此过上了幸福的生活。在那个奇怪的结果中,我得到了一个 stack smashing detected 错误,强制重置。

我已经验证了这些行是正确的并且格式一致。

我已经将它缩小到另一个在文件解析过程中被调用两次的函数:

const char* num = "+-.0123456789";
const char* Mults = "pnumkMGT";

float engtof(char* s)
{
printf("engtof: %s\n",s);
    float f, g = 1;
    char m, *q = 0, w[32] = {0};
    int i;

    for(i = 0; i < 32; i++) w[i] = 0; // clear w

    strcpy(w, s);
    for(q = w; strchr(num, *q) != NULL; q++);
puts(q);       // sometimes prints some garbage after the desired char
    if(*q == 'E' || *q =='e')
        return atof(w);

    if(*q != 0 && strchr(Mults, *q) != NULL)
    {
        m = *q;
        g = getMult(m); // behavior already verified
        *q = 0;
    }
puts(w);       // seems to reach here before sometimes stack smashing, but w still looks right...
    f = atof(w);
    f *= g;
    return f;
}

(忽略各种 printf 和 puts 语句。它们用于调试,因为我们不知道如何从 NetBeans 12.3 进行远程调试)

这应该采用工程格式的字符串(s = "{value}{multiplier for power of 10}{unit}")(s="xkV" 或 s="a.bcmA" ,例如)并将其转换为浮点值(示例中为 x000 或 0.00abc)。当一切正常时,它在启动期间被调用约 300 次,但当它不起作用时,我们在第一次调用时堆栈 smash(通常使用 s =“0”)。

到目前为止,我在堆栈粉碎上发现的所有内容都在谈论缓冲区溢出和超出范围的索引,但据我所知,这些都不适用于此处。诚然,我对指针的理解一般般...

如果每次都发生这种情况,我相信我能找到问题所在,但由于这种情况很少发生,所以我不知道为什么会发生这种情况。有人对此有任何见解吗?

编辑: 这是在启动期间调用 engtof 的函数:

void loadCalFactors()
{
printf("loadCalFactors\n");
    FILE *fp;
    char s[120], w[80] = {0}, *p;
    int i, j;

    fp = fopen(fileCF, "r");
    if(fp == NULL)
    {
        sprintf(s, "Can't open: %s\n", fileCF);
        printf(s);
        netWrite(s);
        return;
    }

    for(i = 0; i < N_CALFACTOR; i++)
    {
        j = 0;
        fgets(s, 80, fp);
//puts(s);
        p = strtok(s, ",");

        while(p != NULL)
        {
//puts(p);
            switch(j)
            {
                case 0:
//puts("Header");
                    strcpy(w, p);// header
                    j++;
                    break;
                case 1:
//puts("Gain");
                    calFactors[i].g = engtof(p);
                    j++;
                    break;
                case 2:
//puts("Offset");
                    calFactors[i].os = engtof(p);
                    j++;
                    break;
                case 3:
//puts("Unit");
                    strcpy(calFactors[i].unit, p);
                    j++;
                    break;
            }
//printf("j= %i, p= %s\n",j,p);
            p = strtok(NULL, ",");
        }
    }
    fclose(fp);
puts("loadCalFactors done");
}

似乎只有第一次调用 engtof 时才会出现问题。如果它过去了,它会一直通过文件就好了。

这里是 fileCF 的前几行:

Input Source 5V Range,0,0,V
Input Voltmeter 5V Range,0,0,V
Input Source -5V Range,0,0,V
Input Voltmeter -5V Range,0,0,V
Input Source 50V Range,0,0,V
Input Voltmeter 50V Range,0,0,V
Input Source -50V Range,0,0,V
Input Voltmeter -50V Range,0,0,V

这里是 getMult:

float getMult(char m)
{
puts("getMult");
    float f;
printf("m=%c\n",m);
    switch(m)
    {
        case 'p': f = 1E-12; break;
        case 'n': f = 1E-9; break;
        case 'u': f = 1E-6; break;
        case 'm': f = 1E-3; break;
        case 'k': f = 1E3; break;
        case 'M': f = 1E6; break;
        case 'G': f = 1E9; break;
        case 'T': f = 1E12; break;
        default: f = 1; break;
    }
//printf("f=%g\n",f);
    return f;
}

为了帮助制作 MVE,以下是我们的所有内容:

#include <fcntl.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <time.h>
#include <wiringPi.h>
#include <math.h>
#include <ctype.h>

我什至不知道上面分享的部分中使用了多少这些,我敢肯定其中 none 已充分发挥其潜力。

for(q = w; strchr(num, *q) != NULL; q++);
如果您使用指向字符串 "0"s 调用函数,

将调用未定义的行为。此循环只会在 q 指向不在 num 中的 non-null 字符时终止,除非 q 指向 [=18] 的边界,否则不会出现这种情况=].

如果您希望 q 指向 w 中不属于 num 的第一个字符,则可以使用以下行:

q = w + strspn( w, num );

也可以通过在循环中调用 strchr 来解决这个问题(效率较低),正如您所尝试的那样。然而,正确的做法是:

for(q = w; *q != '[=12=]' && strchr(num, *q) != NULL; q++)
    ;

添加表达式 *q != '[=23=]' 将防止指针 q 超出 w 的范围(假设字符串具有空终止字符)。