82

ISO C 委员会 ( ISO/IEC JTC1/SC21/WG14 ) 已发布TR 24731-1并正在研究TR 24731-2

TR 24731-1:C 库扩展第一部分:边界检查接口

WG14 正在研究更安全的 C 库函数的 TR。此 TR 旨在修改现有程序,通常通过添加带有缓冲区长度的额外参数。最新的草案在文件 N1225 中。文件 N1173 中有一个基本原理。这将成为技术报告类型 2。

TR 24731-2:C 库的扩展 - 第 II 部分:动态分配函数

WG14 正在研究更安全的 C 库函数的 TR。此 TR 面向使用动态分配而不是缓冲区长度的额外参数的新程序。最新草案在文件 N1337 中。这将成为技术报告类型 2。

问题

  • 您是否使用支持 TR24731-1 功能的库或编译器?
  • 如果是这样,哪个编译器或库以及在哪个平台上?
  • 您是否因为修复代码以使用这些功能而发现任何错误?
  • 哪些功能提供最大价值?
  • 有没有提供没有价值或负价值的东西?
  • 您打算将来使用图书馆吗?
  • 您是否在跟踪 TR24731-2 的工作?
4

5 回答 5

68

自这些 TR 成立以来(当时它还是一个 TR),我一直是他们的直言不讳的批评者,并且永远不会在我的任何软件中使用它们。它们掩盖症状而不是解决原因,我认为如果有的话,它们将对软件设计产生负面影响,因为它们提供了一种错误的安全感,而不是促进可以更有效地实现相同目标的现有实践。我并不孤单,事实上,在开发这些 TR 的委员会之外,我不知道有哪个主要支持者。

我使用 glibc,因此我知道我将不必处理这种废话,正如 glibc 的首席维护者 Ulrich Drepper关于这个话题所说的那样:

提议的 safe(r) ISO C 库未能完全解决问题。... 提议让程序员的生活更加艰难并没有帮助。但这正是所提议的。...他们都需要做更多的工作,或者只是很傻。

他继续详细说明了一些建议功能的问题,并在其他地方表示 glibc 永远不会支持这一点。

奥斯汀集团(负责维护 POSIX)对 TR 进行了非常严格的审查,他们的评论和委员会的回应可在此处获得。Austin Group 的评论很好地详细说明了 TR 的许多问题,所以我不会在这里详细介绍。

所以底线是:我不使用支持或将支持这一点的实现,我不打算使用这些功能,而且我认为 TR 没有积极价值。我个人认为,TR 仍然以任何形式存在的唯一原因是因为它受到了微软的大力推动,尽管受到广泛反对,微软最近证明了它非常有能力通过标准委员会来解决问题。如果这些功能被标准化,我认为它们不会被广泛使用,因为该提案已经存在了几年并且未能获得任何真正的社区支持。

于 2008-12-16T22:59:57.390 回答
31

直接回答问题

我喜欢罗伯特的回答,但我对我提出的问题也有一些看法。

  • 您是否使用支持 TR24731-1 功能的库或编译器?

    不,我没有。

  • 如果是这样,哪个编译器或库以及在哪个平台上?

    我相信这些功能是由 MS Visual Studio(例如 MS VC++ 2008 版)提供的,并且有警告鼓励您使用它们。

  • 您是否因为修复代码以使用这些功能而发现任何错误?

    还没有。而且我不希望在我的代码中发现很多。我使用的其他一些代码 - 也许。但我还没有被说服。

  • 哪些功能提供最大价值?

    我喜欢 printf_s() 系列函数不接受 ' %n' 格式说明符的事实。

  • 有没有提供没有价值或负价值的东西?

    和函数tmpfile_s()令人tmpnam_s()非常失望。他们确实需要更像mkstemp()是创建文件并打开文件以确保不存在 TOCTOU(检查时间,使用时间)漏洞。就目前而言,这两个提供的价值很小。

    我也认为这strerrorlen_s()提供的价值很小。

  • 您打算将来使用图书馆吗?

    我对此有两种看法。我开始在一个库上工作,该库将在标准 C 库上实现 TR 24731 的功能,但被证明它工作正常所需的大量单元测试所困扰。我不确定是否继续这样做。我有一些代码想移植到 Windows(主要是出于在所有平台上提供支持的不正当愿望——它已经在 Unix 衍生产品上工作了几十年)。不幸的是,为了让它在没有来自 MSVC 编译器的警告的情况下编译,我必须在代码中添加一些东西,以防止 MSVC 使用完全可靠(小心使用时)标准 C 库函数对我胡说八道。这并不开胃。我不得不处理在此期间发展起来的系统的大部分价值,这已经够糟糕的了。不得不处理某人的有趣想法(让人们在不需要时采用 TR 24731)很烦人。这也是我开始开发库的部分原因——允许我在 Unix 和 Windows 上使用相同的接口。但我不确定我会从这里做什么。

  • 您是否在跟踪 TR24731-2 的工作?

    直到我在收集问题数据时访问标准站点时,我才开始跟踪它。和函数可能很有价值asprintf()vasprintf()我会用那些。我不确定内存流 I/O 功能。在strdup()C 级别进行标准化将是向前迈出的一大步。这对我来说似乎比第 1 部分(边界检查)接口更具争议性。

