【问题标题】:Mac solution for "safe" alternatives to "unsafe" C/C++ Standard Library functions? [closed]Mac 解决方案“安全”替代“不安全”C/C++ 标准库函数? [关闭]
【发布时间】:2010-01-30 18:55:14
【问题描述】:

Mac 上最好的一站式“安全”C 库解决方案是什么?我在“安全”/“不安全”上使用引号,因为对于某些标准库函数的好处或其假定改进的替代方案存在很多争论。

由于潜在的缓冲区溢出或其他安全问题,许多传统的标准 C 库函数(例如,vfprintf)被认为是不安全的。

在 Windows 上,Microsoft C/C++ 编译器 provide 将“_s”函数(例如 vfprintf_s)作为标准库调用的更安全替代方案。这些功能不是直接替代品,因为它们具有提供额外安全信息(例如缓冲区长度)所需的不同签名。它们还提供其他功能,例如无效格式字符串检测、不同的文件安全性等。据我所知,此实现在 Mac 上不可用。

Apple(或第三方)是否提供任何类似的东西以在 OSX 上与 GCC 一起使用?

特别是,我正在寻找至少以下功能的“安全”实现:

fopen vfprintf vsprintf sprintf strncpy strcpy strcat

请注意:这个问题是关于 Mac 的。我不是在询问您对 Microsoft 的实现的意见(除非它在 ​​Mac 上可用。)虽然其中一些功能可能很容易自己编写,但并非全部都是。我不是在问自己如何写这些。我不是在询问有关如何使用 STL 类来执行此操作的提示。我不是在问如何关闭警告。我的特殊需求非常具体。我试图找出一个与传统 C 库调用尽可能相似的最佳实践 Mac API,同时增加安全性。当然,在 Mac 和 Windows(以及其他操作系统)上运行的可移植实现会更好。

【问题讨论】:

  • 使用这些所谓的“安全”功能并不是最佳实践。
  • 为什么不能使用安全的 C++ 标准库等价物? (或者如果你拒绝使用C++标准库,为什么标题说C/C++标准库函数)
  • GCC 已经对常规 C 标准库函数进行了格式字符串验证。它是使用特殊关键字实现的,因此您甚至可以使用采用标准格式字符串的自己的函数来实现。
  • @Neil Butterworth:为什么“安全”函数不是最佳实践?也许您可以引用解释的链接? Mac 上的最佳做法是什么?
  • @Shteef:非常感谢您的评论。我会咬一口,在 GCC 中启用格式字符串验证的秘密关键字是什么?链接怎么样?

标签: c macos gcc


【解决方案1】:

首先,从 MSDN 打印关于“安全/不安全”功能的文档并烧录!

打开

和 fopen_s 一样安全...除非你是白痴并假设返回的指针不是 NULL,或者提供 NULL 作为输入参数。

vfprintf vsprintf sprintf 

只是MS不支持C99,使用snprintffamily。

 strncpy

如果你阅读手册是绝对安全的

strcpy strcat

使用strncpystrncat 并阅读规范。 (即 strncpy 可能不是以 null 结尾的)

所以……再一次:

从 MSDN 打印有关“安全/不安全”功能的文档并刻录!

【讨论】:

  • strncpy / strncat 是安全的,但正确使用会很痛苦。 strncpy 要求调用者添加 0 终止符。 strncat 要求用户计算要复制多少,而不是目标缓冲区的大小。
  • 请注意,Microsoft 的 vsnprintf() 不能与标准 (C99) 兼容,因为 C99 定义要求 vsnprintf() 添加终止空值,除非缓冲区的长度为零(并且具有特殊含义) .我想 MS 知道它的意思是什么,它说它的 vsnprintf() 并不总是添加终端 null,但这只是意味着 MS(可能)在标准规范正确行为之前实现了它,然后觉得无法冒险改变错误的定义(即使很难看到任何工作代码因更改而中断)。
  • @Artyom,感谢您的回答。你可以放心地认为我是个白痴。特别是,你可以打赌我最终会犯任何“如果我阅读手册是安全的”的功能。
  • @jwfearn -- 关键是 MS 创建了自己的“安全”函数,而不是采用 C99 的安全函数。例如,fopen_s 完全是“废话”,sprintf_s 也是 - 使用 snprintf。等等。是的,strncpy 和 strncat 很棘手,但仍然遵守标准要好得多......不要相信你读到的关于“不安全函数”的所有内容
  • 实际上,TR24731 比 MS 的等效功能晚了一些 - MS 实现了一些东西,然后将他们的系统提交给标准 C 委员会。什么是“标准化”(在某种程度上它是标准化的;它目前是一份技术报告)与 MS 提案不同。
【解决方案2】:

总结:在 Mac 上,有几个 API 和编译器选项可以为 C 标准库函数提供更安全的替代方案。以下是其中一些与Microsoft's "safe" APIs的比较:

