可能由 strcpy 引起的 HardFault

Posted

技术标签:

【中文标题】可能由 strcpy 引起的 HardFault【英文标题】:HardFault caused probably by strcpy 【发布时间】:2021-01-07 01:39:07 【问题描述】:

以下函数在 LPC1769 中运行。我使用 FreeRTOS 版本 10。 我有HardFault。我已经调试过了,我想我在很长一段时间后解决了这个问题。

如果我运行这个函数,它会给出HardFault。最初,我怀疑substr3 函数中的malloc 导致了它。释放内存分配没有帮助。因此,我开始逐块注释代码,直到在parseMessage 函数中找到更准确的问题位置。

如果我注释掉 /* START OF PROBLEMATIC AREA *//* END OF PROBLEMATIC AREA */ 之间的行 其余代码可以毫无问题地工作。

我在那个代码块中所做的一切,都是在结构变量中赋值。该结构是全局的并已初始化。我相信这些线路最终会导致问题。也许间接地,我不知道那么远。

例如strcpy(productInfoLeft.ucActualID, pid);

如果我运行parseMessage 中的所有代码,它适用于一条或几条消息,它们解析成功,然后 MCU 停止响应。

结构在一个名为common.h的文件中

struct ProductInfoLeft

    char ucActualID[ 7 ];  
    char ucProductName[ 13 ];
    char ucBestBeforeDate[ 13 ];
    char ucPrinted[ 4 ];
    char ucToBePrinted[ 4 ];
    char ucLane[ 3 ];
    char ucLcdNumber [ 2 ];
    char ucPrinterLane [ 3 ];
    char ucSupplierInfo [ 13 ];
;
extern struct ProductInfoLeft productInfoLeft;

struct ProductInfoRight

    char ucActualID[ 7 ];
    char ucProductName[ 13 ];
    char ucBestBeforeDate[ 13 ];
    char ucPrinted[ 4 ];
    char ucToBePrinted[ 4 ];
    char ucLane[ 3 ];
    char ucLcdNumber [ 2 ];
    char ucPrinterLane [ 3 ];   
    char ucSupplierInfo [ 13 ];
;
extern struct ProductInfoRight productInfoRight;

结构初始化发生在一个名为lcdtasks.c的文件中;

struct ProductInfoLeft productInfoLeft =  
    .ucActualID = "",
    .ucProductName = "",
    .ucBestBeforeDate = "",
    .ucPrinted = "",
    .ucToBePrinted = "",
    .ucLane = "",
    .ucLcdNumber = "",
    .ucPrinterLane = "",
    .ucSupplierInfo = ""
;

struct ProductInfoRight productInfoRight =  
    .ucActualID = "",
    .ucProductName = "",
    .ucBestBeforeDate = "",
    .ucPrinted = "",
    .ucToBePrinted = "",
    .ucLane = "",
    .ucLcdNumber = "",
    .ucPrinterLane = "",
    .ucSupplierInfo = ""
;

而解析器函数在另一个名为uarttask.c的文件中;

void parseMessage(char * message)
        //Sample data
        //const char* str = "7E00002A347C31323030302D3132353330387C33302E30372E323032307C31317C33307C33317C31352D31367C31357C317C57656E67657274880D0000";
          
        // Parsing the frame
        char* start;
        char* len;
        char* cmd;
        char* data;
        char* chksum;
        char* end;
        
        stripEOL(message);
        unsigned int messagelen = strlen(message);
        
        start = substr3(message, 0, 2);
        len = substr3(message, 2, 4);
        cmd = substr3(message, 6, 2); 
        data = substr3(message, 8, messagelen-8-4);
        chksum = substr3(message, messagelen-4, 2);
        end = substr3(message, messagelen-2, 2); 
       
         // Converting hex (only for data) to string
        char str[250];
        hex_to_string(data, str, sizeof(str));
    
        // Parsing the data in variables
        //Sample data content to be parsed in variables;
        //char str1[50] ="7|10000-145310|12.10.2018|1|10|0|15-16|15|1|Wegert";
        char pid[6], pname[12], bbdate[10], pnr[2], ltoprinted[3], lprinted[3], planes[5], laneNr[2], lcdNr[1], sinfo[12];
    
        strcpy(pid, strtok(str , "|"));
        strcpy(pname, strtok(NULL , "|"));
        strcpy(bbdate, strtok(NULL, "|"));
        strcpy(pnr , strtok(NULL, "|"));
        strcpy(ltoprinted , strtok(NULL, "|"));
        strcpy(lprinted, strtok(NULL, "|"));
        strcpy(planes, strtok(NULL, "|"));
        strcpy(laneNr, strtok(NULL, "|"));
        strcpy(lcdNr, strtok(NULL, "|"));
        strcpy(sinfo, strtok(NULL, "|"));
     
        uint8_t resultLCDNr1 = strncmp(lcdNr, "1", 1);
        uint8_t resultLCDNr2 = strncmp(lcdNr, "2", 1); 
        
        uint8_t result7E = strcmp(start, pcStart);
        uint8_t result0D = strcmp(end, pcEnd);   
        uint8_t result2A = strcmp(cmd, pcProductChange);
        uint8_t result30 = strcmp(cmd, pcSupplierChange);
      
        char planeleft[2], planeright[2], tempplanes[5];
        strcpy(tempplanes, planes); // If this is used, the next strcpy causes lprinted variable's first element to be "0\"
        strcpy(planeleft, strtok(tempplanes , "-"));
        strcpy(planeright, strtok(NULL , "-"));  
     