总的来说,我不相信第 1 部分“边界检查接口”。第 2 部分“动态分配函数”的草稿中的材料更好。

如果由我决定,我会沿着第 1 部分的路线移动,但我还修改了 C99 标准 C 库中返回 achar *到字符串开头的接口(例如strcpy()and strcat()),而不是返回一个指向开始的指针,它们将返回一个指向新字符串末尾的空字节的指针。这将使一些常见的习惯用法(例如重复将字符串连接到另一个字符串的末尾)更有效,因为它可以避免重复使用的代码表现出的二次行为变得微不足道strcat()。替换都将确保输出字符串的空终止,就像 TR24731 版本一样。我并不完全反对检查接口的想法,也不反对异常处理功能。这是一项棘手的业务。


微软的实现与标准规范不一样

更新 (2011-05-08)

另请参阅此问题。可悲的是,对于 TR24731 函数的有用性而言,这是致命的,Microsoft 实现和标准之间的某些函数的定义不同,使它们无用(对我来说)。我的回答引用了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”有什么好处;它看起来像两次相同的信息(或者,至少,代码通常会为两个参数写入相同的值)。

scanf_s()同样,与它的亲属 也有问题。微软表示缓冲区长度参数的类型是unsigned(明确说明'大小参数是类型unsigned,而不是size_t')。相比之下,在附件 K 中,size 参数的类型是rsize_t,它是size_t(rsize_t的另一个名称size_t,但RSIZE_MAX小于SIZE_MAX) 的受限变体。因此,再次,对于 Microsoft C 和标准 C,代码调用scanf_s()必须以不同的方式编写。

最初,我打算使用“安全”函数作为让一些代码在 Windows 和 Unix 上干净编译的一种方式,而无需编写条件代码。由于 Microsoft 和 ISO 的功能并不总是相同,因此失败了,因此几乎是时候放弃了。


微软vsnprintf()在 Visual Studio 2015中的变化

在 Visual Studio 2015 文档中vsnprintf(),它指出界面已更改:

从 Visual Studio 2015 和 Windows 10 中的 UCRT 开始,vsnprintf不再与_vsnprintf. vsnprintf功能符合C99标准;_vnsprintf为向后兼容而保留。

但是,微软的界面vsnprintf_s()并没有改变。


Microsoft 和 Annex K 之间差异的其他示例

的 C11 标准变体localtime_s()在 ISO/IEC 9899:2011 附件 K.3.8.2.4 中定义为:

struct tm *localtime_s(const time_t * restrict timer,
                       struct tm * restrict result);

localtime_s()定义为的 MSDN 变体相比:

errno_t localtime_s(struct tm* _tm, const time_t *time);

POSIX 变体localtime_r()定义为:

struct tm *localtime_r(const time_t *restrict timer,
                       struct tm *restrict result);

除了名称之外,C11 标准和 POSIX 函数是等价的。Microsoft 函数在接口上有所不同,尽管它与 C11 标准同名。

另一个不同的例子是Microsoftstrtok_s()和 Annex K 的strtok_s()

char *strtok_s(char *strToken, const char *strDelimit, char **context); 

与:

char *strtok_s(char * restrict s1, rsize_t * restrict s1max, const char * restrict s2, char ** restrict ptr);

请注意,Microsoft 变体有 3 个参数,而 Annex K 变体有 4 个。这意味着 Microsoft 的参数列表strtok_s()与 POSIX 兼容strtok_r()——因此,如果您更改函数名称(例如通过宏),对这些参数的调用实际上是可互换的——但是标准 C(附件 K)版本与两者都不同,带有额外的参数。