C MSVC 提供者 MAC 解决方案 -------------------------------------------------- ------------------------------------------- fopen fopen_s C 无,假设 fopen 是安全的 vfprintf vfprintf_s GCC GCC_WARN_TYPECHECK_CALLS_TO_PRINTF(1) vsprintf vsprintf_s GCC, C99 GCC_WARN_TYPECHECK_CALLS_TO_PRINTF, vsnprintf(2) sprintf sprintf_s GCC,C99 GCC_WARN_TYPECHECK_CALLS_TO_PRINTF,snprintf(3) strncpy strncpy_s BSD strlcpy(4) strcpy strcpy_s BSD strlcpy strcat strcat_s BSD strlcat(5)

(1) GCC_WARN_TYPECHECK_CALLS_TO_PRINTF 是一个 XCode 配置选项,对应于 GCC 命令行选项 -Wformat。此选项会在参数类型和静态格式字符串之间产生不一致的编译器警告。还有多种其他选项可以控制 GCC 对格式字符串的处理。您甚至可以使用 GCC 的 format function attribute 对您自己的函数启用格式字符串检查。

(2) vsnprintf 和 (3) snprintf 来自 C99 版本的 C 标准库(在 Mac 上的 GCC 中可用,但在 Windows 上的 MSVC 中不可用)。

(4) strlcpy 和 (5) strlcat 是 BSD 库函数,可在 Mac 上使用。

