DeepCode: vue latérale

Image 1

Il n'y a pas si longtemps, DeepCode, un analyseur statique basé sur l'apprentissage automatique, a commencé à prendre en charge la validation des projets C et C ++. Et maintenant, nous pouvons voir en pratique comment les résultats de l'analyse statique classique et de l'analyse statique basée sur l'apprentissage automatique diffèrent.

Nous avons déjà mentionné DeepCode dans notre article « Utilisation de l'apprentissage automatique dans l'analyse statique du code source des programmes ». Et bientôt ils ont publié l'article " DeepCode ajoute un support d'analyse de code statique basé sur l'IA pour C et C ++ " avec l'annonce du support pour l'analyse des projets écrits en C et C ++.

Pour regarder les résultats de l'analyse DeepCode, j'ai décidé de vérifier le projet PhysX. Je n'ai pas besoin ici de présenter une analyse des erreurs trouvées par PVS-Studio ou DeepCode dans ce projet, mais je voulais juste prendre un projet tiers et regarder son exemple, comment les analyseurs fonctionneront.

L'exécution de l'analyse DeepCode est extrêmement simple. L'analyse des projets publiés sur GitHub démarre automatiquement. Bien qu'il y ait un problème, puisque l'intégralité du référentiel a été vérifié consécutivement, bien qu'il contenait divers projets, et les avertissements à leur destination ont fini par être mélangés. De plus, la vitesse d'analyse indique que le code n'est pas compilé et de nombreuses erreurs peuvent être cachées à l'analyseur.

Le résultat global de DeepCode est le suivant:

Image 2

Cela ne reflète pas tout à fait le nombre de voyages, car ils sont regroupés en un seul avertissement, comme je le montrerai plus tard. Bien sûr, il est évident que l'analyse statique de C / C ++ dans DeepCode est une innovation, et le travail sur elle vient de commencer, mais je pense qu'il est déjà possible de tirer quelques conclusions.

Commençant à examiner les déclencheurs DeepCode, je suis tombé sur l'avertissement suivant pour une erreur rencontrée par l'analyseur pas moins de 31 fois:

Image 3

Cette réponse a attiré mon attention, car PVS-Studio avait un diagnostic similaire et, en outre, j'ai examiné une telle réponse dans un autre article. Lorsque je l'ai rencontré pour la première fois, il me semblait que très peu de gens écriraient une constante mathématique «à la main».

Et PVS-Studio a également constaté une telle utilisation imprudente des constantes, mais il n'y a pas eu plus de 31, mais 52 essais de ce type. Presque immédiatement, il y a eu un moment où l'apprentissage automatique était inférieur à l'approche classique. Si le code d'erreur s'écarte d'une manière ou d'une autre d'une erreur très typique de ce type, il devient beaucoup plus difficile de la trouver. Pour l'analyse statique classique, il n'est pas nécessaire que l'erreur se produise plusieurs fois - il suffit au développeur de diagnostic de trouver un principe général pour l'apparition d'une erreur.

De plus, les diagnostics PVS-Studio fonctionnent non seulement sur le nombre Pi ou d'autres constantes fréquemment utilisées, mais également sur des constantes spécifiques (par exemple, la constante de Landau-Ramanujan), dont l'utilisation erronée peut être difficile à détecter sur la base d'une base de formation même importante en raison de leur utilisation rare. .

Étant donné que DeepCode fonctionnait strictement sur une constante de la forme 3.1415, par intérêt, j'ai ajouté 4 décimales (3.14159263) à la constante dans le code en un seul endroit, et l'opération a disparu.

Image 7

PVS-Studio travaille sur diverses options pour arrondir les constantes.

Image 8

Y compris une réponse à la section modifiée:

V624 Il y a probablement une faute d' impression dans la constante '3.14159263'. Pensez à utiliser la constante M_PI de <math.h>. Crab.cpp 219

Et le point ici n'est pas que ce soit une sorte d'erreur terrible / d'arrondi grossier / la possibilité d'une faute de frappe, mais qu'il confirme que la formation sur le code sera limitée à ce code même et, si quelque chose se passe sortir du schéma général ou n'apparaître que dans de rares cas, une erreur ou un défaut peut passer inaperçu.

Prenons une autre opération. DeepCode a émis un avertissement sur le code suivant:

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

  ....

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

Image 10

PVS-Studio a eu plus de plaintes concernant ce code:

  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

Vous pourriez penser que le déclenchement DeepCode s'est avéré simplement concis, et PVS-Studio a produit beaucoup de déclenchements inutiles, mais ce n'est pas le cas, et chaque déclenchement ici a sa propre signification.

Les deux premières opérations sont liées à une vérification excessive du pointeur, car une telle vérification n'est pas nécessaire pour utiliser la fonction free () . Le troisième actionnement indique qu'il n'est pas sûr d'utiliser le pointeur après l'avoir relâché. Même si le pointeur lui-même n'est pas déréférencé, mais seulement vérifié, il est toujours suspect et indique souvent une faute de frappe. Eh bien, la dernière opération indique simplement le même problème que DeepCode a découvert.

Image 11

Il y avait une capture d'écran dans l' article de DeepCode sur le début de la prise en charge de C / C ++ , qui, il nous semblait, contient un faux positif. L'avertissement indique un débordement possible de la mémoire tampon, cependant, la mémoire pour la mémoire tampon est allouée en tenant compte de la longueur de la maison et de la longueur de la ligne, qui est en outre ajoutée par + 1. Ainsi, il y aura suffisamment d'espace dans la mémoire tampon à coup sûr.