Mac 和 Linux 上的不同声明的qsort_r()问题有一个答案,该答案也讨论qsort_s()了 Microsoftqsort_s()定义的和 TR24731-1 定义的 - 再次,接口是不同的。


ISO/IEC 9899:2011 — C11 标准

C11 标准(2010 年 12 月草案;您可以一次从 ANSI 网上商店以 30 美元的价格获得最终标准ISO/IEC 9899:2011的 PDF 副本)确实具有 TR24731-1 功能作为可选功能标准的一部分。它们在附件 K(边界检查接口)中定义,它是“规范性的”而不是“信息性的”,但它是可选的。

C11 标准中没有 TR24731-2 函数——这很可悲,因为该vasprintf()函数及其相关函数可能真的很有用。

快速总结:

  • C11 包含 TR24731-1
  • C11 不含 TR24731-2
  • C18 与 TR24731 中的 C11 相同。

从 C11 的继任者中删除附件 K 的提案

Deduplicator在对另一个问题的评论中指出,在ISO C 标准委员会(ISO/IEC JTC1/SC22/WG14)之前有一个提案

它包含对附件 K 函数的一些现有实现的引用——它们都没有被广泛使用(但如果您有兴趣,可以通过文档找到它们)。

该文件以建议结尾:

因此,我们建议附录 K 要么从 C 标准的下一个修订版中删除,要么弃用然后删除。

我支持该建议。

C18 标准并没有改变 Annex K 的状态。有一篇论文N2336主张对 Annex K 进行一些更改,修复其缺陷而不是完全删除它。

于 2008-12-17T07:43:09.330 回答
7

好的,现在是TR24731-2的支架:

是的,自从我在 glibc 中看到它们以来,我就一直在使用asprintf()/ ,是的,我是它们的坚定拥护者。vasprintf()

为什么?
因为它们一次又一次地准确地提供了我需要的东西:一种强大、灵活、安全且(相对)易于使用的方法,可以将任何文本格式化为新分配的字符串。

我也非常赞成这些memstream功能:像asprintf()open_memstream()(不是fmemopen()!!!)为您分配足够大的缓冲区并为您提供FILE*打印功能,因此您的打印功能可能完全不知道它们是否打印成字符串或文件,您可以简单地忘记您需要多少空间。

于 2013-06-27T21:42:19.043 回答
5

您是否使用支持 TR24731-1 功能的库或编译器?如果是这样,哪个编译器或库以及在哪个平台上?

是的,Visual Studio 2005 和 2008(显然是用于 Win32 开发)。

您是否因为修复代码以使用这些功能而发现任何错误?

有点.... 我编写了自己的安全函数库(我们经常使用的只有大约 15 个),可以在多个平台上使用——Linux、Windows、VxWorks、INtime、RTX 和 uItron。创建安全函数的原因是:

  • 由于不正确地使用标准 C 函数,我们遇到了大量的错误。
  • 我对传入或从 TR 函数返回的信息不满意,或者在某些情况下,对它们的 POSIX 替代方案不满意。

一旦编写了函数,就会发现更多的错误。所以是的,使用这些功能是有价值的。

哪些功能提供最大价值?

更安全的 vsnprintf、strncpy、strncat 版本。

有没有提供没有价值或负价值的东西?

fopen_s 和类似的功能对我个人来说几乎没有什么价值。如果 fopen 返回 NULL,我很好。您应该始终检查函数的返回值。如果有人忽略了 fopen 的返回值,是什么让他们检查 fopen_s 的返回值?我知道 fopen_s 将返回更具体的错误信息,这在某些情况下可能很有用。但是对于我正在做的事情,这并不重要。

您打算将来使用图书馆吗?

我们现在正在使用它——在我们自己的“安全”库中。

您是否在跟踪 TR24731-2 的工作?

不。

于 2009-05-15T16:10:05.173 回答
5

不,这些函数绝对没用,除了鼓励编写代码以使其只能在 Windows 上编译之外,没有任何其他用途。

snprintf 是完全安全的(如果正确实施),所以 snprintf_s 毫无意义。如果缓冲区溢出, strcat_s 将销毁数据(通过清除连接到的字符串)。还有许多其他完全不了解事物如何运作的例子。

真正有用的函数是 BSD strlcpy 和 strlcat。但微软和 Drepper 都出于自私的原因拒绝了这些,这让世界各地的 C 程序员很恼火。

于 2014-02-11T19:02:13.660 回答