字符串操作中的双重自由错误

时间:2012-09-20 00:34:40

标签: c string malloc

如果传递了长字符串,则获得以下代码的双倍空闲。 我尝试过各种各样的事情。如果我删除了自由线,它会消失。 不知道为什么会这样。

void format_str(char *str1,int l,int o) {
    char *s = malloc(strlen(str1)+1);
    char  *s1=s, *b = str1;
    int i=0;
    while(*str1!='\0') {
        i++;
        *s1++=*str1++;

        if(i>=l) {
            if(*str1!=',') {
                continue;
            }
        *s1++=*str1++;
            *s1++='\n';
            for(i=0;i<o;i++) {
                *s1++=' ';
            }
            i = 0;
        }
    }
    *s1 = '\0';
    strcpy(b,s);
    free(s);
}

5 个答案:

答案 0 :(得分:5)

您可能没有在s中为您要复制的数据量分配足够的空间。我不知道你的逻辑在做什么,但我看到像

这样的东西
        *s1++=*str1++;
        *s1++='\n';

您要为s中的单个字符复制多个字符s1(通过str1)。

对于所有可计算的爱,使用更好的变量名称!

答案 1 :(得分:0)

你几乎肯定会破坏堆。例如:

int main() 
{
    char original[1000] = "some,,,string,,, to,,,,format,,,,,";

    printf( "original starts out %u characters long\n", strlen(original));
    format_str( original, 6, 6);
    printf( "original is now %u characters long\n", strlen(original));

    return 0;
}

要求malloc()分配的缓冲区大小远大于strlen(str1)+1。具体来说,它必须至少63字节长(因为函数在问题中编码,分配的大小为35字节)。

如果您需要更具体的帮助,则应描述您要执行的操作(例如lo的参数是什么?)。

答案 2 :(得分:0)

为了心理健康,我会尝试重新格式化您的代码并猜测重命名变量。

void format_str(char *str, int minlen, int indent) 
{
    char *tmpstr = malloc( strlen(str) + 1 ); // here is the problem
    char *wrkstr = tmpstr, *savestr = str;
    int count = 0;

    while ( *str != '\0' ) {
        count++;
        *wrkstr++ = *str++;

        if ( count >= minlen ) {

            if ( *str != ',' ) {
                continue;
            }

            *wrkstr++ = *str++;
            *wrkstr++ = '\n';
            for ( count = 0;  count < indent;  count++ ) {
                *wrkstr ++= ' ';
            }
            count = 0;
        }
    }

    *wrkstr = '\0';
    strcpy(savestr,tmpstr);
    free(tmpstr);
}

正如其他人指出的那样,你没有为临时字符串分配足够的空间。

您的代码中还有另外两个问题(其中一个是主要问题)。

您可能应该通过检查str不是NULL来验证您的参数,也可能minlenindent不是否定的。然而,这并不重要,因为NULL str只是段错(标准库字符串函数的相同行为),minlen和/或indent的值低于1只是表现为0

主要问题是str中你有多少空间。您在格式化过程中盲目地增长字符串,然后将其复制回相同的内存。这是一个等待发生的缓冲区溢出(具有潜在的严重安全隐患,特别是如果str恰好指向堆栈)。

修复它:

  • 您应该分配足够的空间。

  • 您应该返回已分配的字符串并规定调用者负责释放它(如strdup那样)或添加一个参数来指定str中可用的空间然后避免任何工作,如果不存储格式化的字符串。

答案 3 :(得分:0)

用例是需要有可能进行干运行的一个很好的例子。

我建议您修改代码:

ssize_t format_str(const char * input, int p1, int p2, char * output);

1目标缓冲区应由函数调用者通过传递给函数的参数òutput提供

2函数应返回写入目标缓冲区的字符数(负值可能表示任何类型的错误)

3如果作为output传递的值为NULL,则函数复制任何内容,只是解析input引用的数据并确定将写入的字符数目标缓冲区并返回此值。

然后使用转换函数,应该调用它两次,如下所示:

char * input = "some,,test   , data,,, ...";
int p1 = <some value>, p2 = <some other value>;
ssize_t ssizeOutput = format_str(input, p1, p2, NULL)
if (0 > ssizeOutput)
{
  exit(EXIT_FAILURE);
}
else if (0 < ssizeOutput)
{
  char * output = calloc(ssizeOutput, sizeof(*output));
  if (!output)
  {
    exit(EXIT_FAILURE);
  }

  ssizeOutput = format_str(input, p1, p2, output);
  if (0 > ssizeOutput)
  {
    exit(EXIT_FAILURE);
  }
}

答案 4 :(得分:0)

正如其他人所指出的那样,堆内存最有可能被破坏,因为代码写入超出分配内存的末尾。

验证内存是否损坏很简单。在函数开始时保存str1的长度,我们将其命名为'len_before'。在调用free()之前,再次获取字符串长度,并将其命名为“len_after”。

如果(len_after&gt; len_before)那么我们就会发生致命错误。

一个相对简单的修复方法是传递str1可以长到的最大长度, malloc那么大的内存并在超过最大长度之前停止,即用空值截断它但保持在限制范围内。

int len_before, len_after;

len_before = strlen(str1) + 1;
.
. /* Rest of the code. */
.
len_after = strlen(str1) + 1;
if (len_after > len_before) {
    printf("fatal error: buffer overflow by %d bytes.\n", len_after - len_before);
    exit(1);
}
free(s);