【问题标题】:Why is strtok() Considered Unsafe?为什么 strtok() 被认为是不安全的?
【发布时间】:2011-08-25 08:37:20
【问题描述】:

strtok 的哪些功能不安全(就缓冲区溢出而言)我需要注意?

对我来说有点奇怪的是,Visual C++ 中的strtok_s(即“安全”)有一个额外的“上下文”参数,但它看起来在其他方面是一样的……是一样的吗,还是真的不一样?

【问题讨论】:

  • 可能是因为 strtok 的两个参数都是指向 char 的指针,所以 strtok 可能无法到达溢出任何本地缓冲区的任何终止符?
  • @0verbose: 嗯...但不是所有 C 字符串都是这样吗?
  • 你可以看看实现:openbsd.org/cgi-bin/cvsweb/src/lib/libc/string/…。我也去看看。
  • 附带说明,为什么在有strsep的情况下仍然使用它
  • 我认为strtok_s中的上下文是为了让它线程安全,就像strtok_r一样

标签: c security strtok


【解决方案1】:

没有什么不安全的。您只需要了解它是如何工作的以及如何使用它。编写代码和单元测试后,只需多花几分钟时间即可使用 valgrind 重新运行单元测试,以确保您在内存范围内运行。手册页说明了一切:

BUG

使用这些功能时要小心。如果您确实使用它们,请注意:

  • 这些函数修改了它们的第一个参数。
  • 这些函数不能用于常量字符串。
  • 定界字符的标识丢失。
  • strtok() 函数在解析时使用静态缓冲区,因此它不是线程安全的。如果这对您很重要,请使用 strtok_r()

【讨论】:

  • 我对@9​​87654323@做了一个小修改...你知道它有什么不同吗?
  • 我不熟悉那个,但听起来像strtok_r。如果您同时标记两个或多个字符串,那么您需要其中之一。如果你只处理一个,那么没有必要使用它。
  • 为了澄清更多,strtok 在你的字符串中保留了一个隐藏的统计引用。如果您使用多个字符串调用它,则引用将不正确。 strtok_r/s 强制你提供对函数的引用。
  • 在另一个线程中运行strtok 的可能不是。它可能是你的插件。或者,您的代码可能被用于在主应用程序的另一个线程中运行的库中,该线程也使用strtok。或者,也许您只是在忘记了其中有一个 strtok 调用后的某一天才决定让您的代码多线程。
【解决方案2】:

根据this document的strtok_s部分:

6.7.3.1 strtok_s 函数 strtok_s 函数修复了两个问题 在 strtok 函数中:

  1. 新参数 s1max 可防止 strtok_s 存储在 字符串被标记化。 (字符串 被分成令牌既是一个 函数的输入和输出 strtok_s 将空字符存储到 字符串。)
  2. 新参数 ptr 消除了静态内部状态 防止 strtok 重入 (第 1.1.12 条)。 (ISO/IEC 9899 函数 wcstok 和 ISO/IEC 9945 (POSIX) 函数 strtok_r 修复此问题 同样的问题。)

【讨论】:

  • +1 我没有看过那个文件,似乎准确地解释了答案。 :)
  • 请注意,strtok_s() 的此规范来自(可选)ISO/IEC 9899:2011 的 Annex K,其定义与 Microsoft 的 strtok_s() 规范不同。
【解决方案3】:

如果您没有正确以空字符结尾的字符串;你最终会出现缓冲区溢出。另请注意(这是我很难学到的东西) strtok 似乎并不关心内部字符串。 IE。拥有“hello”/“world”将解析“hello”/“world”,而“hello/world”将解析为“hello world”。请注意,它在 / 上拆分,并忽略了它在括号内的事实。

【讨论】:

    【解决方案4】:

    strtok 在 Visual C++ 中是安全的(但在其他地方没有),因为它使用线程本地存储来保存调用之间的状态。在其他任何地方,全局变量都用于保存 strtok() 状态。

    然而,即使在 VC++ 中,strtok 是线程安全的,它仍然有点奇怪——你不能同时在同一个线程中的不同字符串上使用 strtok()s。例如,这将无法正常工作:

         token = strtok( string, seps );
         while(token)
         {
            printf("token=%s\n", token)
            token2 = strtok(string2, seps);
            while(token2)  
            {
                printf("token2=%s", token2);
                token2 = strtok( NULL, seps );
            }
            token = strtok( NULL, seps );
         }
    

    它不能正常工作的原因 - 对于每个线程,只有单个状态可以保存在线程本地存储中,这里需要两个状态 - 第一个字符串和第二个字符串。因此,虽然 strtok 在 VC++ 中是线程安全的,但它不是可重入的。

    strtok_s(或其他地方的 strtok_r)提供的东西 - 一个明确的状态,并且 strtok 变得可重入。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-04
      • 2014-03-08
      • 2014-03-26
      • 2011-08-13
      • 2012-11-19
      • 2020-01-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多