/* START OF PROBLEMATIC AREA   */  
        if (result7E == 0 && result0D == 0)
            if (result2A == 0) //Product Change
                if (resultLCDNr1 == 0)
                    strcpy(productInfoLeft.ucActualID, pid);
                    strcpy(productInfoLeft.ucPrinterLane, planeleft);
                    strcpy(productInfoLeft.ucProductName, pname);
                    strcpy(productInfoLeft.ucBestBeforeDate, bbdate);
                    strcpy(productInfoLeft.ucPrinted, lprinted);
                    strcpy(productInfoLeft.ucToBePrinted, ltoprinted);
                    strcpy(productInfoLeft.ucLane, laneNr);
                    strcpy(productInfoLeft.ucLcdNumber, lcdNr);
                    strcpy(productInfoLeft.ucSupplierInfo, sinfo);
                else if (resultLCDNr2 == 0)
                    strcpy(productInfoRight.ucActualID, pid);
                    strcpy(productInfoRight.ucPrinterLane, planeright);
                    strcpy(productInfoRight.ucProductName, pname);
                    strcpy(productInfoRight.ucBestBeforeDate, bbdate);
                    strcpy(productInfoRight.ucPrinted, lprinted);
                    strcpy(productInfoRight.ucToBePrinted, ltoprinted);
                    strcpy(productInfoRight.ucLane, laneNr);
                    strcpy(productInfoRight.ucLcdNumber, lcdNr); 
                    strcpy(productInfoRight.ucSupplierInfo, sinfo);
                else
                    return;
                
                
                SetProductChangeOnLCD(lcdNr);
            
            if (result30 == 0) //Supply Change
                if (resultLCDNr1 == 0)
                    strcpy(productInfoLeft.ucActualID, pid);
                    strcpy(productInfoLeft.ucPrinterLane, planeleft);
                    strcpy(productInfoLeft.ucProductName, pname);
                    strcpy(productInfoLeft.ucBestBeforeDate, bbdate);
                    strcpy(productInfoLeft.ucPrinted, lprinted);
                    strcpy(productInfoLeft.ucToBePrinted, ltoprinted);
                    strcpy(productInfoLeft.ucLane, laneNr);
                    strcpy(productInfoLeft.ucLcdNumber, lcdNr);
                    strcpy(productInfoLeft.ucSupplierInfo, sinfo);
                else if (resultLCDNr2 == 0)
                    strcpy(productInfoRight.ucActualID, pid);
                    strcpy(productInfoRight.ucPrinterLane, planeright);
                    strcpy(productInfoRight.ucProductName, pname);
                    strcpy(productInfoRight.ucBestBeforeDate, bbdate);
                    strcpy(productInfoRight.ucPrinted, lprinted);
                    strcpy(productInfoRight.ucToBePrinted, ltoprinted);
                    strcpy(productInfoRight.ucLane, laneNr);
                    strcpy(productInfoRight.ucLcdNumber, lcdNr); 
                    strcpy(productInfoRight.ucSupplierInfo, sinfo);
                else
                    return;
                
                SetSupplierChangeOnLCD(lcdNr);
                    
        
/* END OF PROBLEMATIC AREA   */ 
       
     free(start);
     free(len); 
     free(cmd); 
     free(data);
     free(chksum); 
     free(end); 
    

子串函数:

char *substr3(char const *input, size_t start, size_t len)  
    char *ret = malloc(len+1);
    memcpy(ret, input+start, len);
    ret[len]  = '\0';
    return ret;

【问题讨论】:

数据是什么样的? NTW:请给我们看看 substr() 函数。 不是sizeof。您需要使用strlen。并且强烈建议不要保持当前代码不变。它充满了缓冲区溢出漏洞。输入意外的输入会在内存中炸出一个洞。 @user3121023,因为,我还要为\0留一个空间? @user3121023,确实,还有更多。但是,这只是错误的样本。这是正确的样本; char str1[50] ="7|10000-145310|12.10.2018|1|10|0|15-16|15|1|Wegert"; @Sener 在您的示例字符串中,第二个标记 10000-145310 有 12 个字符。计算 nul 终止符 strcpy(pname, strtok(NULL , "|")); 将超出分配的 char pname[12]; 【参考方案1】:

仅供参考,我想分享我的发现和问题的解决方案。

有两个问题。一个是数组大小,其中 char 用于 strcpy。没有像一些贡献者提到的那样正确设置。

一旦数组大小固定,另一个问题就会以更清晰的方式暴露出来。这是关于malloc的。出于某种原因,尽管在各种资源中有些评论另有说明,但如果您在 FreeRTOS 实现中使用 malloc,您可能会遇到 HardFault。一旦我切换到 FreeRTOS 建议的 malloc 和 free 函数,事情就变得平淡了。 HardFault 问题神奇地消失了。

我刚刚放置了这两个包装函数(在一个公共文件中的某个位置),甚至没有更改我的 malloc 和 free 调用。;

创建与内置 FreeRTOS 堆一起使用的 malloc/free 函数非常简单。我们只是包装 pvPortMalloc/pvPortFree 调用:

void* malloc(size_t size)

    void* ptr = NULL;

    if(size > 0)
    
        // We simply wrap the FreeRTOS call into a standard form
        ptr = pvPortMalloc(size);
     // else NULL if there was an error

    return ptr;

void free(void* ptr)

    if(ptr)
    
        // We simply wrap the FreeRTOS call into a standard form
        vPortFree(ptr);
    

请注意:您不能将其与堆模式 #1 一起使用,但与其他模式(2、3、4 和 5)一起使用。我建议开始使用portable/MemMang/heap_4.c

【讨论】:

以上是关于可能由 strcpy 引起的 HardFault的主要内容,如果未能解决你的问题,请参考以下文章

小知识点记录

查找可能由线程锁定引起的性能问题(可能)

这可能由 CredSSP 加密 oracle 修正引起的。

(C)strcpy ,strncpy与strlcpy

strncpy()函数的功能

strncpy()函数的功能