Le problème ici peut être le manque de vérification que la mémoire a été correctement allouée. Peut-être que l'avertissement DeepCode dans ce cas indique autre chose: la première phrase de l'avertissement n'aide pas particulièrement à comprendre ce qu'est l'erreur et ce que l'analyseur jure réellement.

Dans ce cas, peut-être un autre facteur sur lequel nous avons écrit est déclenché - la difficulté de créer des avertissements significatifs. Les avertissements après classification des réponses d'un analyseur qualifié sont écrits par des personnes ou formés à partir de commentaires sur les validations / la documentation. Mais générer un bon avertissement peut être difficile si le modèle d'erreur a été déduit grâce à l'apprentissage automatique et non par le développeur.

Je me demandais ce qui devait être changé dans ce code pour que l'avertissement disparaisse. Mais, étrangement, sur une section de code complètement similaire dans le nouveau fichier, un tel avertissement ne s'est pas produit et un autre est apparu.

Image 5

Peut-être que le fait est que l'environnement vers lequel l'analyseur était orienté a disparu, ou j'ai fait quelque chose de mal. Mais l'instabilité des avertissements, le fait que leur apparence soit imprévisible, peut conduire, à mon avis, à gêner non seulement les utilisateurs, mais aussi les développeurs de l'analyseur lui-même et compliquer le processus de développement.

En général, bien que la majorité des réponses DeepCode ne soient pas encore fausses, leur nombre est si petit que le résultat de son travail pour le moment est plutôt rare qu'exact. Un petit nombre de faux positifs s'accompagne en principe d'un petit nombre de positifs.

Je vais vous donner quelques erreurs intéressantes trouvées par PVS-Studio.

Fragment N1

V773La fonction a été quittée sans relâcher le pointeur «ligne». Une fuite de mémoire est possible. 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;
}

Ici, au cas où la lecture du fichier a été interrompue, une fuite de mémoire se produit, car la mémoire n'est pas libérée avant le retour de la fonction.

Fragment N2

V595 Le pointeur «gSphereActor» a été utilisé avant d'être vérifié par rapport à nullptr. Vérifiez les lignes: 228, 230. SnippetContactReportCCD.cpp 228

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

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

Le pointeur gSphereActor est déréférencé, mais immédiatement après, il est vérifié pour nullptr, et la fonction se ferme. Autrement dit, un pointeur nul est possible ici, mais le déréférencement se produit toujours. Il y avait 24 erreurs de ce type dans le projet PhysX.

DeepCode n'a renvoyé que des positifs pour un type spécifique de cas (si je comprends bien), où le pointeur a été initialement initialisé à zéro, et il n'y avait aucune autre affectation dans le champ de vision. PVS-Studio n'a pas fonctionné sur un tel code, car le plus souvent un tel incendie sera faux, car le pointeur peut obtenir une valeur dans une autre unité de traduction (la plupart des incendies étaient sur des variables globales). Cet exemple montre que moins de faux positifs dans l'analyse statique entraînée ne sont pas nécessairement vrais.

Image 15

Image 13

Ici, DeepCode, très probablement, pour une raison quelconque, indique une mauvaise initialisation. Au lieu de cela, l'initialisation gCooking a mis en évidence l'initialisation gFoundation , bien que l'index ne soit pas davantage éclairé.

Fragment N3

V517 L'utilisation du modèle 'if (A) {...} else if (A) {...}' a été détectée. Il y a une probabilité de présence d'erreur logique. Vérifiez les lignes: 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;
    ....
  }
  ....
}

Ici, il semble que l'erreur de copier-coller s'est glissée. L'analyseur a détecté la même condition dans if and else . D'après le précédent if-else, vous pouvez voir que le ">" dans if et le "<" dans else sont généralement vérifiés . Bien que le modèle semble extrêmement simple, je n'ai pas trouvé une telle réponse parmi les avertissements DeepCode, bien qu'il semble être un modèle très simple à détecter.

Conclusion

Bien sûr, DeepCode vient d'annoncer la prise en charge de C / C ++ et cela fait partie de leur produit qui vient de commencer à se développer. Vous pouvez également l'essayer sur vos projets, à leur service, une fenêtre de rétroaction pratique a été mise en place au cas où vous auriez reçu un faux avertissement.

Cependant, nous pouvons maintenant voir les lacunes qui accompagnent l'apprentissage automatique dans l'analyse statique. Par conséquent, nous sommes sceptiques quant à l'idée d'ajouter l'apprentissage automatique aux analyseurs statiques, car le gain est douteux: la même prise en charge de l'analyseur, la rédaction ou l'édition de la documentation et le travail sur les diagnostics eux-mêmes sont nécessaires. De plus, les problèmes rencontrés au cours du développement nécessitent des solutions plus complexes et des compétences spécifiques de la part des développeurs, ce qui réduit le nombre de candidats potentiels lors de l'expansion de l'équipe. En effet, le développeur dans ce cas doit non seulement être un spécialiste du langage analysé, mais également posséder des connaissances dans le domaine du machine learning.


Si vous souhaitez partager cet article avec un public anglophone, veuillez utiliser le lien de traduction: Victoria Khanieva. DeepCode: en dehors de la perspective .

All Articles