我最近使用了静态分析工具(Checkmarx)来扫描旧游戏引擎的源代码,以查看其中是否存在任何缓冲区溢出漏洞。我很惊讶地看到以下代码被标记为缓冲区溢出的可能来源:
// Get a pointer to a file that describes a 3D model
std::string filename = "my_3D_model.obj"
FILE* stream;
fopen_s(&stream, filename.c_str(), "rb");
// Read the number of vertices that make up the 3D model
int numVertices = 0;
fread(&numVertices, sizeof(int), 1, stream);
// Read the vertices and store them in a vector
// The static analysis tool doesn't complain about the use of numVertices to
// reserve space and to read from the file
std::vector<Vertex> vertices;
vertices.reserve(numVertices);
fread(vertices.data(), sizeof(Vertex), numVertices, stream);
// ...
// Copy the vertices from the vector to an array that has been allocated on the heap
// The static analysis tool complains because numVertices, which was read from a file,
// is being used as an index
Vertex* meshVertices = new Vertex[numVertices];
for (int i = 0; i < numVertices; i++)
{
meshVertices[i] = vertices[i];
}
静态分析工具将此称为“来自输入缓冲区溢出漏洞的索引”。它发现int i
的范围是从0到numVertices
,该范围是从文件读取的,并且认为这可能导致缓冲区溢出。但是在这种特殊情况下真的有可能吗? numVertices
用于分配缓冲区的大小,因此我看不到缓冲区溢出如何发生。如果有可能,您将如何预防呢?请注意,我无法更改缓冲区的类型,因为那样会破坏太多的代码。
感谢您提供任何信息!
答案 0 :(得分:3)
警告绝对正确。您正在从外部来源读取签名的int
,然后在调用size_t
和reserve
时将其提升为fread
。由于size_t
是无符号类型,因此,如果您从文件中读取的值为负数,则提升为size_t
时的结果值将比numVertices
的绝对值大得多在大多数平台上。结果是您将尝试保留和读取大量向量。如果这两个操作成功,那么您将尝试new
使用负数组大小。如果您走得那么远,可以保证您的for
循环永远不会执行。
解决方法是将值读取为无符号int或更好的size_t
格式,尽管这将需要您更改写入该值的代码。另一个选择是至少验证该值。信任来自外部来源的数据是成为本周攻击的好方法。