您是否使用TR 24731的“安全”功能?[关闭]


76

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库的扩展-第二部分:动态分配功能

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

问题

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

1
@MarcusJ:嗯,我需要澄清您对“添加strlen()到代码”的含义。在某些情况下,肯定会有错误strlen()的答案,例如,将缓冲区传递给I / O函数(例如gets_s())。但是也许您可以详细说明您的想法?
乔纳森·莱夫勒

1
@MarcusJ:您无法使用,realloc()因为需要保护的功能没有分配。strcpy()例如,该函数不执行内存分配;即使您有垃圾回收,也不能理智地修改它来进行内存分配,因为人们通常不使用返回值,而是使用strcpy()在后续操作中作为第一个参数传递的值。出现类似的问题gets()strcat()。那些至少返回char *可能指向重新分配的空间的a(不是保证参数已分配)。[…续…]
乔纳森·莱夫勒

1
[…继续…]对于诸如sprintf()不返回a的函数,问题更加严重char *。他们无法告诉调用代码他们已“重新分配”了放置结果的内存。请注意,TR 24731-2之所以没有进入C11的原因之一是,它们将是第一个显式进行内存分配的功能-除了malloc()et al。请花点时间研究这些功能的作用,附件K / TR 24731-1的功能,其为什么执行的原理等等。做出决定有一些合理的理由。
乔纳森·勒夫勒

1
嗯,这个问题现在不适合堆栈溢出;)
Antti Haapala

1
@AnttiHaapala:可能不是(尽管我认为最近这些要求太严格了)。我想为它争取至少一个历史地位(历史锁定)。可以按照“ TR24731(附件K)功能是否可用?”这样的措辞来重新表述,但是……。特别是,我认为答案中的信息对C程序员很有用,并且应该托管在SO的C部分中。曾几何时,它可能已合并到“文档”中-现在不会发生。
乔纳森·莱夫勒19'Apr

Answers:


67

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

我使用glibc,因此知道我将不必处理这些废话,因为glibc的主要维护者Ulrich Drepper谈到了这个话题

提议的safe(R)ISO C库无法完全解决。...提议使程序员的生活更加艰难,这将无济于事。但这正是所建议的。...他们都需要做更多的工作,或者只是愚蠢。

他继续详细介绍了许多建议功能的问题,并在其他地方指出glibc将永远不支持这一功能。

奥斯汀组(负责维护POSIX)提供的TR,他们的意见和提供的委员会响应的非常严格审查这里。奥斯汀小组的审查工作非常出色,详细介绍了TR的许多问题,因此在此我不再赘述。

因此,最重要的是:我不使用支持或将支持此实现的实现,也不打算使用这些函数,并且在TR中看不到任何正值。我个人认为,TR仍然以任何形式存在的唯一原因是因为它受到Microsoft的大力推动,尽管事实证明,TR遭到了广泛反对,但最近微软已经证明能够通过标准委员会来解决问题。如果将这些功能标准化,那么我认为它们将不会被广泛使用,因为该提案已经存在了几年,并且未能获得任何真正的社区支持。


22
无论任何其他赎回情况,将乌尔里希·德雷珀(Ulrich Drepper)的观点视为任何形式的权威都是当场驳斥您观点的好方法。
帕维尔·米纳夫

56
@Pavel,我引用了Drepper作为glibc的授权。尽管您可能与他有任何个人问题,但他还是glibc的首席维护者,几乎可以决定是否会在glibc中包括或不包括哪些内容。我完全没有根据TR的观点对我的案子提出异议,您的评论似乎是基于对一个人的强烈个人仇恨,如果这使您无法看清全局,那是您应该工作的错上。
罗伯特·格兰伯

7
+1。不知道自己在做什么的人应该使用VB,而不是C :-)
paxdiablo 2010年

8
为了胜利:多个libupnp缓冲区溢出。您不喜欢的那些更安全的功能将阻止其中的大多数功能。兜售坏建议的好工作:)
jww 2014年

10
^^^使用知道如何正确编程的程序员会停止所有程序……
John Hascall

30

直接回答问题

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

  • 您是否使用支持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库中的接口,这些接口将a返回char *到字符串的开头(例如strcpy()and strcat()),以便代替返回指向起点的指针,他们将返回指向新字符串末尾的空字节的指针。这将使某些常见的习惯用法(例如,将字符串重复连接到另一个字符串的末尾)更加有效,因为避免重复使用的代码所表现出的二次行为将变得微不足道strcat()。像TR24731版本一样,所有替换都将确保输出字符串的空终止。我并不完全反对检查接口,也不反对异常处理功能。这是一件棘手的事情。


Microsoft的实现与标准规范不同

更新(2011-05-08)

另请参阅此问题。可悲的是,对于TR24731函数的实用性来说,这是致命的,对于Microsoft实现和标准而言,某些函数的定义有所不同,这使它们对我而言毫无用处。我在那儿的答案引用了vsnprintf_s()

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

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

参量

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

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

同样,scanf_s()其亲属也存在问题。 微软表示,缓冲区长度参数的类型为unsigned(明确指出“大小参数的类型为unsigned,不是size_t”)。相反,在附件K中,size参数的类型为rsize_t,是size_trsize_t的另一个名称size_t,但RSIZE_MAX小于SIZE_MAX)的受限变型。因此,同样,scanf_s()对于Microsoft C和Standard C ,代码调用的编写方式必须不同。

