snprintf随机溢出并将垃圾打印到文件中。救命

时间:2010-10-21 23:28:27

标签: c programming-languages printf

这是我的代码,基本上是我测试过的4台计算机,它们都可以完美地处理非常大的数据大小,例如大小达500mb的文本文件,但是当我在服务器上使用真实数据甚至文件运行它们时小到6mb似乎在某处溢出并将垃圾写入我的文件末尾。

以下是整个功能的来源,因此人们可以有更深入的了解

/** Reads values from tagname between start_time and end_time which are strings in the format 
 of 01/01/1970-12:00, a null string is treated as 01/01/1970, values are stored in
 "tagname".csv */
int ReadValues(char * tagname, char * start_time, char * end_time, char * working_directory, char * new_tag_name)
{
 long lRet;
 int  number_of_samples;
 int     loop;
 IHU_DATA_SAMPLE  * pSamples=NULL;
 IHU_TIMESTAMP  StartTime;
 IHU_TIMESTAMP  EndTime;
 FILE *stream;
 char outputFileName[200];
 char szQuality[100];
 char    newTempTagName[100];
 int  Year;
 int  Month;
 int  Day;
 int  Hour;
 int  Minute;
 int  Second;
 long Subsecond;
 int date[10];

    //if we want to change the tagname do it now
 if(new_tag_name != 0){
       strncpy(newTempTagName, new_tag_name, 100);
    } else {
       strncpy(newTempTagName, tagname, 100);  
    } 

 // if the tagname contains a character that is invalid for a filename then we have to make a name
 if ( (strstr(newTempTagName, "/")) || (strstr(newTempTagName, "\\")) || (strstr(newTempTagName, ":")))
 {

  sprintf(outputFileName, "MadeUpTag%d", MadeUpTagCount++);
 }
 else
 {

        //If a working directory was passed in use it
        if(!working_directory){           
            strncpy(outputFileName, newTempTagName,199);   
            } else {
      strcpy(outputFileName, working_directory); //Copy the working directory to the filename
      // Append a / on the end of the working directory if it doesn't already have one
         if(outputFileName[strlen(outputFileName)-1] == '\"'){                   
                outputFileName[strlen(outputFileName)-1] = 0;
            } 
            if(outputFileName[strlen(outputFileName)-1] != '\\'){         
                strncat(outputFileName, "\\", 199);                                
            }
            strncat(outputFileName, newTempTagName, 199);   //Copy the tagname to the end of the working directory
        }

 }

 //Add the csv file extension
 strcat(outputFileName, ".csv");
 #ifdef DEBUG
 printf("Output filename: %s\n", outputFileName);
 #endif

 stream = fopen(outputFileName, "w");
 if( stream == NULL ) {
  printf("The file %s can not be opened\n", outputFileName );
 } else {

  //If start_time == 0 we want to start at 1970
  if(start_time == 0){
   // we want all the data so use an old start time
   struct tm local;
   local.tm_year = date[2] - 1900;
   local.tm_mon = date[1] - 1;
   local.tm_mday = date[0];
   local.tm_hour = date[3];
   local.tm_min = date[4];
   local.tm_sec = 0;
            time_t utc_seconds = mktime(&local);
   StartTime.Seconds = (long)utc_seconds;
            StartTime.Subseconds = 0; 

  } else {
            //we have been supplied a start time
   #ifdef DEBUG
            printf("Start Time: ");
   #endif
            lRet = convert_date(start_time, &StartTime);
   #ifdef DEBUG

   printf("Seconds %ld \n", StartTime.Seconds);
   #endif
  }

  //if end_time == 0 we want to go all the way until now.
  if(end_time == 0){
   // end time of 0 means now
   memset(&EndTime, 0, sizeof(EndTime));
  } else {
            //we have been supplied an end time
   #ifdef DEBUG
            printf("End Time: ");
   #endif
            lRet = convert_date(end_time, &EndTime);
   #ifdef DEBUG
            printf("Seconds %ld \n", EndTime.Seconds);
   #endif
  }

  // API will determine actual samples that are in that time range
  number_of_samples=0;

  // API will allocate the memory
  pSamples = NULL;
     //timeTaken();
  lRet = ihuReadRawDataByTime
  (
   serverhandle,  // the handle returned from the connect
   tagname,   // the single tagname to retrieve 
   &StartTime,   // start time for query
   &EndTime,   // end time for query
   &number_of_samples, // will be set by API
   &pSamples   // will be allocated and populated in the user API
  );

        char temp[100];
        char Header[100];
        char lengthOfHeader = 0;
        char SampleAndTag[100];
        int  lengthOfSampleAndTag = 0;
        char ActualSample[100];
        int  lengthOfActualSample = 0;
        char NumberOfSamples[100];
        int  lengthOfNumberOfSamples = 0;
  int  oldQualityStatus = 99999;

  if (lRet == ihuSTATUS_OK)
  { 

      // successful read
            lengthOfHeader = snprintf(Header, 100, "[Data]\nTagname,TimeStamp,Value,DataQuality\n");
            if(lengthOfHeader < 0){
                printf("ERROR WRITING TO BUFFER!\n");                                 
            } else {
                if(fwrite(Header, 1, lengthOfHeader, stream) < lengthOfHeader){
                        printf("ERROR WRITING TO FILE!\n");
                }
            }

   for (loop = 0;loop < number_of_samples;loop++)
   {
                struct tm * local;
                local = localtime(&pSamples[loop].TimeStamp.Seconds);
                Month = local->tm_mon + 1;
                Day = local->tm_mday;
                Year = local->tm_year + 1900;
                Hour = local->tm_hour;
                Minute = local->tm_min;
                Second = local->tm_sec;
                Subsecond = pSamples[loop].TimeStamp.Subseconds;

                //lengthOfSampleAndTag = snprintf(SampleAndTag, 100, "Sample %d, %s",loop, newTempTagName);
                lengthOfSampleAndTag = snprintf(SampleAndTag, 100, "%s", newTempTagName);
                if(lengthOfSampleAndTag < 0){
                   printf("ERROR WRITING TO BUFFER!\n");                                 
                } else {
                    if(fwrite(SampleAndTag, 1,lengthOfSampleAndTag, stream) < lengthOfSampleAndTag){
                        printf("ERROR WRITING TO FILE!\n");
                    }
                }

                //Doing the formatting ourselves manually per character saves about 20% of cpu time
                //on large databases this can save hours
                temp[0] = ',';
    temp[1] = ' ';
    temp[2] = Month/10 + 0x30; //Tens
    temp[3] = Month%10 + 0x30; //units
    temp[4] = '/';
    temp[5] = Day/10 + 0x30;
    temp[6] = Day%10 + 0x30;
    temp[7] = '/';
    temp[8] = Year/1000 + 0x30;
    temp[9] = '0';
    temp[10] = (Year%100)/10 + 0x30;
    temp[11] = (Year%100)%10 + 0x30;
    temp[12] = ' ';
    temp[13] = Hour/10 + 0x30; //Tens
    temp[14] = Hour%10 + 0x30; //units
    temp[15] = ':';
    temp[16] = Minute/10 + 0x30;
    temp[17] = Minute%10 + 0x30;
    temp[18] = ':';
    temp[19] = Second/10 + 0x30;
    temp[20] = Second%10 + 0x30;
    temp[21] = '.';
    temp[22] = '0';
    temp[23] = '0';
    temp[24] = '0';
    temp[25] = ',';
    temp[26] = ' ';
    temp[27] = 0;  //Null termination

                //This is to save copying the string if it is the same
                if(oldQualityStatus != pSamples[loop].Quality.QualityStatus){
                    oldQualityStatus = pSamples[loop].Quality.QualityStatus;
        switch(pSamples[loop].Quality.QualityStatus)
        {
         case ihuOPCBad:
          strncpy(szQuality,"Bad",99);
          break;
         case ihuOPCUncertain:
          strncpy(szQuality,"Uncertain",99);
          break;
         case ihuOPCNA:
          strncpy(szQuality,"NotAvailable",99);
          break;
         case ihuOPCGood:
          strncpy(szQuality,"Good",99);
          break;
         default:
          strncpy(szQuality,"Really unknown",99);
          break;
        }
                }

    if ( pSamples[loop].ValueDataType == ihuFloat )
    {
                    //lengthOfActualSample = snprintf(ActualSample, 100,"%s%8.7f, %s, Type:Float\n",temp,pSamples[loop].Value.Float,szQuality);
                    lengthOfActualSample = snprintf(ActualSample, 100,"%s%8.7f, %s\n",temp,pSamples[loop].Value.Float,szQuality);
                    if(lengthOfActualSample < 0){
                         printf("ERROR WRITING TO BUFFER!\n");  
                    } else {
                        if(fwrite(ActualSample, 1, lengthOfActualSample, stream) < lengthOfActualSample){
                            printf("ERROR WRITING TO FILE!\n");
                        }
                    }
    }
    else if ( pSamples[loop].ValueDataType == ihuDoubleFloat )
    {
                    //lengthOfActualSample = snprintf(ActualSample, 100,"%s%8.15f, %s, Type: DoubleFloat\n ",temp,pSamples[loop].Value.DoubleFloat,szQuality);
                    lengthOfActualSample = snprintf(ActualSample, 100,"%s%8.15f, %s\n ",temp,pSamples[loop].Value.DoubleFloat,szQuality);
                    if(lengthOfActualSample < 0){
                         printf("ERROR WRITING TO BUFFER!\n");   
                    } else {
                        if(fwrite(ActualSample, 1,lengthOfActualSample, stream) < lengthOfActualSample){
                            printf("ERROR WRITING TO FILE!\n");
                        }
                    }

    }
    else if ( pSamples[loop].ValueDataType == ihuShort )
    {
                    //lengthOfActualSample = snprintf(ActualSample, 100,"%s%d, %s, Type:Short\n",temp,pSamples[loop].Value.Short,szQuality);
                    lengthOfActualSample = snprintf(ActualSample, 100,"%s%d, %s\n",temp,pSamples[loop].Value.Short,szQuality);
                    if(lengthOfActualSample <0){
                         printf("ERROR WRITING TO BUFFER!\n"); 
                    } else {
                        if(fwrite(ActualSample, 1, lengthOfActualSample, stream) < lengthOfActualSample){
                            printf("ERROR WRITING TO FILE!\n");
                        }
                    }
    }
    else if ( pSamples[loop].ValueDataType == ihuInteger )
    {
                    //lengthOfActualSample = snprintf(ActualSample, 100,"%s%d, %s, Type:Integer\n",temp, pSamples[loop].Value.Integer, szQuality);
                    lengthOfActualSample = snprintf(ActualSample, 100,"%s%d, %s\n",temp, pSamples[loop].Value.Integer, szQuality);
                    if(lengthOfActualSample < 0){
                         printf("ERROR WRITING TO BUFFER!\n");  
                    } else {
                        if(fwrite(ActualSample, 1, lengthOfActualSample, stream) < lengthOfActualSample){
                            printf("ERROR WRITING TO FILE! \n");
                        }
                    }

    }
    else if ( pSamples[loop].ValueDataType == ihuString )
    {
     //lengthOfActualSample = snprintf(ActualSample, 100,"%s%s, %s, Type:String\n",temp,pSamples[loop].Value.String,szQuality);
                    lengthOfActualSample = snprintf(ActualSample, 100,"%s%s, %s\n",temp,pSamples[loop].Value.String,szQuality);
                    if(lengthOfActualSample < 0){
                        printf("ERROR WRITING TO BUFFER!\n"); 
                    } else {
                        if(fwrite(ActualSample, 1, lengthOfActualSample, stream) < lengthOfActualSample){
                            printf("ERROR WRITING TO FILE! \n");
                        }
                    }
    }     
   }
  }
  lengthOfNumberOfSamples = snprintf(NumberOfSamples, 100,"Number of samples: %d\n",number_of_samples);
  if(lengthOfNumberOfSamples < 0){
            printf("ERROR WRITING TO BUFFER!\n"); 
        } else {
            if(fwrite(NumberOfSamples, 1, lengthOfNumberOfSamples, stream) < lengthOfNumberOfSamples){
                printf("ERROR WRITING TO FILE!\n");
            }
        }

      //timeDifference = GetTickCount() - timeDifference;
     //fprintf(stream, "Time taken: %d\n", timeDifference);
  // success or not you should free this
  FreeSamples(pSamples, number_of_samples);
  ihuFreePtr (pSamples);
  pSamples = NULL;
        timeTaken();
  fclose(stream);
 }


 return 0;
}

