2015-04-03 92 views
1

我创建了一个程序,它定期从网站下载一个文本文件,格式为csv,并对其进行解析,然后提取相关数据,然后显示。防止字符串操作崩溃导致整个应用崩溃

我注意到,偶尔每隔几个月左右就会崩溃。考虑到数据下载和解析的周期可能每5分钟甚至更少发生,崩溃很少见。我很确定它在解析字符串并提取数据的函数内崩溃。当它崩溃时,会发生拥塞的互联网连接,即大量下载和/或连接速度较慢。有时远程站点可能正在处理损坏或不完整的数据。

我使用了一个测试应用程序,它在处理之前保存要处理的数据,并且确实发现崩溃发生时它并未完成。

我调整了函数来适应一些无效或不完整数据的情况,以及检查所有返回值。我还检查用于连接远程站点并下载数据的各种函数的返回值。如果返回值表示没有成功,则不会再继续。

功能的核心使用strsep()通过数据走路,提取出来的信息:

/ * 
    * delimiters typically contains: <;>, <">, < > 
    * strsep() is used to split part of the string using delimiter 
    * and copy into token which then is copied into the array 
    * normally the function stops way before ARRAYSIZE which is just a safeguard 
    * it would normally stop when the end of file is reached, i.e. \0 
    */ 
for(n=0;n<ARRAYSIZE;n++) 
{ 
    token=strsep(&copy_of_downloaded_data, delimiters); 
    if (token==NULL) 
    break; 

    data->array[n].example=strndup(token, strlen(token)); 

    if (data->array[n].example!=NULL) 
    { 
    token=strsep(&copy_of_downloaded_data, delimiters); 
    if (token==NULL) 
     break; 

    (..) 

    copy_of_downloaded_data=strchr(copy_of_downloaded_data,'\n'); /* find newline */ 
    if (copy_of_downloaded_data==NULL) 
    break; 

    copy_of_downloaded_data=copy_of_downloaded_data+1; 
    if (copy_of_downloaded_data=='\0') /* find end of text */ 
    break; 
} 

因为我怀疑我能不能解释其中的数据可以被破坏我想所有的方法想知道是否有一种方法来编程,以便在运行时的函数在数据损坏的情况下不会使整个应用程序崩溃。

如果这是不可能的,我可以做些什么来使它更健壮。

编辑:当数据突然结束,其中一个场的中间被切割的,碰撞的一个可能的实例是即

“测试”,“示例”,“这个数据是布洛克

至少我注意到它通过保存的数据看,但我发现它不是一致的。会不会有压力测试它是以下建议。

回答

6

做会找出最好的事情什么输入导致函数崩溃,并修复功能,使其不会崩溃。由于该函数正在执行字符串处理,因此应该可以通过为其提供大量虚拟/测试数据(或者如果它是导致崩溃的特定输入,则将其“喂”给它“正确的”测试数据)。你基本上想要对功能进行折磨 - 测试,直到你找到如何让它按需崩溃;那时你就可以开始调查崩溃的确切位置和原因了,一旦你明白了,修复崩溃的必要改变对你来说可能会变得很明显。

在valgrind下运行该程序也可能会导致您发现该错误。

如果由于某种原因您无法修复该错误,则另一种选择是产生子进程并在子进程内运行错误代码。这样,如果它崩溃,只有子进程丢失,而不是父进程。 (你可以通过调用fork()在大多数操作系统下产生子进程;当然,你需要为子进程提供一些方法来将结果传回给父进程)。(请注意,这样做是一种混乱,并且可能效率不高,并且如果有人能够发送程序输入的恶意程序可以找出如何操作错误的方法,也可能会在应用程序中引入安全漏洞去控制孩子的过程 - 所以我不推荐这种方法!)

+1

如果有人可以控制孩子的过程,他们现在也可以控制过程 – 2015-04-03 22:19:30

+0

这是一个非常好的点科尔! – 2015-04-03 22:51:02

+0

非常好的建议,谢谢。我一直在考虑喂它测试数据。在开发的早期阶段,我确实使用了valgrind,并且修复了所有可以解决的问题。这个错误每隔几个月就会触发一次,虽然有时候会在一天内出现几次,然后在几周内没有出现,这使得它在调试器和类似工具中运行变得不太实际。 – aseq 2015-04-03 23:24:28

1

coredump指向什么?

strsep - 没有内存同步机制,因此将其保护为关键部分(当您执行strsep时将其锁定)?

看看strsep是否可以处理大块(ARRAYSIZE不会帮你在这里)。线程/程序接收copy_of_downloaded_data的

堆栈大小(我知道你只是引用它,看看它接收它的功能。)

+0

当我通过gdb运行应用程序时,它会在我上面提到的函数中崩溃。 copy_of_downloaded_data是原始下载数据的实际单独副本,因为strsep()实际上会更改数据。锁定它意味着什么? – aseq 2015-04-03 23:17:11

1

我会建议人们应该尝试写,保持跟踪代码字符串长度是故意的,并不关心字符串是否是零终止的。即使空指针被称为“十亿美元的错误”(*),我认为零终止的字符串更糟糕。虽然在某些情况下,使用零终止字符串的代码可能比跟踪字符串长度的代码“更简单”,但确保没有任何东西可以导致字符串处理代码超出缓冲区边界的额外工作量超出了处理已知长度的字符串。

如果,例如,一个人想长度length1length2的字符串的串联存储到缓冲器中,如果长度BUFF_SIZE,可以测试是否容易length1+length2 <= BUFF_SIZE如果一个不期望的字符串是空终止,或length1+length2 < BUFF_SIZE如果有人期望每个字符串都有一个无用的空字节。当使用以零结尾的字符串时,必须在连接之前确定两个字符串的长度,并且这样做可以使用memcpy()而不是strcpy()或无用strcat()。 (*)在许多情况下,拥有一个可识别的无效指针要比不要指向任何有意义的指针必须指向无意义的指针要好得多。许多与空指针相关的问题实际上源于实现失败以捕获具有空指针的算术;将空指针归咎于本来可能会发生但未被避免的问题是不公平的。

+0

空终止和零终止是一回事吗? – 2015-04-03 22:20:49

+0

@ColeJohnson:我更喜欢术语“零终止”,因为终止符是一个值为零的字节。在ASCII码中,零码被称为“空字节”,因为它不会导致电传打字机做任何事情,并且其纸带表示是空白的,因此字符串末尾的零字节通常称为“空字节“,但字符串方法的重要性不在于字符代码表示”空字符“,而是数字值为零。即使C实现使用空字符为42的字符集,strcpy仍会复制到第一个零字节。 – supercat 2015-04-03 22:29:19

+1

我认为它可能会使函数过于复杂,以考虑字符串大小,而不是空终止。然而,你可能有一个问题,因为一个潜在的错误很可能在'/ 0'测试中。顺便说一句,你会不会更好地使用strncpy()和strncat()?据了解,由于缓冲区溢出问题,strcpy()和strcat()以及其他类似软件在当今被人们所诟病。 – aseq 2015-04-03 23:34:52