如何避免使用C中非空终止的缓冲区调用fopen()?

时间:2021-06-21 20:45:41

Let's look at this example:

我们来看看这个例子:

static FILE *open_file(const char *file_path)
{
    char buf[80];
    size_t n = snprintf(buf, sizeof (buf), "%s", file_path);
    assert(n < sizeof (buf));
    return fopen(buf, "r");
}

Here, the assert() is off-by-one. From the manpage for snprintf:

这里,assert()是一个接一个。从snprintf的联机帮助页:

"Upon successful return, these functions return the number of characters printed (excluding the null byte used to end output to strings)."

“成功返回后,这些函数返回打印的字符数(不包括用于结束输出到字符串的空字节)。”

So, if it returns 80, then the string will fill the buffer, and won't be terminated by \0. This will cause a problem because fopen() assumes it is null terminated.

因此,如果它返回80,那么字符串将填充缓冲区,并且不会被\ 0终止。这将导致问题,因为fopen()假定它以null结尾。

What is the best way to prevent this?

防止这种情况的最佳方法是什么?

3 个解决方案

#1


2  

What is the best way to prevent this?

防止这种情况的最佳方法是什么?

Simple, don't give it a non-null terminated string. Academic questions aside, you are in control of the code you write. You don't have to protect against yourself sabotaging the project in every conceivable way, you just have to not sabotage yourself.

很简单,不要给它一个非空终止字符串。除了学术问题,您可以控制自己编写的代码。你不必以各种可能的方式防止自己破坏项目,你只需要不要破坏自己。

If everyone checked and double checked everything in code, the performance loss would be incredible. There's a reason why fopen doesn't do it.

如果每个人都检查并仔细检查代码中的所有内容,那么性能损失将是不可思议的。 fopen没有这样做的原因。

#2


2  

So, if it returns 80, then the string will fill the buffer, and won't be terminated by \0

因此,如果它返回80,那么字符串将填充缓冲区,并且不会被\ 0终止

That is incorrect: the string would be null-terminated no matter what you pass for file_path. Obviously, the string would be cut off at the sizeof(buf)-1.

这是不正确的:无论你为file_path传递什么,字符串都将以null结尾。显然,字符串将以sizeof(buf)-1切断。

Note that snprintf could return a number above 80 as well. This would mean that the string you wanted to print was longer than the buffer you have provided.

请注意,snprintf也可以返回高于80的数字。这意味着您要打印的字符串比您提供的缓冲区长。

What is the best way to prevent this?

防止这种情况的最佳方法是什么?

You are already doing it: the assert is not necessary for preventing unterminated strings. You can use the return value to decide if any truncation has happened, and pass a larger buffer to compensate:

您已经这样做了:断言不是防止未终止字符串所必需的。您可以使用返回值来确定是否发生任何截断,并传递更大的缓冲区来补偿:

if (n >= sizeof(buf)) {
    char *tmpBuf = malloc(n+1);
    snprintf(tmpBuf, n+1, "%s", file_path);
    FILE *res = fopen(tmpBuf, "r");
    free(tmpBuf);
    return res;
}

#3


1  

There are a couple of issues here.

这里有几个问题。

First assert() is used to catch issues as a part of designer testing. It is not meant to be used in production code.

第一个assert()用于捕获问题,作为设计器测试的一部分。它不适用于生产代码。

Secondly if the file path is not complete then do you really want to call fopen()?

其次,如果文件路径不完整,那么你真的想调用fopen()吗?

Normally what is done is to add one to the expected number of characters.

通常,所做的是将一个加到预期的字符数。

static FILE *open_file(const char *file_path)
{
    char buf[80 + 1] = {0};

    size_t n = snprintf(buf, 80, "%s", file_path);
    assert(n < sizeof (buf));
    return fopen(buf, "r");
}

#1


2  

What is the best way to prevent this?

防止这种情况的最佳方法是什么?

Simple, don't give it a non-null terminated string. Academic questions aside, you are in control of the code you write. You don't have to protect against yourself sabotaging the project in every conceivable way, you just have to not sabotage yourself.

很简单,不要给它一个非空终止字符串。除了学术问题,您可以控制自己编写的代码。你不必以各种可能的方式防止自己破坏项目,你只需要不要破坏自己。

If everyone checked and double checked everything in code, the performance loss would be incredible. There's a reason why fopen doesn't do it.

如果每个人都检查并仔细检查代码中的所有内容,那么性能损失将是不可思议的。 fopen没有这样做的原因。

#2


2  

So, if it returns 80, then the string will fill the buffer, and won't be terminated by \0

因此,如果它返回80,那么字符串将填充缓冲区,并且不会被\ 0终止

That is incorrect: the string would be null-terminated no matter what you pass for file_path. Obviously, the string would be cut off at the sizeof(buf)-1.

这是不正确的:无论你为file_path传递什么,字符串都将以null结尾。显然,字符串将以sizeof(buf)-1切断。

Note that snprintf could return a number above 80 as well. This would mean that the string you wanted to print was longer than the buffer you have provided.

请注意,snprintf也可以返回高于80的数字。这意味着您要打印的字符串比您提供的缓冲区长。

What is the best way to prevent this?

防止这种情况的最佳方法是什么?

You are already doing it: the assert is not necessary for preventing unterminated strings. You can use the return value to decide if any truncation has happened, and pass a larger buffer to compensate:

您已经这样做了:断言不是防止未终止字符串所必需的。您可以使用返回值来确定是否发生任何截断,并传递更大的缓冲区来补偿:

if (n >= sizeof(buf)) {
    char *tmpBuf = malloc(n+1);
    snprintf(tmpBuf, n+1, "%s", file_path);
    FILE *res = fopen(tmpBuf, "r");
    free(tmpBuf);
    return res;
}

#3


1  

There are a couple of issues here.

这里有几个问题。

First assert() is used to catch issues as a part of designer testing. It is not meant to be used in production code.

第一个assert()用于捕获问题,作为设计器测试的一部分。它不适用于生产代码。

Secondly if the file path is not complete then do you really want to call fopen()?

其次,如果文件路径不完整,那么你真的想调用fopen()吗?

Normally what is done is to add one to the expected number of characters.

通常,所做的是将一个加到预期的字符数。

static FILE *open_file(const char *file_path)
{
    char buf[80 + 1] = {0};

    size_t n = snprintf(buf, 80, "%s", file_path);
    assert(n < sizeof (buf));
    return fopen(buf, "r");
}