最初,我打算使用“安全”功能作为一种方法,使某些代码可以在Windows和Unix上干净地编译,而无需编写条件代码。由于Microsoft和ISO功能并不总是相同,因此失败了,因此有很多时间要放弃。


Microsoftvsnprintf()在Visual Studio 2015中的更改

在的Visual Studio 2015文档中vsnprintf(),它注意到界面已更改:

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

但是,Microsoft的界面vsnprintf_s()没有更改。


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

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

与MSDN变体localtime_s()定义为:

POSIX变体localtime_r()定义为:

除了名称,C11标准和POSIX函数是等效的。尽管Microsoft功能与C11标准共享一个名称,但其界面却有所不同。

差异另一个例子是微软strtok_s()和附录K的strtok_s()

vs:

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

问题Mac和Linux上的不同声明qsort_r()的答案也qsort_s()像Microsoft定义的和qsort_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与C11 wrt TR24731相同。

关于将附件K从C11的继承人中删除的提案

重复数据删除器在对另一个问题的评论中指出,ISO C标准委员会(ISO / IEC JTC1 / SC22 / WG14)已有一项提案。

它包含对附件K功能的某些现有实现的引用-没有一个被广泛使用(但如果您有兴趣,可以在文档中找到它们)。

该文件以建议结尾:

因此,我们建议将附件K从C标准的下一个修订版本中删除,或者不推荐使用,然后删除。

我支持该建议。

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


3
好吧,如果MS与标准相抵触,那就是需要更改的MS,而不是标准...
cmaster-恢复莫妮卡2013年

5
我也想这么想,但是他们已经安装了基础并且不会破坏向后兼容性,因此在实践中,这仍然意味着MS将不支持比C89(C90)更现代的C标准。
乔纳森·莱夫勒2013年

您可以尝试在Windows clang.llvm.org/get_started.html上使用clang编译器。它支持C17,并且可以与Visual Studio工具无缝地协同工作。
annoying_squid

7

好了,现在站在TR24731-2:

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

为什么?
因为它们恰好一次又一次地满足了我的需求:一种强大,灵活,安全和(相对)易于使用的方式,可将任何文本格式化为新分配的字符串。

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


感谢您的反馈。遗憾的是,TR24731-2不是C2011标准的一部分,但通常是一组有用的功能。我也对fmemopen()POSIX中的功能有所保留。该open_memstream()功能很有趣。我怀疑使用它有些麻烦,因为您将指针传递给了缓冲区指针和size变量。但是,总的来说,TR23731-2很好。
乔纳森·莱夫勒2013年

我宁愿看到的vasprintf是一个“通用” vformat函数,该函数除了接受通常的vprintf参数之外还接受anint(*func)(void*,size_t,char const*)和a void*,并为要输出的每个字符“ span”调用提供的函数[如果该函数返回,则提早返回一个非零值]。可以从这种功能中合成自动分配的sprintf,但是通用版本也将与自定义分配器兼容。
超级猫

@supercat在这些方面,有几个stdio变体可以让您指定自己的读/写功能。因此,例如,您可以执行某些操作FILE *fp = ffunopen(myreadfunc, mywritefunc),然后调用fprintf任何stdio函数,并调用您的回调。我还看到了至少一个fmemopen内置自动分配的变体的实现-再次,这意味着您可以为任何顺序的输出调用(不仅仅是)获得自动分配*printf
史蒂夫·萨米特

@SteveSummit:FILE*包含指向功能表的指针将非常有用,但是我的主要观点是,库例程不应该依赖,malloc而是允许用户代码以最适合预期使用情况的方式来管理内存。 。
超级猫

5

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

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

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

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

  • 由于不正确使用标准C函数,我们遇到了许多错误。
  • 我不满意传递给TR函数或从TR函数或在某些情况下返回其POSIX替代方法的信息。

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

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

vsnprintf,strncpy,strncat的安全版本。

有没有价值或负值的东西吗?

fopen_s和类似的函数对我个人而言几乎没有任何价值。如果fopen返回NULL,我很好。您应该始终检查函数的返回值。如果有人忽略了fopen的返回值,那将使他们检查fopen_s的返回值是什么?我知道fopen_s将返回更特定的错误信息,这在某些情况下可能有用。但是对于我正在从事的工作,这并不重要。

您打算将来使用该库吗?

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

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

没有。


谢谢你的信息,凯文。
乔纳森·莱夫勒

5

不,这些功能绝对没有用,除了鼓励编写代码以便仅在Windows上编译外,它们没有其他目的。

snprintf是绝对安全的(如果正确实现),因此snprintf_s是毫无意义的。如果缓冲区溢出(清除连接字符串),strcat_s将破坏数据。还有许多其他的例子,它们完全不了解事物的运作方式。

真正有用的功能是BSD strlcpy和strlcat。但是Microsoft和Drepper都出于自私的原因而拒绝了这些程序,这引起了各地C程序员的烦恼。


2
感谢您的输入。我不确定“完全无知”是否合适,但是我同意新功能并不总是可以得到应有的改进。
乔纳森·莱夫勒

我认为strlcat样式的函数可以更有效地接受指向目标缓冲区末尾的指针,并返回指向目标末尾空字节的指针。这样一来,一个调用的返回值就可以传递给另一个调用,从而将多个值连接到一个字符串上,而不必每次都重新扫描目标字符串。
2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.