strncat 导致缓冲区溢出

strncat causes buffer overflow

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

我试图编写一个简单的函数来将 2 个字符串连接在一起 return 结果,但是,如果不使 return 缓冲区非常大(添加大约增加 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)] = '[=10=]';
    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;
}

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

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

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

if (!ret)
    // error

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

这个

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

根本不需要。事实上,如果 arg1 少于 10 个字符,它有潜在的危险。

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

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

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

在此内存分配

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

您忘记为空终止字符 '[=24=]' 保留内存。你必须写

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;
}

我忘记了 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;
}

问题

  1. 不为空字符分配space。减 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;
}