这是该功能的缩减版本,因此您没有完整的源代码,但是如果您想要它就在这里。我已经通过手动格式化字符串(temp [])来优化代码,该字符串运行速度提高了大约40倍,但它绝对是空终止的,所以这不应该是一个问题。

   stream = fopen(outputFileName, "w");

   for(...){
     ... lots of conditions
     if ( pSamples[loop].ValueDataType == ihuFloat )
     {
          lengthOfActualSample = snprintf(ActualSample, 100,"%s%8.7f, %s\n",temp,pSamples[loop].Value.Float,szQuality);
          if(lengthOfActualSample < 0){
               printf("ERROR WRITING TO BUFFER!\n");  
          } else {
               if(fwrite(ActualSample, 1, lengthOfActualSample, stream) < lengthOfActualSample){
                    printf("ERROR WRITING TO FILE!\n");
               }
          }
      }
    }

    fclose(stream);

现在结果:

Sample 81035, debug, 09/13/2010 11:10:55.000, 0.8900000, Good, Type:Float
Sample 81036, debug, 09/13/2010 11:11:00.000, 0.9500000, Good, Type:Float
Number of samples: 81037
**:56:15.000, 0.2800000, Good, Type:Float
Sample 80164, debug, 07/22/2010 15:56:20.000, 0.3400000, Good, Type:Float
Sample 80165, debug, 07/22/2010 15:56:30.000, 0.4100000, Good, Type:Float
... lots more samples
Sample 80322, debug, 08/01/2010 00:04:35.000, 0.5800000, Good, Type:Float
Sample 80323, debug, 08/01/2010 00:04:45.000, 0.6700000, Good, Type:Float
Sample 80324**
file ends here halfway through sample 80324

