Recherche de bogues dans le code source Amazon Web Services SDK for .NET

Image 1


Salutations à tous les fans de critiquer le code de quelqu'un d'autre. :) Aujourd'hui, dans notre laboratoire, le nouveau matériel de recherche est le code source du projet AWS SDK pour .NET. À un moment donné, nous avons écrit un article sur la vérification du kit SDK AWS pour C ++. Ensuite, il n'y avait rien de particulièrement intéressant. Voyons comment la version .NET du kit AWS SDK nous plaira. Une bonne occasion de démontrer une fois de plus les capacités de l'analyseur PVS-Studio, ainsi que de rendre le monde un peu plus parfait.

Le Kit de développement logiciel (SDK) .NET Amazon Web Services (AWS) est une boîte à outils de développeur conçue pour créer des applications basées sur .NET dans l'infrastructure AWS et simplifier considérablement le processus d'écriture de code. Le SDK comprend des suites d'API .NET pour divers services AWS tels qu'Amazon S3, Amazon EC2, DynamoDB et autres. Le code source du SDK est hébergé sur GitHub .

Comme je l'ai dit, à un moment donné, nous avons écrit un article sur la vérification du kit SDK AWS pour C ++. L'article s'est avéré être petit - il n'y a eu que quelques erreurs sur 512 000 lignes de code. Cette fois, nous avons affaire à une quantité de code beaucoup plus importante, dont environ 34 000 fichiers cs, et le nombre total de lignes de code (à l'exclusion des lignes vides) est impressionnant de 5 millions. Une petite partie du code (200 000 lignes dans 664 fichiers cs) tombe sur les tests, je ne les ai pas considérés.

Si la qualité du code .NET de la version SDK est approximativement la même que celle de C ++ (deux erreurs par 512 KLOC), alors nous devrions obtenir environ 10 fois plus d'erreurs. C'est, bien sûr, une méthode de calcul très inexacte qui ne prend pas en compte les caractéristiques du langage et de nombreux autres facteurs, mais je ne pense pas que le lecteur veuille maintenant se plonger dans des discussions ennuyeuses. Au lieu de cela, je propose d'aller directement aux résultats.

La vérification a été effectuée à l'aide de PVS-Studio version 6.27. Incroyablement, l'analyseur a pu détecter environ 40 erreurs dans le code AWS SDK for .NET qui méritaient d'être mentionnées. Cela indique non seulement la haute qualité du code SDK (environ 4 erreurs pour 512 KLOC), mais également la qualité comparable du travail C # de l'analyseur PVS-Studio par rapport à C ++. Excellent résultat!

Les auteurs du kit AWS SDK pour .NET sont excellents. De projet en projet, ils démontrent une qualité de code incroyable. Cela peut servir de bon exemple pour d'autres équipes. Mais, bien sûr, je ne serais pas le développeur d'un analyseur statique si je n'avais pas inséré mes 5 kopecks. :) Nous travaillons déjà avec l'équipe d'Amazon Lumberyard sur l'utilisation de PVS-Studio. Mais comme il s'agit d'une très grande entreprise avec un tas de divisions à travers le monde, il est très probable que l'équipe AWS SDK for .NET n'ait jamais entendu parler de PVS-Studio. En tout cas, dans le code SDK, je n'ai trouvé aucun signe d'utilisation de notre analyseur, bien que cela ne signifie rien. Cependant, l'équipe, au minimum, utilise l' analyseur intégré à Visual Studio. C'est bien, mais les vérifications de code peuvent être améliorées :).

En conséquence, j'ai quand même réussi à trouver des erreurs dans le code du SDK, et enfin, il est temps de les partager.

Erreur de logique

Avertissement PVS-Studio: V3008 [CWE-563] La variable 'this.linker.s3.region' reçoit des valeurs successives deux fois. C'est peut-être une erreur. Vérifiez les lignes: 116, 114. AWSSDK.DynamoDBv2.Net45 S3Link.cs 116

public string Region { get { .... } set { if (String.IsNullOrEmpty(value)) { this.linker.s3.region = "us-east-1"; } this.linker.s3.region = value; } } 

L'analyseur avertit de la réaffectation de la valeur de la même variable. À partir du code, il devient clair que cela est dû à une erreur qui viole la logique de fonctionnement: la valeur de la variable this.linker.s3.region sera toujours égale à la valeur , quelle que soit la condition if (String.IsNullOrEmpty (value)) . Dans le corps du bloc if , le retour a été ignoré. Le code doit être corrigé comme suit:

 public string Region { get { .... } set { if (String.IsNullOrEmpty(value)) { this.linker.s3.region = "us-east-1"; return; } this.linker.s3.region = value; } } 

Récursion infinie

PVS-Studio Warning: V3110 [CWE-674] Possible récursion infinie à l'intérieur de la propriété 'OnFailure'. AWSSDK.ElasticMapReduce.Net45 ResizeJobFlowStep.cs 171

 OnFailure? onFailure = null; public OnFailure? OnFailure { get { return this.OnFailure; } // <= set { this.onFailure = value; } } 

Un exemple de faute de frappe classique qui provoque une récursion infinie dans l' accesseur get de la propriété OnFailure . Au lieu de renvoyer la valeur du champ privé, onFailure fait référence à la propriété OnFailure . Option corrigée:

 public OnFailure? OnFailure { get { return this.onFailure; } set { this.onFailure = value; } } 

Vous demandez: "Comment cela a-t-il fonctionné?" Jusqu'à présent, aucun moyen. La propriété n'est utilisée nulle part, mais elle est temporaire. À un moment donné, quelqu'un commencera à l'utiliser et obtiendra certainement un résultat inattendu. Pour éviter de telles fautes de frappe, il est recommandé de ne pas utiliser d'identificateurs qui ne diffèrent que dans la première casse.

Une autre note à cette conception est l'utilisation d'un identifiant qui correspond complètement au nom du type OnFailure . Du point de vue du compilateur, c'est tout à fait acceptable, mais cela rend la perception humaine du code plus difficile.

Une autre erreur similaire:

Avertissement PVS-Studio: V3110 [CWE-674] Possible récursion infinie à l'intérieur de la propriété 'SSES3'. AWSSDK.S3.Net45 InventoryEncryption.cs 37

 private SSES3 sSES3; public SSES3 SSES3 { get { return this.SSES3; } set { this.SSES3 = value; } } 

La situation est identique à celle décrite ci-dessus. Seulement ici, une récursion infinie se produira lors de l'accès à la propriété SSES3 pour la lecture et l'écriture. Option corrigée:

 public SSES3 SSES3 { get { return this.sSES3; } set { this.sSES3 = value; } } 

Test de pleine conscience

Ce qui suit est une tâche d'un programmeur désireux d'utiliser la technique du copier-coller. Regardez à quoi ressemble le code dans l'éditeur Visual Studio et essayez de trouver l'erreur.

Image 3


Avertissement PVS-Studio: V3029 Les expressions conditionnelles des instructions «if» situées côte à côte sont identiques. Vérifiez les lignes: 91, 95. AWSSDK.AppSync.Net45 CreateApiKeyResponseUnmarshaller.cs 91

J'ai réduit le corps de la méthode UnmarshallException , supprimant tous les éléments inutiles. Vous pouvez maintenant voir que des vérifications identiques se succèdent:

 public override AmazonServiceException UnmarshallException(....) { .... if (errorResponse.Code != null && errorResponse.Code.Equals("LimitExceededException")) { return new LimitExceededException(errorResponse.Message, innerException, errorResponse.Type, errorResponse.Code, errorResponse.RequestId, statusCode); } if (errorResponse.Code != null && errorResponse.Code.Equals("LimitExceededException")) { return new LimitExceededException(errorResponse.Message, innerException, errorResponse.Type, errorResponse.Code, errorResponse.RequestId, statusCode); } .... } 

Il peut sembler que l'erreur n'est pas grossière - juste une vérification supplémentaire. Mais souvent, un tel modèle peut indiquer des problèmes plus graves dans le code lorsque certaines vérifications nécessaires ne sont pas effectuées.

Il y a quelques erreurs plus similaires dans le code.

Avertissements de PVS-Studio:

  • V3029 Les expressions conditionnelles des instructions «if» situées côte à côte sont identiques. Vérifiez les lignes: 75, 79. AWSSDK.CloudDirectory.Net45 CreateSchemaResponseUnmarshaller.cs 75
  • V3029 Les expressions conditionnelles des instructions «if» situées côte à côte sont identiques. Vérifiez les lignes: 105, 109. AWSSDK.CloudDirectory.Net45 GetSchemaAsJsonResponseUnmarshaller.cs 105
  • V3029 Les expressions conditionnelles des instructions «if» situées côte à côte sont identiques. Vérifiez les lignes: 201, 205. AWSSDK.CodeCommit.Net45 PostCommentForPullRequestResponseUnmarshaller.cs 201
  • V3029 Les expressions conditionnelles des instructions «if» situées côte à côte sont identiques. Vérifiez les lignes: 101, 105. AWSSDK.CognitoIdentityProvider.Net45 VerifySoftwareTokenResponseUnmarshaller.cs 101
  • V3029 Les expressions conditionnelles des instructions «if» situées côte à côte sont identiques. Vérifiez les lignes: 72, 76. AWSSDK.Glue.Net45 UpdateConnectionResponseUnmarshaller.cs 72
  • V3029 Les expressions conditionnelles des instructions «if» situées côte à côte sont identiques. Vérifiez les lignes: 123, 127. AWSSDK.Neptune.Net45 RestoreDBClusterFromSnapshotResponseUnmarshaller.cs 123
  • V3029 Les expressions conditionnelles des instructions «if» situées côte à côte sont identiques. Vérifiez les lignes: 167, 171. AWSSDK.Neptune.Net45 RestoreDBClusterFromSnapshotResponseUnmarshaller.cs 167
  • V3029 Les expressions conditionnelles des instructions «if» situées côte à côte sont identiques. Vérifiez les lignes: 127, 131. AWSSDK.RDS.Net45 RestoreDBClusterFromSnapshotResponseUnmarshaller.cs 127
  • V3029 Les expressions conditionnelles des instructions «if» situées côte à côte sont identiques. Vérifiez les lignes: 171, 175. AWSSDK.RDS.Net45 RestoreDBClusterFromSnapshotResponseUnmarshaller.cs 171
  • V3029 Les expressions conditionnelles des instructions «if» situées côte à côte sont identiques. Vérifiez les lignes: 99, 103. AWSSDK.Rekognition.Net45 RecognizeCelebritiesResponseUnmarshaller.cs 99

Tu es quoi

PVS-Studio Warning: V3062 Un objet 'attributeName' est utilisé comme argument de sa propre méthode. Pensez à vérifier le premier argument réel de la méthode «Contient». AWSSDK.MobileAnalytics.Net45 CustomEvent.cs 261

 /// <summary> /// Dictionary that stores attribute for this event only. /// </summary> private Dictionary<string,string> _attributes = new Dictionary<string,string>(); /// <summary> /// Gets the attribute. /// </summary> /// <param name="attributeName">Attribute name.</param> /// <returns>The attribute. Return null of attribute doesn't /// exist.</returns> public string GetAttribute(string attributeName) { if(string.IsNullOrEmpty(attributeName)) { throw new ArgumentNullException("attributeName"); } string ret = null; lock(_lock) { if(attributeName.Contains(attributeName)) // <= ret = _attributes[attributeName]; } return ret; } 

L'analyseur a détecté une erreur dans la méthode GetAttribute : la chaîne est vérifiée pour le fait qu'elle se contient. De la description de la méthode, il s'ensuit que si le nom d'attribut (clé attributeName ) est trouvé (dans le dictionnaire _attributes ), alors la valeur d'attribut doit être retournée, sinon null . En réalité, comme la condition attributeName.Contains (attributeName) est toujours vraie, une tentative est faite pour renvoyer une valeur par une clé qui peut ne pas être trouvée dans le dictionnaire. Ensuite, au lieu de retourner null, une exception KeyNotFoundException sera levée.

Essayons de corriger ce code. Pour mieux comprendre comment procéder, jetez un œil à une autre méthode:

 /// <summary> /// Determines whether this instance has attribute the specified /// attributeName. /// </summary> /// <param name="attributeName">Attribute name.</param> /// <returns>Return true if the event has the attribute, else /// false.</returns> public bool HasAttribute(string attributeName) { if(string.IsNullOrEmpty(attributeName)) { throw new ArgumentNullException("attributeName"); } bool ret = false; lock(_lock) { ret = _attributes.ContainsKey(attributeName); } return ret; } 

Cette méthode vérifie si le nom d'attribut (clé attributeName ) existe dans le dictionnaire _attributes . Revenons à la méthode GetAttribute et corrigeons l'erreur:

 public string GetAttribute(string attributeName) { if(string.IsNullOrEmpty(attributeName)) { throw new ArgumentNullException("attributeName"); } string ret = null; lock(_lock) { if(_attributes.ContainsKey(attributeName)) ret = _attributes[attributeName]; } return ret; } 

Maintenant, la méthode fait exactement ce qui est indiqué dans la description.

Et encore une petite remarque sur ce fragment de code. J'ai remarqué que les auteurs utilisent le verrouillage lorsqu'ils travaillent avec le dictionnaire _attributes . De toute évidence, cela est nécessaire avec un accès multi-thread, mais la construction du verrou est plutôt lente et lourde. Au lieu de Dictionary dans ce cas, il pourrait être plus pratique d'utiliser une version thread-safe du dictionnaire - ConcurrentDictionary . Ensuite, le besoin de verrouillage disparaît. Bien que, je ne connaisse peut-être aucune caractéristique du projet.

Comportement suspect

Avertissement PVS-Studio: V3063 [CWE-571] Une partie de l'expression conditionnelle est toujours vraie si elle est évaluée: string.IsNullOrEmpty (inferredIndexName). AWSSDK.DynamoDBv2.PCL ContextInternal.cs 802

 private static string GetQueryIndexName(....) { .... string inferredIndexName = null; if (string.IsNullOrEmpty(specifiedIndexName) && indexNames.Count == 1) { inferredIndexName = indexNames[0]; } else if (indexNames.Contains(specifiedIndexName, StringComparer.Ordinal)) { inferredIndexName = specifiedIndexName; } else if (string.IsNullOrEmpty(inferredIndexName) && // <= indexNames.Count > 0) throw new InvalidOperationException("Local Secondary Index range key conditions are used but no index could be inferred from model. Specified index name = " + specifiedIndexName); .... } 

L'analyseur a alerté la vérification de string.IsNullOrEmpty (inferredIndexName) . En effet, la chaîne inferredIndexName est affectée null , puis la valeur de cette variable n'est modifiée nulle part, puis pour une raison quelconque, elle est vérifiée pour null ou une chaîne vide. Ça a l'air suspect. Examinons de près l'extrait de code donné. Je ne l'ai délibérément pas réduit pour une meilleure compréhension de la situation. Ainsi, dans la première instruction if (et également dans la suivante), la variable specifiedIndexName est vérifiée d'une manière ou d'une autre. Selon le résultat des vérifications, la variable inferredIndexName reçoit une nouvelle valeur. Prenons maintenant la troisième instruction if . Le corps de cet opérateur (levant une exception) sera exécuté si indexNames.Count> 0 , car la première partie de la condition est string.IsNullOrEmpty (inferredIndexName) est toujours vrai. Peut-être que les variables specifiedIndexName et inferredIndexName sont simplement confondues. Ou le troisième chèque doit être sans autre , représentant une instruction if autonome:

 if (string.IsNullOrEmpty(specifiedIndexName) && indexNames.Count == 1) { inferredIndexName = indexNames[0]; } else if (indexNames.Contains(specifiedIndexName, StringComparer.Ordinal)) { inferredIndexName = specifiedIndexName; } if (string.IsNullOrEmpty(inferredIndexName) && indexNames.Count > 0) throw new InvalidOperationException(....); 

Dans ce cas, il est difficile de donner une réponse définitive sur les options de correction de ce code. Mais il est absolument nécessaire que l'auteur l'inspecte.

NullReferenceException

Avertissement PVS-Studio: V3095 [CWE-476] L'objet 'conditionValues' a été utilisé avant d'être vérifié par rapport à null. Vérifiez les lignes: 228, 238. AWSSDK.Core.Net45 JsonPolicyWriter.cs 228

 private static void writeConditions(....) { .... foreach (....) { IList<string> conditionValues = keyEntry.Value; if (conditionValues.Count == 0) // <= continue; .... if (conditionValues != null && conditionValues.Count != 0) { .... } .... } } 

Classiques du genre. La variable conditionValues ​​est utilisée sans d'abord vérifier la valeur null . De plus, plus loin dans le code, une telle vérification est effectuée, mais il est trop tard. :)

Le code peut être corrigé comme suit:

 private static void writeConditions(....) { .... foreach (....) { IList<string> conditionValues = keyEntry.Value; if (conditionValues != null && conditionValues.Count == 0) continue; .... if (conditionValues != null && conditionValues.Count != 0) { .... } .... } } 

Quelques erreurs similaires ont été trouvées dans le code.

Avertissements de PVS-Studio:

  • V3095 [CWE-476] L'objet 'ts.Listeners' a été utilisé avant d'être vérifié par rapport à null. Vérifiez les lignes: 140, 143. AWSSDK.Core.Net45 Logger.Diagnostic.cs 140
  • V3095 [CWE-476] L'objet 'obj' a été utilisé avant d'être vérifié par rapport à null. Vérifiez les lignes: 743, 745. AWSSDK.Core.Net45 JsonMapper.cs 743
  • V3095 [CWE-476] L'objet 'multipartUploadMultipartUploadpartsList' a été utilisé avant d'être vérifié par rapport à null. Vérifiez les lignes: 65, 67. AWSSDK.S3.Net45 CompleteMultipartUploadRequestMarshaller.cs 65

L'avertissement suivant est très similaire dans sa signification, mais la situation est l'inverse de celle discutée ci-dessus.

Avertissement PVS-Studio: V3125 [CWE-476] L'objet 'état' a été utilisé après avoir été vérifié par rapport à null. Vérifiez les lignes: 139, 127. AWSSDK.Core.Net45 RefreshingAWSCredentials.cs 139

 private void UpdateToGeneratedCredentials( CredentialsRefreshState state) { string errorMessage; if (ShouldUpdate) { .... if (state == null) errorMessage = "Unable to generate temporary credentials"; else .... throw new AmazonClientException(errorMessage); } state.Expiration -= PreemptExpiryTime; // <= .... } 

L'un des fragments de code contient une vérification de la valeur de la variable d' état pour l'égalité nulle . En outre, selon le code, la variable d' état est utilisée pour se désinscrire de l'événement PreemptExpiryTime , tandis que la vérification de null n'est plus effectuée et une NullReferenceException est possible . Option de code plus sécurisée:

 private void UpdateToGeneratedCredentials( CredentialsRefreshState state) { string errorMessage; if (ShouldUpdate) { .... if (state == null) errorMessage = "Unable to generate temporary credentials"; else .... throw new AmazonClientException(errorMessage); } if (state != null) state.Expiration -= PreemptExpiryTime; .... } 

Il existe d'autres erreurs similaires dans le code.

Avertissements de PVS-Studio:

  • V3125 [CWE-476] L'objet 'wrappedRequest.Content' a été utilisé après avoir été vérifié par rapport à null. Vérifiez les lignes: 395, 383. AWSSDK.Core.Net45 HttpHandler.cs 395
  • V3125 [CWE-476] L'objet 'datasetUpdates' a été utilisé après avoir été vérifié par rapport à null. Vérifiez les lignes: 477, 437. AWSSDK.CognitoSync.Net45 Dataset.cs 477
  • V3125 [CWE-476] L'objet 'cORSConfigurationCORSConfigurationcORSRulesListValue' a été utilisé après avoir été vérifié par rapport à null. Vérifiez les lignes: 125, 111. AWSSDK.S3.Net45 PutCORSConfigurationRequestMarshaller.cs 125
  • V3125 [CWE-476] L'objet 'lifecycleConfigurationLifecycleConfigurationrulesListValue' a été utilisé après avoir été vérifié par rapport à null. Vérifiez les lignes: 157, 68. AWSSDK.S3.Net45 PutLifecycleConfigurationRequestMarshaller.cs 157
  • V3125 [CWE-476] L'objet 'this.Key' a été utilisé après avoir été vérifié par rapport à null. Vérifiez les lignes: 199, 183. AWSSDK.S3.Net45 S3PostUploadRequest.cs 199

Une réalité incontestée

Avertissement PVS-Studio: V3009 [CWE-393] Il est étrange que cette méthode renvoie toujours une seule et même valeur de 'true'. AWSSDK.Core.Net45 Lexer.cs 651

 private static bool State19 (....) { while (....) { switch (....) { case '"': .... return true; case '\\': .... return true; default: .... continue; } } return true; } 

La méthode renvoie toujours true . Voyons à quel point cela est critique pour le code appelant. J'ai suivi l'utilisation de la méthode State19 . Il participe au remplissage du tableau des gestionnaires fsm_handler_table avec d'autres méthodes similaires (il y en a 28 avec des noms, respectivement, de State1 à State28 ). Il est important de noter ici qu'en plus de State19 , des avertissements V3009 [CWE-393] ont également été émis pour certains autres gestionnaires. Ce sont les gestionnaires: State23, State26, State27, State28 . Alertes émises par l'analyseur pour eux:

  • V3009 [CWE-393] Il est étrange que cette méthode renvoie toujours une seule et même valeur de 'true'. AWSSDK.Core.Net45 Lexer.cs 752
  • V3009 [CWE-393] Il est étrange que cette méthode renvoie toujours une seule et même valeur de 'true'. AWSSDK.Core.Net45 Lexer.cs 810
  • V3009 [CWE-393] Il est étrange que cette méthode renvoie toujours une seule et même valeur de 'true'. AWSSDK.Core.Net45 Lexer.cs 822
  • V3009 [CWE-393] Il est étrange que cette méthode renvoie toujours une seule et même valeur de 'true'. AWSSDK.Core.Net45 Lexer.cs 834

Voici à quoi ressemble la déclaration et l'initialisation du tableau de gestionnaires:

 private static StateHandler[] fsm_handler_table; .... private static void PopulateFsmTables () { fsm_handler_table = new StateHandler[28] { State1, State2, .... State19, .... State23, .... State26, State27, State28 }; 

Pour être complet, voyons le code de l'un des gestionnaires, auquel l'analyseur ne s'est pas plaint, par exemple, State2 :

 private static bool State2 (....) { .... if (....) { return true; } switch (....) { .... default: return false; } } 

Et voici comment les gestionnaires sont appelés:

 public bool NextToken () { .... while (true) { handler = fsm_handler_table[state - 1]; if (! handler (fsm_context)) // <= throw new JsonException (input_char); .... } .... } 

Comme vous pouvez le voir, une exception sera levée si false est retourné. Dans notre cas, pour les gestionnaires State19, State23, State26, State27 et State28, cela ne se produira jamais. Ça a l'air suspect. D'un autre côté, jusqu'à cinq gestionnaires ont un comportement similaire (retournent toujours true ), donc peut-être que cela était prévu et n'est pas le résultat d'une faute de frappe.

Pourquoi ai-je insisté sur tout cela avec autant de détails? Cette situation est très indicative dans le sens où un analyseur statique n'est souvent capable d'indiquer qu'une conception suspecte. Et même une personne (pas une machine) qui n'a pas une connaissance suffisante du projet, ayant passé du temps à étudier le code, n'est toujours pas en mesure de donner une réponse exhaustive sur la présence d'une erreur. Le code doit être étudié par le développeur.

Des contrôles dénués de sens

Avertissement PVS-Studio: V3022 [CWE-571] L'expression 'doLog' est toujours vraie. AWSSDK.Core.Net45 StoredProfileAWSCredentials.cs 235

 private static bool ValidCredentialsExistInSharedFile(....) { .... var doLog = false; try { if (....) { return true; } else { doLog = true; } } catch (InvalidDataException) { doLog = true; } if (doLog) // <= { .... } .... } 

Faites attention à la variable doLog . Après l'initialisation avec false , alors dans le code cette variable deviendra vraie dans tous les cas. La vérification if (doLog) est donc toujours vraie. Il y avait peut-être une branche plus tôt dans cette méthode dans laquelle la variable doLog n'avait pas de valeur, puis au moment de la vérification, elle pouvait contenir la fausse valeur obtenue lors de l'initialisation. Mais maintenant, il n'y a plus de branche.

Une autre erreur similaire:

Avertissement PVS-Studio: V3022 L' expression '! Résultat' est toujours fausse. AWSSDK.CognitoSync.PCL SQLiteLocalStorage.cs 353

 public void PutValue(....) { .... bool result = PutValueHelper(....); if (!result) <= { _logger.DebugFormat("{0}", @"Cognito Sync - SQLiteStorage - Put Value Failed"); } else { UpdateLastModifiedTimestamp(....); } .... } 

L'analyseur prétend que la valeur de la variable de résultat est toujours vraie . Cela n'est possible que si la méthode PutValueHelper renvoie toujours true . Jetez un œil à cette méthode:

 private bool PutValueHelper(....) { .... if (....)) { return true; } if (record == null) { .... return true; } else { .... return true; } } 

En effet, la méthode retournera vrai dans n'importe quelle condition. De plus, l'analyseur a émis un avertissement pour cette méthode. Avertissement PVS-Studio: V3009 [CWE-393] Il est étrange que cette méthode renvoie toujours une seule et même valeur de 'true'. SQLiteLocalStorage.cs 1016

Je n'ai délibérément pas cité cet avertissement plus tôt lorsque je considérais d'autres erreurs du V3009 , mais je l'ai enregistré pour ce cas. Ainsi, l'analyseur avait raison d'indiquer l'erreur V3022 dans le code appelant.

Copiez-collez. Encore une fois

Avertissement PVS-Studio: V3001 Il existe des sous-expressions identiques 'this.token == JsonToken.String' à gauche et à droite de '||' opérateur. AWSSDK.Core.Net45 JsonReader.cs 343

 public bool Read() { .... if ( (this.token == JsonToken.ObjectEnd || this.token == JsonToken.ArrayEnd || this.token == JsonToken.String || // <= this.token == JsonToken.Boolean || this.token == JsonToken.Double || this.token == JsonToken.Int || this.token == JsonToken.UInt || this.token == JsonToken.Long || this.token == JsonToken.ULong || this.token == JsonToken.Null || this.token == JsonToken.String // <= )) { .... } .... } 

Comparez deux fois le champ this.token avec la valeur JsonToken.String de l'énumération JsonToken . L'une des comparaisons devrait probablement contenir une valeur d'énumération différente. Si oui, alors une grave erreur a été commise.

Refactorisation + insouciance?

Avertissement PVS-Studio: V3025 [CWE-685] Format incorrect. Un nombre différent d'éléments de format est attendu lors de l'appel de la fonction 'Format'. Arguments non utilisés: AWSConfigs.AWSRegionKey. AWSSDK.Core.Net45 AWSRegion.cs 116

 public InstanceProfileAWSRegion() { .... if (region == null) { throw new InvalidOperationException( string.Format(CultureInfo.InvariantCulture, "EC2 instance metadata was not available or did not contain region information.", AWSConfigs.AWSRegionKey)); } .... } 

Probablement, la chaîne de format de la méthode string.Format contenait précédemment le spécificateur de sortie {0} , pour lequel l'argument AWSConfigs.AWSRegionKey a été défini. Ensuite, la ligne a été modifiée, le spécificateur a disparu, mais ils ont oublié de supprimer l'argument. Le fragment de code ci-dessus fonctionne sans erreur (une exception serait levée dans le cas contraire - un spécificateur sans argument), mais il a l'air moche. Le code doit être fixé comme suit:

 if (region == null) { throw new InvalidOperationException( "EC2 instance metadata was not available or did not contain region information."); } 

Dangereux

Avertissement PVS-Studio: V3083 [CWE-367] L'appel non sécurisé de l'événement 'mOnSyncSuccess', NullReferenceException est possible. Pensez à affecter un événement à une variable locale avant de l'invoquer. AWSSDK.CognitoSync.PCL Dataset.cs 827

 protected void FireSyncSuccessEvent(List<Record> records) { if (mOnSyncSuccess != null) { mOnSyncSuccess(this, new SyncSuccessEventArgs(records)); } } 

Une situation assez courante d'un appel non sécurisé au gestionnaire d'événements. Entre la vérification de la variable mOnSyncSuccess pour null et l'appel du gestionnaire, un événement peut être désabonné et sa valeur deviendra nulle. La probabilité d'un tel scénario est faible, mais il vaut toujours mieux sécuriser le code:

 protected void FireSyncSuccessEvent(List<Record> records) { mOnSyncSuccess?.Invoke(this, new SyncSuccessEventArgs(records)); } 

Il existe d'autres erreurs similaires dans le code.

Avertissements de PVS-Studio:

  • V3083 [CWE-367] L'appel non sécurisé de l'événement 'mOnSyncFailure', NullReferenceException est possible. Pensez à affecter un événement à une variable locale avant de l'invoquer. AWSSDK.CognitoSync.PCL Dataset.cs 839
  • V3083 [CWE-367] L'appel d'événement à risque, NullReferenceException est possible. Pensez à affecter un événement à une variable locale avant de l'invoquer. AWSSDK.Core.PCL AmazonServiceClient.cs 332
  • V3083 [CWE-367] L'appel d'événement à risque, NullReferenceException est possible. Pensez à affecter un événement à une variable locale avant de l'invoquer. AWSSDK.Core.PCL AmazonServiceClient.cs 344
  • V3083 [CWE-367] L'appel d'événement à risque, NullReferenceException est possible. Pensez à affecter un événement à une variable locale avant de l'invoquer. AWSSDK.Core.PCL AmazonServiceClient.cs 357
  • V3083 [CWE-367] L'appel non sécurisé de l'événement 'mExceptionEvent', NullReferenceException est possible. Pensez à affecter un événement à une variable locale avant de l'invoquer. AWSSDK.Core.PCL AmazonServiceClient.cs 366
  • V3083 [CWE-367] L'appel d'événement à risque, NullReferenceException est possible. Pensez à affecter un événement à une variable locale avant de l'invoquer. AWSSDK.Core.PCL AmazonWebServiceRequest.cs 78
  • V3083 [CWE-367] L'appel non sûr de l'événement 'OnRead', NullReferenceException est possible. Pensez à affecter un événement à une variable locale avant de l'invoquer. AWSSDK.Core.PCL EventStream.cs 97
  • V3083 [CWE-367] L'appel d'événement à risque, NullReferenceException est possible. Pensez à affecter un événement à une variable locale avant de l'invoquer. AWSSDK.Core.Android NetworkReachability.cs 57
  • V3083 [CWE-367] L'appel d'événement à risque, NullReferenceException est possible. Pensez à affecter un événement à une variable locale avant de l'invoquer. AWSSDK.Core.Android NetworkReachability.cs 94
  • V3083 [CWE-367] L'appel d'événement à risque, NullReferenceException est possible. Pensez à affecter un événement à une variable locale avant de l'invoquer. AWSSDK.Core.iOS NetworkReachability.cs 54

Classe inachevée

Avertissement PVS-Studio: le type V3126 implémentant l'interface IEquatable <T> de type 'JsonData' ne remplace pas la méthode 'GetHashCode'. AWSSDK.Core.Net45 JsonData.cs 26

 public class JsonData : IJsonWrapper, IEquatable<JsonData> { .... } 

La classe JsonData contient beaucoup de code, donc je ne l'ai pas donné dans son intégralité, me limitant à le déclarer. Cette classe ne contient vraiment pas de méthode GetHashCode substituée, ce qui n'est pas sûr car elle peut conduire à un comportement erroné lors de l'utilisation du type JsonData pour fonctionner, par exemple, avec des collections. Il n'y a probablement pas de problème à l'heure actuelle, mais la stratégie d'utilisation de ce type peut changer à l'avenir. Cette erreur est décrite plus en détail dans la documentation .

Conclusion

C'est toutes les erreurs intéressantes que j'ai réussi à détecter dans le code AWS SDK for .NET à l'aide de l'analyseur statique PVS-Studio. Encore une fois, je souligne la qualité du projet. J'ai trouvé un très petit nombre d'erreurs pour 5 millions de lignes de code. Bien que, probablement, une analyse plus approfondie des avertissements émis me permette d'ajouter quelques erreurs de plus à cette liste. Mais il est également très probable que j'ai en vain attribué certains des avertissements décrits à des erreurs. Dans ce cas, des conclusions non ambiguës sont toujours faites uniquement par le développeur, qui se trouve dans le contexte du code en cours de vérification.



Si vous souhaitez partager cet article avec un public anglophone, veuillez utiliser le lien vers la traduction: Sergey Khrenov. Recherche d'erreurs dans le code source du SDK Amazon Web Services pour .NET

Source: https://habr.com/ru/post/fr437516/


All Articles