5

我有以下 C 代码片段,必须识别错误并建议一种更安全的编写方法:

char somestring[] = "Send money!\n";
char *copy;

copy = (char *) malloc(strlen(somestring));

strcpy(copy, somestring);
printf(copy);

所以错误是 strlen 忽略'\0'了字符串的尾随,因此不会为副本分配足够的内存,但我不确定他们在更安全地编写它方面的目的是什么?

我可以使用malloc(strlen(somestring)+1))我假设,但我认为必须有比这更好的方法?


编辑:好的,我已经接受了一个答案,我怀疑我们不会期望 strdup 解决方案,因为它不是 ANSI C 的一部分。这似乎是一个相当主观的问题,所以我不确定我是否有接受其实是最好的。无论如何感谢所有的答案。

4

10 回答 10

7

我不能对上面的回复发表评论,但除了检查返回码和使用之外strncpy,你不应该这样做:

printf(string)

但是使用:

printf("%s", string);

参考:http ://en.wikipedia.org/wiki/Format_string_attack

于 2009-05-26T17:49:42.153 回答
6
char somestring[] = "Send money!\n";
char *copy = strdup(something);

if (copy == NULL) {
    // error
}

或者只是把这个逻辑放在一个单独的函数xstrdup 中

char * xstrdup(const char *src) 
{
    char *copy = strdup(src);

    if (copy == NULL) {
       abort();
    }

    return copy;
}
于 2009-05-26T16:52:50.600 回答
4
char   somestring[] = "Send money!\n";
char   *copy;
size_t copysize;

copysize = strlen(somestring)+1;
copy = (char *) malloc(copysize);
if (copy == NULL)
    bail("Oh noes!\n");

strncpy(copy, somestring, copysize);
printf("%s", copy);

上述差异:

  • malloc()必须检查的结果!
  • 计算并存储内存大小!
  • 使用strncpy()因为strcpy()是淘气。在这个人为的例子中,它不会受到伤害,但不要养成使用它的习惯。

编辑:

对于那些认为我应该使用的人strdup()......只有在你对问题采取最狭隘的观点时才有效。这不仅愚蠢,而且忽略了一个更好的答案:

char somestring[] = "Send money!\n";
char *copy = somestring;
printf(copy);

如果你要变得迟钝,至少要擅长它。

于 2009-05-26T17:01:37.103 回答
3
  1. strlen + 1,用于 \0 终止符
  2. malloc 可能会失败;总是检查 malloc 返回值
于 2009-05-26T16:59:50.647 回答
3

Ick...strdup()像其他人所说的那样使用,如果需要的话自己写。既然您现在有时间考虑这个问题……请查看Mitre 的 25 个最危险的编程错误,然后考虑为什么该短语printf(copy)应该出现在代码中。就彻底的糟糕而言,就在那里,更不用说追查为什么当复制类似于......malloc(strlen(str))"%s%n"

于 2009-05-26T17:43:20.733 回答
1

我会评论以前的解决方案,但我没有足够的代表。在这里使用strncpy与使用strcpy一样错误(因为绝对没有溢出的风险)。<string.h>中有一个名为memcpy的函数,它正是为此而生的。它不仅明显更快,而且是用于在标准 C 中复制已知长度的字符串的正确函数。

从接受的答案:

char   somestring[] = "Send money!\n";
char   *copy;
size_t copysize;

copysize = strlen(somestring)+1;
copy = (char *) malloc(copysize);
if (copy == NULL)
    bail("Oh noes!\n");

memcpy(copy, somestring, copysize); /* You don't use str* functions for this! */
printf("%s", copy);
于 2009-05-27T01:56:46.617 回答
1

使代码更安全(更正确)的方法。

  1. 不要制作不必要的副本。从示例中,没有明显要求您实际需要复制somestring. 可以直接输出。
  2. 如果您必须复制字符串,请编写一个函数来执行此操作(或使用 strdup 如果有它)。然后你只需要在一个地方把它做好。
  3. 只要有可能,在声明副本时立即初始化指向副本的指针。
  4. 请记住为空终止符分配空间。
  5. 记得检查来自的返回值malloc
  6. 记得释放malloc'ed 内存。
  7. 不要printf使用不受信任的格式字符串调用。使用printf("%s", copy)puts(copy)
  8. 使用带有字符串类的面向对象语言或任何具有内置字符串支持的语言来避免大多数这些问题。
于 2009-05-27T17:08:39.493 回答
1

为 Adrian McCarthy 编写更安全代码的方法添加更多内容,

使用静态代码分析器,他们非常擅长发现这种错误

于 2009-05-28T04:02:22.213 回答
0

如果一个人真的对这样的事情感兴趣,那么更安全地编写它的最好方法就是用 Ada 编写它。

somestring : constant string := "Send money!";
declare
   copy : constant string := somestring;
begin
   put_line (somestring);
end;

结果相同,那么有什么区别?

  • 整个事情都是在堆栈上完成的(没有指针)。重新分配是自动且安全的。
  • 一切都自动进行范围检查,因此没有缓冲区溢出漏洞的机会
  • 两个字符串都是常量,所以没有机会搞砸和修改它们。
  • 它可能会比 C快得多,不仅因为缺少动态分配,还因为没有对 strlen() 所需的字符串进行额外的扫描。

请注意,在 Ada 中,“字符串”不是一些特殊的动态构造。它是内置的字符数组。但是,Ada 数组可以在声明时通过您分配给它们的数组来调整大小。

于 2009-05-26T17:18:25.773 回答
-2

更安全的方法是使用strncpy而不是strcpy. 该函数接受第三个参数:要复制的字符串的长度。此解决方案不会超出 ANSI C,因此它适用于所有环境(而其他方法可能仅适用于 POSIX 兼容系统)。

char somestring[] = "Send money!\n";
char *copy;

copy = (char *) malloc(strlen(somestring));

strncpy(copy, somestring, strlen(somestring));
printf(copy);
于 2009-05-26T16:52:58.947 回答