Warning C6029
Possible buffer overrun in call to 'function'
Possible buffer overrun in called function due to an unchecked buffer length/size parameter.
Remarks
This warning indicates that code passes an unchecked size to a function that takes a buffer and a size. The code doesn't verify that the data read from some external source is smaller than the buffer size. An attacker might intentionally specify a larger than expected value for the size, which can lead to a buffer overrun. Generally, whenever you read data from an untrusted external source, make sure to verify it for validity. It's appropriate to verify the size to make sure it's in the expected range.
Code analysis name: USING_TAINTED_DATA
Example
The following code generates this warning when it calls the annotated function std::fread
two times. The code uses the first call to determine the length of the data to read in later calls. After the first call, analysis marks dataSize
as coming from an untrusted source. Therefore, when the code passes the untrusted value to the second std::fread
call, the analyzer generates this warning. A malicious actor could modify the file and cause the call to std::fread
to overflow the buffer
array. In real world code, you should also handle error recovery based on the return value of std::fread
. For simplicity, these examples intentionally leave out error recovery code.
void processData(FILE* file)
{
const size_t MAX_BUFFER_SIZE = 100;
uint32_t buffer[MAX_BUFFER_SIZE]{};
uint8_t dataSize = 0;
// Read length data from the beginning of the file
fread(&dataSize, sizeof(uint8_t), 1, file);
// Read the rest of the data based on the dataSize
fread(buffer, sizeof(uint32_t), dataSize, file);
}
The fix for the issue depends on the nature of the data and the behavior of the annotated function that triggers the diagnostic. For more information, see the documentation for that function. A straightforward fix is to check the size before the second call to std:fread
. In the next example, we throw an exception to terminate the function. Most real-world code would instead have an error recovery strategy that's specific to the scenario.
void processData(FILE* file)
{
const size_t MAX_BUFFER_SIZE = 100;
uint32_t buffer[MAX_BUFFER_SIZE]{};
uint8_t dataSize = 0;
fread(&dataSize, sizeof(uint32_t), 1, file);
if (dataSize > MAX_BUFFER_SIZE)
{
throw std::runtime_error("file data unexpected size");
}
fread(buffer, sizeof(uint32_t), dataSize, file);
}
In std:fread
and similar functions, the code may need to read large amounts of data. To handle large data, you can allocate the size of the buffer dynamically after the size becomes known. Or, you can call std:fread
multiple times as needed to read in the rest of the data. If you allocate the buffer dynamically, we recommend you put a limit on the size to avoid introducing an out-of-memory exploit for large values. We don't use this approach in our example because it's already bounded by the size of uint8_t
.
void processDataDynamic(FILE* file)
{
uint8_t dataSize = 0;
fread(&dataSize, sizeof(uint8_t), 1, file);
// Vector with `dataSize` default initialized objects
std::vector<uint32_t> vecBuffer(dataSize);
fread(&vecBuffer[0], sizeof(uint32_t), dataSize, file);
}
void processDataMultiple(FILE* file)
{
const size_t MAX_BUFFER_SIZE = 100;
uint32_t buffer[MAX_BUFFER_SIZE]{};
uint8_t dataSize = 0;
fread(&dataSize, sizeof(uint32_t), 1, file);
while( dataSize > 0 )
{
size_t readSize = dataSize > MAX_BUFFER_SIZE ? MAX_BUFFER_SIZE : dataSize;
readSize = fread(buffer, sizeof(uint32_t), readSize, file);
dataSize = dataSize - readSize;
// Process the data in `buffer`...
}
}
See also
Rule sets for C++ code
Using SAL Annotations to reduce code defects