DeepCode: vista lateral

Imagem 1

Há pouco tempo, o DeepCode, um analisador estático baseado em aprendizado de máquina, começou a dar suporte à validação de projetos em C e C ++. E agora podemos ver na prática como os resultados da análise estática clássica e da análise estática baseada no aprendizado de máquina diferem.

Já mencionamos o DeepCode em nosso artigo " Usando o Machine Learning na análise estática do código-fonte dos programas ". E logo eles publicaram o artigo " DeepCode adiciona suporte à análise de código estático baseado em IA para C e C ++ " com o anúncio do suporte para a análise de projetos escritos em C e C ++.

Para analisar os resultados da análise do DeepCode, decidi verificar o projeto PhysX. Não tenho nenhum objetivo aqui apresentar uma análise dos erros encontrados pelo PVS-Studio ou DeepCode neste projeto, mas eu só queria pegar um projeto de terceiros e ver seu exemplo, como os analisadores funcionarão.

A execução da análise do DeepCode é extremamente simples. A análise dos projetos postados no GitHub é iniciada automaticamente. Embora exista um problema, uma vez que todo o repositório foi verificado em uma linha, apesar de conter vários projetos, e os avisos para eles acabaram sendo confundidos. Além disso, a velocidade da análise indica que o código não está compilado e muitos erros podem estar ocultos no analisador.

O resultado geral do DeepCode é o seguinte:

Quadro 2

Isso não reflete bem o número de viagens, pois elas são agrupadas em um aviso, como mostrarei mais adiante. Obviamente, é óbvio que a análise estática de C / C ++ no DeepCode é uma inovação, e o trabalho está apenas começando, mas acho que já é possível tirar algumas conclusões.

Começando a examinar os acionadores do DeepCode, me deparei com o seguinte aviso para um erro encontrado pelo analisador pelo menos 31 vezes:

Quadro 3

Essa resposta chamou minha atenção, já que o PVS-Studio tinha um diagnóstico semelhante e, além disso, examinei essa resposta em outro artigo. Quando o conheci pela primeira vez, pareceu-me que poucas pessoas escreviam uma constante matemática "à mão".

E o PVS-Studio também encontrou esse uso descuidado de constantes, mas não havia mais que 31, mas 52 ensaios desse tipo. Quase imediatamente, houve um momento em que o aprendizado de máquina era inferior à abordagem clássica. Se o código de erro, pelo menos de alguma forma, se afasta de um erro muito típico desse tipo, é muito mais difícil encontrá-lo. Para a análise estática clássica, não é necessário que o erro ocorra muitas e muitas vezes - é suficiente para o desenvolvedor de diagnóstico apresentar um princípio geral para a aparência de um erro.

Além disso, o diagnóstico do PVS-Studio funciona não apenas no número Pi ou em outras constantes usadas com freqüência, mas também em determinadas (por exemplo, a constante Landau-Ramanujan), cujo uso incorreto pode ser difícil de entender, mesmo com uma grande base de treinamento, devido ao seu uso raro. .

Como o DeepCode trabalhou estritamente em uma constante no formato 3.1415, fora de interesse, adicionei 4 casas decimais (3.14159263) à constante no código em um único local, e a operação desapareceu.

Quadro 7

O PVS-Studio trabalha em várias opções para arredondar constantes.

Quadro 8

Incluindo a resposta à seção alterada:

V624 Provavelmente, há uma impressão incorreta na constante '3.14159263'. Considere usar a constante M_PI de <math.h>. Crab.cpp 219

E o ponto aqui não é que isso seja algum tipo de erro terrível / arredondamento grosseiro / a possibilidade de cometer um erro de digitação, mas que confirme que o treinamento no código será limitado a esse mesmo código e, se algo acontecer sair do padrão geral ou aparecer apenas em casos raros, um erro ou falha pode passar despercebida.

Vamos considerar mais uma operação. O DeepCode emitiu um aviso no seguinte código:

bool Shader::loadShaderCode(const char* fragmentShaderCode, ....)
{
  ....
  if (mFragmentShaderSource != NULL)
    free(mFragmentShaderSource);

  ....

  if (mFragmentShaderSource != NULL)
    free(mFragmentShaderSource);
  ....
}

Quadro 10

O PVS-Studio teve mais reclamações sobre esse código:

  1. V809 Verifying that a pointer value is not NULL is not required. The 'if (mFragmentShaderSource != 0)' check can be removed. Shader.cpp 178
  2. V809 Verifying that a pointer value is not NULL is not required. The 'if (mFragmentShaderSource != 0)' check can be removed. Shader.cpp 194
  3. V774 The 'mFragmentShaderSource' pointer was used after the memory was released. Shader.cpp 194
  4. V586 The 'free' function is called twice for deallocation of the same memory space. Inspect the first argument. Check lines: 179, 195. Shader.cpp 195

Você pode pensar que o acionamento do DeepCode acabou sendo conciso e o PVS-Studio produziu muitos acionamentos desnecessários, mas não é assim, e cada acionador aqui tem seu próprio significado.

As duas primeiras operações estão relacionadas à verificação excessiva de ponteiros, pois essa verificação não é necessária para usar a função free () . A terceira atuação indica que não é seguro usar o ponteiro depois que ele for liberado. Mesmo que o ponteiro em si não seja desreferenciado, mas apenas verificado, ele ainda é suspeito e geralmente indica um erro de digitação. Bem, a última operação apenas aponta para o mesmo problema que o DeepCode descobriu.

Quadro 11

Havia uma captura de tela no artigo do DeepCode sobre o início do suporte ao C / C ++ , que, como nos pareceu, contém um falso positivo. O aviso indica um possível estouro de buffer, mas a memória para o buffer é alocada levando em consideração o comprimento da casa e o comprimento da linha, que é adicionalmente adicionada por + 1. Portanto, há espaço suficiente no buffer, com certeza.

O problema aqui pode ser a falta de verificação de que a memória foi alocada com êxito. Talvez o aviso do DeepCode neste caso diga outra coisa: a primeira frase do aviso não ajuda particularmente a entender qual é o erro e sobre o que o analisador realmente jura.

Nesse caso, talvez outro fator sobre o qual escrevemos seja acionado - a dificuldade de criar avisos significativos. Os avisos após a classificação das respostas de um analisador treinado são escritos por pessoas ou formados a partir de comentários sobre confirmações / documentação. Mas gerar um bom aviso pode ser difícil se o padrão de erro tiver sido deduzido pelo aprendizado de máquina, e não pelo desenvolvedor.

Eu queria saber o que precisa ser alterado neste código para que o aviso desapareça. Mas, estranhamente, em uma seção de código completamente semelhante no novo arquivo, esse aviso não ocorreu e outro apareceu.

Quadro 5

Talvez a questão seja que o ambiente para o qual o analisador foi orientado tenha desaparecido ou fiz algo errado. Mas a instabilidade dos avisos, o fato de sua aparência ser imprevisível, pode levar, na minha opinião, a inconvenientes não apenas para os usuários, mas também para os desenvolvedores do próprio analisador e complicam o processo de desenvolvimento.

Em geral, embora a maioria das respostas do DeepCode ainda não seja falsa, seu número é tão pequeno que o resultado de seu trabalho no momento é mais escasso do que preciso. Um pequeno número de falsos positivos é acompanhado por um pequeno número de positivos em princípio.

Vou apresentar alguns erros interessantes encontrados pelo PVS-Studio.

Fragmento N1

V773A função foi encerrada sem liberar o ponteiro 'linha'. É possível um vazamento de memória. PxTkBmpLoader.cpp 166

bool BmpLoader::loadBmp(PxFileHandle f)
{
  ....
  int lineLen = ....;
  unsigned char* line = static_cast<unsigned char*>(malloc(lineLen));
  for(int i = info.Height-1; i >= 0; i--)
  {
    num = fread(line, 1, static_cast<size_t>(lineLen), f);
    if (num != static_cast<size_t>(lineLen)) { fclose(f); return false; }
    ....
  }
  free(line);
  return true;
}

