【问题标题】:strncat causes buffer overflowstrncat 导致缓冲区溢出
【发布时间】:2021-06-15 17:09:42
【问题描述】:

如何将strncat 与堆对象一起使用?

我正在尝试编写一个简单的函数来将 2 个字符串连接在一起返回结果,但是,如果不使返回缓冲区非常大(在其长度上增加大约 5000)以使其不会溢出,我就无法运行它。

我可能只是错误地使用了strncat 函数,使用堆对象而不是固定长度的字符数组。但我不知道我会怎么写。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char *concatStrings(char *arg1, char *arg2) {
    char *ret = malloc(strlen(arg1) + strlen(arg2));
    strncpy(ret, arg1, strlen(arg1));
    strncat(ret, arg2, strlen(arg2));
    ret[strlen(arg1)+strlen(arg2)] = '\0';
    return ret;
}

int main(int argc, char *argv[]) {
    if (argc == 3) {
        char *print = concatStrings(argv[1], argv[2]);
        printf("\n%s", print);
        free(print);
    }
    return 0;
}

【问题讨论】:

  • 您必须malloc(len+1) 才能说明0-byte。为什么你在这里使用硬编码长度的strncpystrncat
  • 查看snprintf(buf, sizeof buf, "%s%s", arg1, arg2); 以(可能)替换您的strcpy(...); strcat(...);
  • strncatstrlen 作为参数是完全没有意义的。 strncat 通常是应该避免的危险函数之一,就像 strncpy 一样。
  • @SpeedrunnerG55:您不应该修复问题中的代码,因为它会使 cmets 和答案不一致。
  • strncpystrlen(arg1) 一起使用不仅没有意义。比那更糟。它不仅不提供任何额外的检查,而且主动省略了终止的 0 字节,因为它不包含在 strlen 中。结果不是有效的字符串,当您之后使用 strncat 时,您会导致未定义的行为。

标签: c concatenation c-strings function-definition


【解决方案1】:

对于初学者来说,函数应该声明为

char * concatStrings( const char* arg1, const char* arg2 );

因为指针arg1arg2 所指向的字符串在函数内没有被改变。

在这个内存分配中

char *ret = malloc(strlen(arg1) + strlen(arg2));

您忘记为空终止字符'\0' 保留内存。你必须写

char *ret = malloc( strlen(arg1) + strlen(arg2) + 1 );

在此调用中使用幻数 10

strncpy(ret,arg1,10);

没有意义。

如果你会写例如

strncpy(ret,arg1,strlen(arg1));

然后下一个电话

strncat(ret,arg2,strlen(arg2));

将调用未定义的行为,因为调用 strncpy 没有将空终止字符“\0”附加到指针 ret 指向的字符串。

至少写出来会好很多

strcpy( ret, arg1 );

在任何情况下,您的函数实现都是低效的。例如有两次为参数arg2调用函数strlen

char *ret = malloc(strlen(arg1) + strlen(arg2));
//...
strncat(ret,arg2,strlen(arg2));

另外,strncat 的调用也是低效的,因为函数需要遍历整个目标字符串来找到它的终止零。

函数可以通过以下方式定义

char * concatStrings( const char* arg1, const char* arg2 )
{
    size_t n1 = strlen( arg1 );
    char *ret = malloc( n1 + strlen( arg2 ) + 1 );

    if ( ret != NULL )
    {
        strcpy( ret, arg1 );
        strcpy( ret + n1, arg2 );
    }

    return ret;
}

这是一个演示程序。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char * concatStrings( const char* arg1, const char* arg2 )
{
    size_t n1 = strlen( arg1 );
    char *ret = malloc( n1 + strlen( arg2 ) + 1 );

    if ( ret != NULL )
    {
        strcpy( ret, arg1 );
        strcpy( ret + n1, arg2 );
    }

    return ret;
}

int main(void) 
{
    const char *argv1 = "Hello ";
    const char *argv2 = "World!";
    
    char *print = concatStrings( argv1, argv2 );
    
    if ( print != NULL ) puts( print );
    
    free( print );
    
    return 0;
}

程序输出是

Hello World!

在函数中将strcpy 的第一次调用替换为memcpy 会更好。那就是函数也可以是这样的

char * concatStrings( const char* arg1, const char* arg2 )
{
    size_t n1 = strlen( arg1 );
    char *ret = malloc( n1 + strlen( arg2 ) + 1 );

    if ( ret != NULL )
    {
        memcpy( ret, arg1, n1 );
        strcpy( ret + n1, arg2 );
    }

    return ret;
}

【讨论】:

  • 我忘了我把 10 留在了那里,但在我的实际实现中它没有任何区别,我忘了从我在这里写的更通用的函数中删除它
【解决方案2】:

第一个问题是,您没有分配足够的内存,因为在 C 中,字符串由 0-byte 终止,所以它应该是

char *ret = malloc((strlen(arg1) + strlen(arg2))+1);

当然你应该检查 malloc 是否有效。

if (!ret)
    // error

第二个问题是,您在此处使用带有硬编码长度的strncpy。 只需使用strcpy,因为您已经分配了足够的内存。 出于同样的原因,您也可以使用strcatstrncat 没有提供任何额外的好处,实际上会使代码变慢,因为您再次调用 strlen

这个

ret[10 + strlen(arg2)] = '\0';

根本不需要。事实上,如果 arg1 少于 10 个字符,它可能会很危险。

【讨论】:

  • 另一个问题是(与strncat() 不同)strncpy() 函数在没有足够空间时不会写入空终止符,这将使strncat() 失灵。如果 OP 已经检查了长度并(希望)分配了足够的内存,则长度限制版本不是必需的 - 甚至是责任。
【解决方案3】:

我忘记了 snprintf 的存在,并认为我必须使用 strcpy strcat 或其他字符串或内存管理函数来执行此操作

char* concatStrings(char* arg1, char*arg2){
  size_t n1 = strlen(arg1), n2 = strlen(arg2);
  char *ret = malloc(n1 + n2 + 1);
  snprintf(ret,n1 + n2 + 1, "%s%s", arg1, arg2);
  return ret;
}

【讨论】:

  • 我可能应该只使用sprintf(ret,"%s%s", arg1, arg2); 而不是snprintf
  • 使用 sprintf 或 snprintf 效率低下。事实上,这是我的函数实现的包装器,带有一个附加参数。:)
【解决方案4】:

问题

  1. 没有为空字符分配空间。关闭 1。

  2. strncpy(ret, arg1, strlen(arg1)) 不会 空字符 终止 ret,因此下一个 strncat(ret,...未定义的行为,因为 ret 需要指向一个 字符串.

一些替代方案:

// Via sprintf
// Good compilers will emit efficient code 
//   and not perform the usual parsing overhead of *printf format at run time
char *concatStrings_alt1(const char *arg1, const char *arg2) {
  char *ret = malloc(strlen(arg1) + strlen(arg2) + 1);
  sprintf(ret, "%s%s", arg1, arg2);
  return ret;
}

// Via memcpy
char *concatStrings_alt2(const char *arg1, const char *arg2) {
  size_t len1 = strlen(arg1);
  size_t size2 = strlen(arg2) + 1;
  char *ret = malloc(len1 + size2);
  memcpy(ret, arg1, len1);
  memcpy(ret + len1, arg2, size2);
  return ret;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-02-17
    • 1970-01-01
    • 2010-10-09
    • 1970-01-01
    • 1970-01-01
    • 2021-10-12
    • 2016-07-27
    相关资源
    最近更新 更多