为什么std :: is_permutation()这样的函数本身不安全?

时间:2013-07-10 23:03:03

标签: c++ algorithm security stl

C和C ++程序员在过去十年左右的时间里一直受到打击,因为他们经常无法执行正确的边界检查,特别是在字符串上。这些故障经常导致主要软件产品出现严重的安全问题。由于缓冲区溢出不安全性已被充分理解,因此驱动程序进行适当的边界检查已使许多程序员远离传统的缓冲区和字符串操作函数,如strcpy()sprintf(),至少部分是因为这种趋势这些函数通过假设目标缓冲区的大小来引发缓冲区溢出问题。 STL类型(如std::stringstd::vector)的优点之一是它们对缓冲区访问的强大控制。

但有一件事困扰着我。 <algorithms> C ++标头中许多最广泛使用的函数似乎都积极地乞求溢出滥用:具体来说,那些采用begin迭代器(尤其是InputIterator)而没有匹配{{1}的函数迭代器。例如:

end

最后一个例子 - template <class InputIterator, class OutputIterator> OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result); template <class InputIterator, class OutputIterator, class UnaryOperation> OutputIterator transform (InputIterator first1, InputIterator last1, OutputIterator result, UnaryOperation op); template <class ForwardIterator1, class ForwardIterator2> bool is_permutation (ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2); 特别有启发性。 is_permutation()copy()已被充分理解,因此C ++程序员知道在调用这些函数或使用transform()之类的东西来确保输出容器根据需要增长。因此,可以证明虽然back_insertercopy()可以被滥用,但任何事情都可以,并且程序员很容易接受有关这些功能的最佳实践的教育。

transform()是一个棘手的案例。只要看一下上面的函数声明,你会对第二个范围的大小(以is_permutation()开头的那个)假设什么?第二个范围是否需要与第一个范围相同,或者不小,或者不大?我敢打赌,这些问题的简单答案并没有出现在你脑海中。对于大多数程序员而言,“置换”的概念并不像复制概念那样舒适和熟悉。因此,使first2错误并以某种方式溢出缓冲区似乎相对容易。

“查一查!”我听你说。嗯,是的,当然。但是,如果程序员记住他们应该做的一切,并查找其他所有内容,那么我们就不会有错误和安全漏洞了吗?

为什么不is_permutation()和类似函数(即函数接受所有输入迭代器而不是每个范围的完整开始端迭代器对)需要一个完整的开始 - 结束对所有输入范围? (请注意,is_permutation(),例如, 符合此要求。)像lexicographical_compare()这样的函数实际上并不像我想象的那样不安全吗?

3 个答案:

答案 0 :(得分:8)

大多数语言本质上都是不安全的,程序员可以正确使用它。程序员必须知道调用函数之前是否正确使用的参数是正确的。

此外,在某些情况下,例如copy,它允许在开放范围上使用前向迭代器。例如:

std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout," "));

没有相应的迭代器来标记流的结尾,并且流确实没有结束,您可以继续不断添加它。

答案 1 :(得分:7)

在C ++ 14中,有四个迭代器版本的equalis_permutationmismatch来解决这一问题。

答案 2 :(得分:1)

我不确定在is_permutation的第二个范围内引入 last 迭代器会使函数变得不那么笨拙。我认为这会让它更加混乱。

排列的东西是语义本身就是名称。要检查一个序列是否是另一个序列的排列,您希望没有 last 迭代器的序列至少与第一个序列一样长。

如果情况并非如此,那么您不需要致电is_permutation,因为它根本不能是排列。如果它更长,你期望它不会迭代超过第一个序列的长度 - 为什么会这样?嗯,它没有 - 这就是你所期望的,所以没有信心丢失。

C ++确实希望程序员采取基本的预防措施,并使我们在许多情况下负责边界检查。如果不对程序员进行控制,语言的力量就会减弱。如果我正在调用is_permutation,那么我知道我的第二个迭代器不会溢出,因为我知道排列是什么。我当然不想浪费周期进行无意义的边界检查。

我认为这句老话适用:以强大的力量来承担更大的责任。这很公平,不是吗?