【问题标题】:How to use strnlen safely?如何安全使用 strnlen?
【发布时间】:2018-07-11 13:58:56
【问题描述】:

我试图了解使用 strnlen 的正确方法是什么,以便即使在考虑极端情况时也能安全使用它。

例如将非空终止字符串作为输入。

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

int main()
{
    void* data = malloc(5);

    size_t len = strnlen((const char*)data, 10);
    printf("len = %zu\n", len);

    return 0;
}

如果我期望一个最大大小的字符串10,但该字符串在这 10 个字符中不包含空字符,strnlen 将读取超出边界字节(输入指针可能指向堆分配的数据)。这种行为是否未定义?如果是,有没有办法安全地使用strnlen 来计算考虑到这种情况并且不会导致未定义行为的字符串的长度?

【问题讨论】:

  • 如果字符串不是以 NUL 结尾的,你如何定义它的长度?
  • 要使用strnlen(),第二个参数需要指定一个长度上限。在您的情况下,strnlen() 的调用不应有超过 5 的第二个参数。如果您不尊重这一点,您将导致未定义的行为,并且没有办法解决。
  • 问题是如何使用strnlen并被标记为C,而不是如何避免strnlen并抛弃C。
  • C 安全方法可能是strnlenmemchr

标签: c strlen


【解决方案1】:

为了安全使用strnlen,您需要

  1. 自己跟踪输入缓冲区的大小(在您的情况下为 5)并将 that 作为第二个参数传递,not 是大于该值的数字.

  2. 确保输入指针不是NULL

  3. 确保另一个线程没有写入缓冲区。

形式上,您不需要初始化缓冲区的内容,因为从概念上讲,函数读取缓冲区就像它们是 char 类型一样。

【讨论】:

    【解决方案2】:

    此代码很可能会调用undefined behavior

    malloc 返回的字节具有不确定的值。如果在返回的 5 个字节中没有空字节,则 strnlen 将读取这些字节,因为它最多传递了 10 个字节,并且读取超过分配内存的末尾会调用未定义的行为。

    只是读取返回的字节,但是应该是未定义的。虽然不确定的值可以包含陷阱表示,但strnlen 使用char * 读取字节,并且字符类型具有陷阱表示,因此这些值只是未指定并且读取它们是安全的。

    如果传递给strnlen的值不大于分配的内存大小,那么它的使用是安全的。

    【讨论】:

    • 没那么快。 char 读取未初始化字节 char 的行为不是未定义的。但确实超出缓冲区的读取未定义的。
    • @Bathsheba 在理论上是正确的,在实践中非常不是正确的;我测试过的每个编译器都将 J.2 “行为未定义,如果......一个值 [任何类型的,无论它是否具有陷阱表示] 在它不确定时被使用”作为规范,即使仔细阅读实际的文字并没有那么远。
    • 在这种情况下它是未定义的,因为分配的块是 5 个字节,strnlen max len 是10,因此,这是一个问题。否则如果maxlen小于或等于分配的内存块长度,那么应该没问题。
    • @phoxis:在编辑之前是不正确的,我猜反对者还没有撤回。
    • @Bathsheba:您是正确的:“您正在读取未初始化的字节,”部分不正确。
    【解决方案3】:

    由于数据的实际长度是 5 并且你很可能没有 '\0' ,它会开始读取未分配的内存(从 data[5] 开始),这可能有点不愉快。

    【讨论】:

    • 您几乎已经回答了问题的第一部分(这种行为是否未定义?),但不是主要问题(使用@的正确方法是什么? 987654321@?)。因此,恐怕这并不能真正作为答案。
    猜你喜欢
    • 2016-09-02
    • 1970-01-01
    • 1970-01-01
    • 2011-06-29
    • 2016-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多