A análise de fluxo de dados é parte integrante de qualquer analisador de código estático moderno. No entanto, do lado de fora, não está muito claro o que é e o que é mais importante - por que é necessário. Até agora, algumas pessoas associam a análise estática à busca por algo no código de acordo com um determinado padrão. Portanto, de vez em quando fazemos anotações nas quais demonstramos como esta ou aquela tecnologia, usada no analisador PVS-Studio, ajuda a revelar outro erro interessante. Hoje é apenas um artigo em que consideraremos um bug em uma das implementações do padrão de codificação de dados binários Base64.
Tudo começou com a verificação da versão mais recente da biblioteca Qt 6. Este foi um artigo clássico separado , onde descrevi 77 erros encontrados. Acontece que a princípio decidi folhear o relatório, ainda sem esconder os avisos relacionados a bibliotecas de terceiros. Em outras palavras, não desativei os avisos relacionados a \ src \ 3rdparty nas configurações. E aconteceu que imediatamente me deparei com um exemplo interessante de um erro na Biblioteca de importação de ativos aberta , sobre o qual decidi fazer esta pequena observação separada.
, , PVS-Studio, . . , , ", PVS-Studio ".
, , , Open Asset Import Library (assimp). : \src\3rdparty\assimp\src\code\FBX\FBXUtil.cpp.
std::string EncodeBase64(const char* data, size_t length)
{
// calculate extra bytes needed to get a multiple of 3
size_t extraBytes = 3 - length % 3;
// number of base64 bytes
size_t encodedBytes = 4 * (length + extraBytes) / 3;
std::string encoded_string(encodedBytes, '=');
// read blocks of 3 bytes
for (size_t ib3 = 0; ib3 < length / 3; ib3++)
{
const size_t iByte = ib3 * 3;
const size_t iEncodedByte = ib3 * 4;
const char* currData = &data[iByte];
EncodeByteBlock(currData, encoded_string, iEncodedByte);
}
// if size of data is not a multiple of 3,
// also encode the final bytes (and add zeros where needed)
if (extraBytes > 0)
{
char finalBytes[4] = { 0,0,0,0 };
memcpy(&finalBytes[0], &data[length - length % 3], length % 3);
const size_t iEncodedByte = encodedBytes - 4;
EncodeByteBlock(&finalBytes[0], encoded_string, iEncodedByte);
// add '=' at the end
for (size_t i = 0; i < 4 * extraBytes / 3; i++)
encoded_string[encodedBytes - i - 1] = '=';
}
return encoded_string;
}
, . , , Base64 :). :
Ok, . Base64. 64 . - A-Z, a-z 0-9 (62 ) 2 , . 3 4 .
, , "=". . .
, . , . , , - . "- " : V547 [CWE-571] Expression 'extraBytes > 0' is always true. FBXUtil.cpp 224
, , extraBytes:
// calculate extra bytes needed to get a multiple of 3
size_t extraBytes = 3 - length % 3;
, , 3. 3. :
size_t extraBytes = length % 3;
, , , 5 , 5 % 3 = 2, 2 . 6 , , 6 % 3 = 0.
, , . :
size_t extraBytes = (3 - length % 3) % 3;
, . , :
size_t extraBytes = 3 - length % 3;
. length, [0..2]. PVS-Studio , . . . Value Range Analysis. .
:
size_t extraBytes = 3 - [0..2];
, extraBytes . : [1..3].
, . , , , :
if (extraBytes > 0)
, , , , , , , .
, , , . . , 6 . 8 . , .
// calculate extra bytes needed to get a multiple of 3
size_t extraBytes = 3 - length % 3; // 3-6%3 = 3
// number of base64 bytes
size_t encodedBytes = 4 * (length + extraBytes) / 3; // 4*(6+3)/3 = 12
std::string encoded_string(encodedBytes, '=');
, 12 , 8. – .
. , - , , Base64. , , , " , ".
, : Andrey Karpov. Why PVS-Studio Uses Data Flow Analysis: Based on Gripping Error in Open Asset Import Library.