以粗体显示文件溢出的位置,并在最后一次打印调用后继续将垃圾打印到文件中(“样本数量:....)

结果应该是这样的,并且在我尝试过的每一台计算机上都是ARE(4),除了服务器是它必须处理的唯一PC。

Sample 81035, debug, 09/13/2010 11:10:55.000, 0.8900000, Good, Type:Float
Sample 81036, debug, 09/13/2010 11:11:00.000, 0.9500000, Good, Type:Float
Number of samples: 81037

我迫不及待地想出这个,并且完全不知道为什么会这样,有没有人有任何想法?

编辑:我在gcc -Wall -Wformat上有这些选项,我在整个项目中都有ZERO警告。我还编译了-pedantic和

edit2:做了一个小得多的运行,而不是用数据超限,它超过了0或NUL

run1, 02/01/2010 08:50:50.000, 0.6000000, Good
run1, 02/01/2010 08:51:00.000, 0.6900000, Good
run1, 02/01/2010 08:51:10.000, 0.7600000, Good   #Sample505
Number of samples: 505
NULNULNULNULNULNULNULNULNULNULNULNULNULNULNULNULNULNULNULNUL (0 in a hex editor)

一旦运行大于1000,它就会再次开始打印垃圾

run2, 03/31/2010 15:25:30.000, 0.8200000, Good
Number of samples: 1742
/22/2010 09:39:35.000, 0.5400000, Good
run2, 01/22/2010 09:39:45.000, 0.6400000, Good
run2, 01/22/2010 09:39:50.000, 0.6900000, Good