Aqui, caso a leitura do arquivo seja interrompida, ocorre um vazamento de memória, pois a memória não é liberada antes de retornar da função.

Fragmento N2

V595 O ponteiro 'gSphereActor' foi utilizado antes de ser verificado no nullptr. Verifique as linhas: 228, 230. SnippetContactReportCCD.cpp 228

void initScene()
{
  ....
  gSphereActor = gPhysics->createRigidDynamic(spherePose);
  gSphereActor->setRigidBodyFlag(PxRigidBodyFlag::eENABLE_CCD, true);

  if (!gSphereActor)
    return;
  ....
}

O ponteiro do gSphereActor é desreferenciado, mas imediatamente depois é verificado se há nullptr e a função é encerrada. Ou seja, um ponteiro nulo é possível aqui, mas a desreferenciação ainda ocorre. Houve 24 erros desse tipo no projeto PhysX.

O DeepCode forneceu apenas pontos positivos para um tipo específico de casos (como eu o entendo), onde o ponteiro foi inicializado como zero e não havia outras atribuições no campo de visão. O PVS-Studio não funcionou com esse código, pois na maioria das vezes esse incêndio é falso, porque o ponteiro pode obter um valor em outra unidade de tradução (a maioria dos incêndios ocorreu em variáveis ​​globais). Este exemplo mostra que menos falsos positivos na análise estática treinada não são necessariamente verdadeiros.

Quadro 15

Quadro 13

Aqui, o DeepCode, provavelmente, por algum motivo, indica a inicialização incorreta. Em vez disso, a inicialização gCooking destacou a inicialização gFoundation , embora o índice não esteja mais iluminado.

Fragmento N3

V517 O uso do padrão 'if (A) {...} else if (A) {...}' foi detectado. Há uma probabilidade de presença de erro lógico. Verifique as linhas: 266, 268. AABox.cpp 266

bool AABox::initRT(int depthSamples, int coverageSamples)
{
  ....
  int query;
  ....
  if (....)  
  {
    ....
    if ( query < coverageSamples)
      ret = false;
    else if ( query > coverageSamples) 
      coverageSamples = query;
    ....
    if ( query < depthSamples)
      ret = false;
    else if ( query > depthSamples)
      depthSamples = query;
  }
  else {
    ....
    if ( query < depthSamples)
      ret = false;
    else if ( query < depthSamples) // <=
      depthSamples = query;
    ....
  }
  ....
}

Aqui parece que o erro de copiar e colar apareceu. O analisador detectou a mesma condição em if e else . No if-else anterior , você pode ver que o ">" no if e o "<" no else normalmente são verificados . Embora o padrão pareça extremamente simples, não encontrei essa resposta entre os avisos do DeepCode, embora pareça ser um padrão muito simples de detectar.

Conclusão

É claro que o DeepCode acaba de anunciar o suporte ao C / C ++ e faz parte do produto que apenas começou a se desenvolver. Você também pode experimentá-lo em seus projetos. Em seu serviço, uma janela de feedback conveniente foi implementada caso você recebesse um aviso falso.

No entanto, agora podemos ver as deficiências que acompanham o aprendizado de máquina na análise estática. Portanto, somos céticos em relação à idéia de adicionar aprendizado de máquina aos analisadores estáticos, uma vez que o ganho é duvidoso: o mesmo suporte do analisador, a documentação ou a redação ou edição da documentação e o trabalho no próprio diagnóstico são necessários. Além disso, os problemas encontrados durante o desenvolvimento exigem soluções mais complexas e habilidades específicas dos desenvolvedores, o que reduz o número de possíveis candidatos ao expandir a equipe. De fato, o desenvolvedor nesse caso não deve ser apenas um especialista na linguagem analisada, mas também possuir conhecimento no campo de aprendizado de máquina.


Se você deseja compartilhar este artigo com um público que fala inglês, use o link da tradução: Victoria Khanieva. DeepCode: perspectiva externa .

All Articles