【讨论】:

    【解决方案3】:

    你想用 sprintf 和 vsprintf 代替:

    snprintf(buffer, buffer_size, fmt_string, args, ...);
    vsnprintf(buffer, buffer_size, fmt_string, valist);
    

    你要我们代替strcpy、strncpy、strcat和strncat:

    strlcpy(dest, src, dest_size);
    strlcat(dest, src, dest_size);
    

    strn 函数不能被 strl 函数替代的重要方式之一。如果要复制非 0 终止的字符串,strn 函数允许您通过将长度设置为复制量和目标缓冲区大小的较小值来实现。 strl 函数不这样做,并且仅在源字符串为 0 终止时才起作用。

    不确定 fopen 或 vfprintf 如何被认为是不安全的。

    【讨论】:

    • vfprintf() 是不安全的,因为它支持 '%n' 指令(报告处理指令时写入的字节数)。这是 C89 标准化过程中一个有趣的(坏的?)举措——它需要 printf() 等中的输出参数(指向 int 的指针)。这么多是不安全的——但其余的都不是。
    【解决方案4】:

    另请参阅:SO 327980

    Standard C committee 创建了一份技术报告TR 24731-1,部分是在微软的鼓励下(我相信)。它标准化了各种功能的接口,例如vsnprintf_s()。然而遗憾的是,该标准定义的接口与微软定义的接口不兼容,从而使该标准在很大程度上无关紧要。

    例如,TR 24731-1 说与vsnprintf_s() 的接口是:

    #define _ _STDC_WANT_LIB_EXT1_ _ 1
    #include <stdarg.h>
    #include <stdio.h>
    int vsnprintf_s(char * restrict s, rsize_t n,
                    const char * restrict format, va_list arg);
    

    不幸的是,MSDN 说与vsnprintf_s() 的接口是:

    int vsnprintf_s(
       char *buffer,
       size_t sizeOfBuffer,
       size_t count,
       const char *format,
       va_list argptr 
    );
    

    参数

    • 缓冲区 - 输出的存储位置。
    • sizeOfBuffer - 输出缓冲区的大小。
    • count - 要写入的最大字符数(不包括终止的 null)或 _TRUNCATE。
    • 格式 - 格式规范。
    • argptr - 指向参数列表的指针。

    请注意,这不仅仅是类型映射的问题:固定参数的数量是不同的,因此是不可调和的。我也不清楚(可能也是标准委员会)同时拥有“sizeOfBuffer”和“count”有什么好处;两次看起来相同的信息(或者,至少,代码通常会为两个参数写成相同的值)。

    【讨论】:

    • @Jonathan Leffler:你知道这些 API 在 Mac 上是否可用吗?
    • 它们不在主系统库 - /usr/lib/libSystem.B.dylib 中。我还没有检查机器上的每个 dylib,但我怀疑它们的缺席会很明显,在这里和大多数基于 Unix 的机器上。哦,大约 6 个月前,有人确实为字符串例程创建了安全库;你也许可以在网上找到它(如果你不能问我)。
    • 我进行了彻底的搜索 (find / -name '*.dylib' -print0 | xargs -0 nm -og | grep '_s$') 并没有找到任何 TR 24731 安全功能。有许多以“_s”结尾的函数,但没有一个是相关的。
    【解决方案5】:

    特别是,我正在寻找至少以下功能的“安全”实现:fopen vfprintf vsprintf sprintf strncpy strcpy strcat ...

    我正在尝试找出一个与传统 C 库调用尽可能相似的最佳实践 Mac API,同时增加安全性。

    这很容易。查看Apple Secure Coding Guide。 Apple 碰巧使用 BSD “更安全”的功能。


    相关:虽然 Apple 和 Microsoft 提供更安全的功能,但 Linux 没有。 GNU C 不包括“边界检查接口”(ISO 的 TR24731),因为像 Ulrich Drepper(GNU libc 看门人)这样的人反对。他反对,因为只指定了目标缓冲区。他将“更安全”的功能称为 BSD Crap。有关 Drepper 的报价,请参阅 Sourceware 邮件列表上的 Re: PATCH: safe string copy and concetation

    听从 Drepper 的建议会导致严重的失败。 CVE-2012-5958 CVE-2012-5959 CVE-2012-5960 CVE-2012-5961 CVE-2012-5962 CVE-2012-5963 CVE-2012-5964 CVE-2012-5965(也称为libupnp中的多个缓冲区溢出)为了胜利!它太糟糕了 libupnp 遵循 Drepper 并忽略了最佳实践并丢弃了“更安全”的功能。我想知道即使在今天还有多少百万路由器和网关没有打补丁......

    【讨论】:

    • @jww:你图片中列出的那些实际上更安全的功能是很久以前在Linux上提供的。只有功能失调的附件 k 被视为多余且有害而被忽略。
    • @Deduplicator - 出于好奇,您为什么认为附件 K 功能失调? (顺便说一句,它现在是标准的一部分,不再是附件)。
    • 看看(再次)这个问题:stackoverflow.com/questions/372980/… 从 C11 开始(这仍然是当前标准),他们只是将它变成了一个可选附件(这并不意味着 MS 实现将永远符合)。在它是 TR 之前。
    • @Deduplicator - 我的错,你是对的。它们仍然在附件中,但它们现在是规范性的。显然,自 2011 年以来就是这种情况(ISO/IEC 9899:2011)。但我仍然对功能失调感到好奇。
    【解决方案6】:

    由于 OSX 的用户空间基于 FreeBSD,您确实有一些更好的功能,例如 strlcpy and strlcat

    【讨论】:

      【解决方案7】:

      C 标准已经有了这些函数的一组“安全”版本。
      (对于安全一词的特定定义)

      snprintf()(和系列)提供您正在寻找的安全功能。缓冲区溢出检查。
      gcc 编译器还进行格式字符串验证(但比 MS 更好,因为验证是在编译时完成的)。

      fopen()             Not sure how you make that safer?
      vfprintf            --  These are low level functions
      vsprintf            --  These are low level functions
      sprintf             snprintf
      strncpy             Already the safe version
      strcpy              strncpy
      strcat              strncat
      

      【讨论】:

      • 我对 fopen 的理解是,“标准”版本被指定使用默认安全规则,这些规则可能与主机操作系统的“安全”默认值不匹配……或类似的东西。并不是说这是缓冲区溢出问题。我可能是错的。因为 MSVC 提供了 fopen_s 函数,所以我将它包含在我的列表中。
      • strncpy() 必须非常小心地使用以确保安全 - 当源对于目标而言太长时,它不会终止。它的填充属性(零填充到全长)有时是一个障碍,特别是如果你有一个 20 KB 的缓冲区,但主要是 50 字节的字符串。
      • @jwfearn:在 TR24731 中,标准 fopen() 和 fopen_s() 之间的主要区别在于添加了一个标志 'u'。引用:在底层系统支持概念的范围内,为写入而打开的文件应以独占(也称为非共享)访问方式打开。如果正在创建文件,并且模式字符串的第一个字符不是'u',则在底层系统支持的范围内,该文件应具有阻止系统上其他用户访问该文件的文件权限。 [...]
      【解决方案8】:

      Google Summer of Code 2010:OpenAfs 和 Google 正在赞助 Microsoft 的 String Safe 库的移植。见http://www.openafs.org/pages/gsoc.html

      【讨论】:

        【解决方案9】:

        safeclib 是一个可移植的解决方案。 MS 实现有缺陷且不安全,其他实现确实存在,但不可移植或过于幼稚。

        https://github.com/rurban/safeclib

        【讨论】:

        • 当链接到您自己的网站或内容(或您附属的内容)时,您must disclose your affiliation in the answer 以免被视为垃圾邮件。根据 Stack Exchange 政策,在您的用户名中包含与 URL 相同的文本或在您的个人资料中提及它不被视为充分披露。您还在多个答案中链接到您的存储库。您需要在所有这些中披露从属关系。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-27
        • 1970-01-01
        • 2023-02-20
        • 2013-11-27
        • 2010-09-07
        相关资源
        最近更新 更多