出于某种原因,2次运行一致,样本量为76660

run5, 12/31/2009 16:03:15.000, 0.6600000, Good
run5, 12/31/2009 16:03:25.000, 0.7600000, Good
Number of samples: 76660

因此结果得出结论:&lt; 1000将nuls打印到文件末尾,在1000和76660之间打印垃圾,然后在样本大小80234也在文件末尾打印垃圾,非常奇怪,但多余的数据总是如果多次运行则一致且相同。

3 个答案:

答案 0 :(得分:6)

以下行使用无效的fprintf格式字符串:

fprintf(stream,"%s%8.15, %s, Type: DoubleFloat\n",temp,pSamples[loop].Value.DoubleFloat,szQuality)

具体来说,%8.15,部分。你遗漏了类型说明符。如果您使用的是gcc,则可以使用-Wformat选项让编译器检查*printf调用此类错误。

另外,我同意pmg。您发布的代码似乎与您发布的输出不匹配。请仔细检查这是否是正确的代码。

答案 1 :(得分:3)

 for (loop = 0;loop< number_of_samples;loop++){
                /* ... */
                if(fprintf(stream,"Sample %d, %s",loop, tagname) < 0){
                /* ... */
                    if(fprintf(stream,"%s%8.7f, %s, Type:Float\n",temp,pSamples[loop].Value.Float,szQuality) < 0){
                    /* ... */
                    if(fprintf(stream,"%s%s, %s, Type:String\n",temp,pSamples[loop].Value.String,szQuality) <0){
                    /* ... */
        if(fprintf(stream, "Number of samples: %d\n",number_of_samples) < 0){
        /* ... */

“Type:”输出在哪里?你在看正确的源文件吗?

答案 2 :(得分:0)

我解决了自己的问题!

我改变了代码:

FreeSamples(pSamples, number_of_samples);
ihuFreePtr (pSamples);
pSamples = NULL;
timeTaken();
fclose(stream);

对此:

fclose(stream);
FreeSamples(pSamples, number_of_samples);
ihuFreePtr (pSamples);
pSamples = NULL;
timeTaken();

出于某种原因,我使用的用户API在释放内存时损坏了我的文件,我完全不知道如何或为什么,但它确实存在。