
Saudações a todos os fãs por criticarem o código de outra pessoa. :) Hoje, em nosso laboratório, novos materiais de pesquisa são o código-fonte do projeto AWS SDK para .NET. Ao mesmo tempo, escrevemos um artigo sobre como verificar o AWS SDK para C ++. Então não havia nada particularmente interessante. Vamos ver como a versão .NET do AWS SDK nos agradará. Uma boa oportunidade para demonstrar mais uma vez os recursos do analisador PVS-Studio, além de tornar o mundo um pouco mais perfeito.
O .NET SDK da Amazon Web Services (AWS) é um kit de ferramentas para desenvolvedores projetado para criar aplicativos baseados em .NET na estrutura da AWS e simplificar bastante o processo de gravação de código. O SDK inclui pacotes de API do .NET para vários serviços da AWS, como Amazon S3, Amazon EC2, DynamoDB e outros. O código fonte do SDK está hospedado no
GitHub .
Como eu disse, escrevemos
um artigo sobre como verificar o AWS SDK para C ++. O artigo acabou sendo pequeno - houve apenas alguns erros em 512 mil linhas de código. Desta vez, estamos lidando com uma quantidade muito maior de código, que inclui cerca de 34 mil arquivos cs, e o número total de linhas de código (excluindo vazio) é de impressionantes 5 milhões. Uma pequena parte do código (200 mil linhas em arquivos 664 cs) cai nos testes, eu não os considerei.
Se a qualidade do código .NET da versão do SDK for aproximadamente a mesma que a do C ++ (dois erros por 512 KLOC), devemos obter cerca de 10 vezes mais erros. É claro que esse é um método de cálculo muito impreciso que não leva em consideração os recursos da linguagem e muitos outros fatores, mas não creio que o leitor agora queira se aprofundar em discussões chatas. Em vez disso, proponho ir direto aos resultados.
A verificação foi realizada usando o PVS-Studio versão 6.27. Incrivelmente, o analisador conseguiu detectar cerca de 40 erros no código do AWS SDK para .NET que mereciam ser mencionados. Isso fala não apenas da alta qualidade do código SDK (cerca de 4 erros por 512 KLOC), mas também da qualidade comparável do trabalho em C # do analisador PVS-Studio em comparação com o C ++. Ótimo resultado!
Os autores do AWS SDK for .NET são ótimos. De projeto para projeto, eles demonstram uma incrível qualidade de código. Isso pode servir como um bom exemplo para outras equipes. Mas, é claro, eu não seria o desenvolvedor de um analisador estático se não tivesse inserido meus 5 copeques. :) Já estamos trabalhando com a equipe do Amazon Lumberyard no uso do PVS-Studio. Mas como essa é uma empresa muito grande com várias divisões em todo o mundo, é altamente provável que a equipe do AWS SDK for .NET nunca tenha ouvido falar do PVS-Studio. De qualquer forma, no código SDK não encontrei sinais de uso do analisador, embora isso não signifique nada. No entanto, a equipe, no mínimo,
usa o analisador incorporado no Visual Studio. Isso é bom, mas as verificações de código podem ser
aprimoradas :).
Como resultado, ainda consegui encontrar alguns erros no código SDK e, finalmente, é hora de compartilhá-los.
Erro na lógicaPVS-Studio Warning: V3008 [CWE-563] A variável 'this.linker.s3.region' recebe valores duas vezes sucessivos. Talvez isso seja um erro. Verifique as linhas: 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; } }
O analisador avisa sobre a reatribuição do valor da mesma variável. A partir do código, fica claro que isso se deve a um erro que viola a lógica da operação: o valor da variável
this.linker.s3.region sempre será igual ao
valor , independentemente da condição
if (String.IsNullOrEmpty (value)) . No corpo do bloco
if , o
retorno foi ignorado. O código precisa ser corrigido da seguinte maneira:
public string Region { get { .... } set { if (String.IsNullOrEmpty(value)) { this.linker.s3.region = "us-east-1"; return; } this.linker.s3.region = value; } }
Recursão infinitaAviso do PVS-Studio: V3110 [CWE-674] Possível recursão infinita dentro da propriedade 'OnFailure'. AWSSDK.ElasticMapReduce.Net45 ResizeJobFlowStep.cs 171
OnFailure? onFailure = null; public OnFailure? OnFailure { get { return this.OnFailure; }
Um exemplo clássico de erro de digitação que causa recursão infinita no
acessador get da propriedade
OnFailure . Em vez de retornar o valor do campo privado,
onFailure refere-se à propriedade
OnFailure . Opção corrigida:
public OnFailure? OnFailure { get { return this.onFailure; } set { this.onFailure = value; } }
Você pergunta: "Como funcionou?" Até agora, de jeito nenhum. A propriedade não é usada em nenhum lugar, mas é temporária. A certa altura, alguém começará a usá-lo e obterá definitivamente um resultado inesperado. Para evitar erros de digitação, é recomendável não usar identificadores que diferem apenas no caso da primeira letra.
Outra observação nesse design é o uso de um identificador que corresponde completamente ao nome do tipo
OnFailure . Do ponto de vista do compilador, isso é bastante aceitável, mas dificulta a percepção humana do código.
Outro erro semelhante:
Aviso do PVS-Studio: V3110 [CWE-674] Possível recursão infinita dentro da propriedade 'SSES3'. AWSSDK.S3.Net45 InventoryEncryption.cs 37
private SSES3 sSES3; public SSES3 SSES3 { get { return this.SSES3; } set { this.SSES3 = value; } }
A situação é idêntica à descrita acima. Somente aqui, uma recursão infinita ocorrerá ao acessar a propriedade
SSES3 para leitura e gravação. Opção corrigida:
public SSES3 SSES3 { get { return this.sSES3; } set { this.sSES3 = value; } }
Teste de atenção plenaA seguir, uma tarefa de um programador interessado em usar a técnica Copiar e Colar. Veja como o código fica no editor do Visual Studio e tente encontrar o erro.

PVS-Studio Warning: V3029 As expressões condicionais das instruções 'if' localizadas uma ao lado da outra são idênticas. Verifique as linhas: 91, 95. AWSSDK.AppSync.Net45 CreateApiKeyResponseUnmarshaller.cs 91
Reduzi o corpo do método
UnmarshallException , removendo todos os desnecessários. Agora você pode ver que verificações idênticas seguem uma após a outra:
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); } .... }
Pode parecer que o erro não é grave - apenas uma verificação extra. Porém, frequentemente, esse padrão pode indicar problemas mais sérios no código quando alguma verificação necessária não é executada.
Existem alguns erros mais semelhantes no código.
Avisos do PVS-Studio:- V3029 As expressões condicionais das instruções 'if' localizadas uma ao lado da outra são idênticas. Verifique as linhas: 75, 79. AWSSDK.CloudDirectory.Net45 CreateSchemaResponseUnmarshaller.cs 75
- V3029 As expressões condicionais das instruções 'if' localizadas uma ao lado da outra são idênticas. Verifique as linhas: 105, 109. AWSSDK.CloudDirectory.Net45 GetSchemaAsJsonResponseUnmarshaller.cs 105
- V3029 As expressões condicionais das instruções 'if' localizadas uma ao lado da outra são idênticas. Verifique as linhas: 201, 205. AWSSDK.CodeCommit.Net45 PostCommentForPullRequestResponseUnmarshaller.cs 201
- V3029 As expressões condicionais das instruções 'if' localizadas uma ao lado da outra são idênticas. Verifique as linhas: 101, 105. AWSSDK.CognitoIdentityProvider.Net45 VerifySoftwareTokenResponseUnmarshaller.cs 101
- V3029 As expressões condicionais das instruções 'if' localizadas uma ao lado da outra são idênticas. Verifique as linhas: 72, 76. AWSSDK.Glue.Net45 UpdateConnectionResponseUnmarshaller.cs 72
- V3029 As expressões condicionais das instruções 'if' localizadas uma ao lado da outra são idênticas. Verifique as linhas: 123, 127. AWSSDK.Neptune.Net45 RestoreDBClusterFromSnapshotResponseUnmarshaller.cs 123
- V3029 As expressões condicionais das instruções 'if' localizadas uma ao lado da outra são idênticas. Verifique as linhas: 167, 171. AWSSDK.Neptune.Net45 RestoreDBClusterFromSnapshotResponseUnmarshaller.cs 167
- V3029 As expressões condicionais das instruções 'if' localizadas uma ao lado da outra são idênticas. Verifique as linhas: 127, 131. AWSSDK.RDS.Net45 RestoreDBClusterFromSnapshotResponseUnmarshaller.cs 127
- V3029 As expressões condicionais das instruções 'if' localizadas uma ao lado da outra são idênticas. Verifique as linhas: 171, 175. AWSSDK.RDS.Net45 RestoreDBClusterFromSnapshotResponseUnmarshaller.cs 171
- V3029 As expressões condicionais das instruções 'if' localizadas uma ao lado da outra são idênticas. Verifique as linhas: 99, 103. AWSSDK.Rekognition.Net45 RecognizeCelebritiesResponseUnmarshaller.cs 99
O que você éPVS-Studio Warning: V3062 Um objeto 'attributeName' é usado como argumento para seu próprio método. Considere verificar o primeiro argumento real do método 'Contém'. AWSSDK.MobileAnalytics.Net45 CustomEvent.cs 261
O analisador detectou um erro no método
GetAttribute : a sequência de caracteres é verificada quanto ao seu conteúdo. A partir da descrição do método, segue-se que, se o nome do atributo (chave
attributeName ) for encontrado (no dicionário
_attributes ), o valor do atributo deverá ser retornado, caso contrário
nulo . Na realidade, como a condição
attributeName.Contains (attributeName) é sempre verdadeira, é feita uma tentativa de retornar um valor por uma chave que pode não ser encontrada no dicionário. Em vez de retornar
nulo, uma KeyNotFoundException será lançada.
Vamos tentar corrigir esse código. Para entender melhor como fazer isso, dê uma olhada em outro método:
Este método verifica se o nome do atributo (chave
attributeName )
existe no dicionário
_attributes .
Vamos voltar ao método
GetAttribute e corrigir o erro:
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; }
Agora, o método faz exatamente o que é indicado na descrição.
E mais uma pequena observação para esse fragmento de código. Notei que os autores usam o
bloqueio ao trabalhar com o dicionário
_attributes . Claramente, isso é necessário com acesso multiencadeado, mas a construção de
bloqueio é bastante lenta e complicada. Em vez de
Dicionário , nesse caso, pode ser mais conveniente usar uma versão segura do encadeamento do dicionário -
ConcurrentDictionary . Então a necessidade de
bloqueio desaparece. Embora, talvez eu não conheça nenhum recurso do projeto.
Comportamento suspeitoAviso do PVS-Studio: V3063 [CWE-571] Uma parte da expressão condicional sempre é verdadeira se for avaliada: 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) &&
O analisador alertou a verificação
string.IsNullOrEmpty (inferredIndexName) . De fato, a cadeia
inferredIndexName é atribuída
nula , o valor dessa variável não é alterado em lugar algum e, por algum motivo, é verificado se há uma cadeia
nula ou vazia. Parece suspeito. Vamos dar uma olhada no trecho de código fornecido. Eu deliberadamente não o reduzi para uma melhor compreensão da situação. Portanto, na primeira
instrução if (e também na próxima), a variável
especificadaIndexName é verificada de alguma forma. Dependendo do resultado das verificações, a variável
inferredIndexName recebe um novo valor. Agora vamos prestar atenção à terceira
declaração if . O corpo dessa instrução (lançando uma exceção) será executado se
indexNames.Count> 0 , pois a primeira parte da condição é
string.IsNullOrEmpty (inferredIndexName) sempre é verdadeira. Talvez as variáveis
especificadasIndexName e
inferredIndexName estejam simplesmente confusas. Ou a terceira verificação deve ser sem
outra , representando uma
instrução if independente:
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(....);
Nesse caso, é difícil dar uma resposta inequívoca sobre as opções para corrigir esse código. Mas é definitivamente necessário que o autor a inspecione.
NullReferenceExceptionAviso do PVS-Studio: V3095 [CWE-476] O objeto 'conditionValues' foi usado antes de ser verificado com valor nulo. Verifique as linhas: 228, 238. AWSSDK.Core.Net45 JsonPolicyWriter.cs 228
private static void writeConditions(....) { .... foreach (....) { IList<string> conditionValues = keyEntry.Value; if (conditionValues.Count == 0)
Clássicos do gênero. A variável
conditionValues é usada sem primeiro verificar
nulo . Além disso, ainda mais no código, essa verificação é realizada, mas é tarde demais. :)
O código pode ser corrigido da seguinte maneira:
private static void writeConditions(....) { .... foreach (....) { IList<string> conditionValues = keyEntry.Value; if (conditionValues != null && conditionValues.Count == 0) continue; .... if (conditionValues != null && conditionValues.Count != 0) { .... } .... } }
Mais alguns erros semelhantes foram encontrados no código.
Avisos do PVS-Studio:- V3095 [CWE-476] O objeto 'ts.Listeners' foi usado antes de ser verificado como nulo. Verifique as linhas: 140, 143. AWSSDK.Core.Net45 Logger.Diagnostic.cs 140
- V3095 [CWE-476] O objeto 'obj' foi usado antes de ser verificado com relação a nulo. Verifique as linhas: 743, 745. AWSSDK.Core.Net45 JsonMapper.cs 743
- V3095 [CWE-476] O objeto 'multipartUploadMultipartUploadpartsList' foi usado antes de ser verificado como nulo. Verifique as linhas: 65, 67. AWSSDK.S3.Net45 CompleteMultipartUploadRequestMarshaller.cs 65
O aviso a seguir tem significado muito semelhante, mas a situação é o inverso do discutido acima.
Aviso do PVS-Studio: V3125 [CWE-476] O objeto 'state' foi usado após a verificação contra nulo. Verifique as linhas: 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;
Um dos fragmentos de código contém uma verificação da variável de
estado para
nulo . Além do código, a variável
state é usada para cancelar a inscrição no evento
PreemptExpiryTime , enquanto a verificação de
nulo não
é mais realizada e uma
NullReferenceException é possível . Opção de código mais seguro:
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; .... }
Existem outros erros semelhantes no código.
Avisos do PVS-Studio:- V3125 [CWE-476] O objeto 'wrapRequest.Content' foi usado após a verificação contra nulo. Verifique as linhas: 395, 383. AWSSDK.Core.Net45 HttpHandler.cs 395
- V3125 [CWE-476] O objeto 'datasetUpdates' foi usado após a verificação contra nulo. Verifique as linhas: 477, 437. Conjunto de dados AWSSDK.CognitoSync.Net45.cs 477
- V3125 [CWE-476] O objeto 'cORSConfigurationCORSConfigurationcORSRulesListValue' foi usado depois que foi verificado como nulo. Verifique as linhas: 125, 111. AWSSDK.S3.Net45 PutCORSConfigurationRequestMarshaller.cs 125
- V3125 [CWE-476] O objeto 'lifecycleConfigurationLifecycleConfigurationrulesListValue' foi usado depois que foi verificado como nulo. Verifique as linhas: 157, 68. AWSSDK.S3.Net45 PutLifecycleConfigurationRequestMarshaller.cs 157
- V3125 [CWE-476] O objeto 'this.Key' foi usado após a verificação contra nulo. Verifique as linhas: 199, 183. AWSSDK.S3.Net45 S3PostUploadRequest.cs 199
Realidade incontestadaAviso do PVS-Studio: V3009 [CWE-393] É estranho que esse método sempre retorne um e o mesmo valor de 'true'. AWSSDK.Core.Net45 Lexer.cs 651
private static bool State19 (....) { while (....) { switch (....) { case '"': .... return true; case '\\': .... return true; default: .... continue; } } return true; }
O método sempre retorna
verdadeiro . Vamos ver o quão crítico isso é para o código de chamada.
Acompanhei o uso do método
State19 . Ele participa do preenchimento da matriz de manipuladores
fsm_handler_table junto com outros métodos semelhantes (há 28 deles com nomes, respectivamente, de
State1 para
State28 ). É importante observar aqui que, além do
State19 , também foram emitidos avisos
V3009 [CWE-393] para alguns outros manipuladores. Estes são os manipuladores:
State23, State26, State27, State28 . Alertas emitidos pelo analisador para eles:
- V3009 [CWE-393] É estranho que esse método sempre retorne um e o mesmo valor de 'true'. AWSSDK.Core.Net45 Lexer.cs 752
- V3009 [CWE-393] É estranho que esse método sempre retorne um e o mesmo valor de 'true'. AWSSDK.Core.Net45 Lexer.cs 810
- V3009 [CWE-393] É estranho que esse método sempre retorne um e o mesmo valor de 'true'. AWSSDK.Core.Net45 Lexer.cs 822
- V3009 [CWE-393] É estranho que esse método sempre retorne um e o mesmo valor de 'true'. AWSSDK.Core.Net45 Lexer.cs 834
Aqui está a aparência da declaração e inicialização da matriz manipuladora:
private static StateHandler[] fsm_handler_table; .... private static void PopulateFsmTables () { fsm_handler_table = new StateHandler[28] { State1, State2, .... State19, .... State23, .... State26, State27, State28 };
Para garantir a integridade, vamos ver o código de um dos manipuladores, dos quais o analisador não teve queixas, por exemplo,
State2 :
private static bool State2 (....) { .... if (....) { return true; } switch (....) { .... default: return false; } }
E é assim que os manipuladores são chamados:
public bool NextToken () { .... while (true) { handler = fsm_handler_table[state - 1]; if (! handler (fsm_context))
Como você pode ver, uma exceção será lançada se
false for retornado. No nosso caso, para os manipuladores
State19, State23, State26, State27 e
State28 isso nunca acontecerá. Parece suspeito. Por outro lado, até cinco manipuladores têm comportamento semelhante (sempre retornam
verdadeiros ); talvez isso tenha sido planejado e não seja o resultado de um erro de digitação.
Por que insisti em tudo isso com tanto detalhe? Essa situação é muito indicativa no sentido de que um analisador estático geralmente é capaz de indicar apenas um projeto suspeito. E mesmo uma pessoa (não uma máquina) que não possui conhecimento suficiente sobre o projeto, tendo passado algum tempo estudando o código, ainda não é capaz de dar uma resposta exaustiva sobre a presença de um erro. O código deve ser estudado pelo desenvolvedor.
Verificações sem sentidoAviso do PVS-Studio: V3022 [CWE-571] A expressão 'doLog' sempre é verdadeira. 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)
Preste atenção à variável
doLog . Após a inicialização com
false , no código essa variável será
verdadeira em todos os casos. Portanto, a verificação
if (doLog) é sempre verdadeira. Talvez houvesse uma ramificação anteriormente neste método na qual a variável
doLog não
recebesse nenhum valor e, no momento da verificação, ela poderia conter o valor
falso obtido durante a inicialização. Mas agora não existe tal ramo.
Outro erro semelhante:
Aviso do PVS-Studio: A expressão
V3022 '! Result' é sempre falsa. 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(....); } .... }
O analisador afirma que o valor da variável de
resultado é sempre
verdadeiro . Isso só é possível se o método
PutValueHelper sempre retornar
true . Dê uma olhada neste método:
private bool PutValueHelper(....) { .... if (....)) { return true; } if (record == null) { .... return true; } else { .... return true; } }
De fato, o método retornará
verdadeiro em qualquer condição. Além disso, o analisador emitiu um aviso para esse método.
Aviso do PVS-Studio: V3009 [CWE-393] É estranho que esse método sempre retorne um e o mesmo valor de 'true'. SQLiteLocalStorage.cs 1016
Eu deliberadamente não citei esse aviso anteriormente quando estava considerando outros erros do
V3009 , mas o salvei neste caso. Assim, o analisador estava certo ao indicar o erro
V3022 no código de chamada.
Copie e cole. NovamentePVS-Studio Warning: V3001 Existem sub-expressões idênticas 'this.token == JsonToken.String' à esquerda e à direita da '||' operador. AWSSDK.Core.Net45 JsonReader.cs 343
public bool Read() { .... if ( (this.token == JsonToken.ObjectEnd || this.token == JsonToken.ArrayEnd || this.token == JsonToken.String ||
Compare duas vezes o campo
this.token com o valor
JsonToken.String da enumeração
JsonToken . Provavelmente, uma das comparações deve conter um valor de enumeração diferente. Nesse caso, um erro grave foi cometido.
Refatoração + descuido?Aviso do PVS-Studio: V3025 [CWE-685] Formato incorreto. É esperado um número diferente de itens de formato ao chamar a função 'Format'. Argumentos não usados: 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)); } .... }
Provavelmente, a string de formato para o método
string.Format anteriormente continha o especificador de saída
{0} , para o qual o argumento
AWSConfigs.AWSRegionKey foi definido. Então a linha foi alterada, o especificador desapareceu, mas eles esqueceram de excluir o argumento. O fragmento de código acima funciona sem erros (uma exceção seria lançada no caso oposto - um especificador sem argumento), mas parece feio. O código deve ser corrigido da seguinte maneira:
if (region == null) { throw new InvalidOperationException( "EC2 instance metadata was not available or did not contain region information."); }
InseguroAviso do PVS-Studio: V3083 [CWE-367] Chamada não segura do evento 'mOnSyncSuccess', NullReferenceException é possível. Considere atribuir um evento a uma variável local antes de invocá-lo. AWSSDK.CognitoSync.PCL Dataset.cs 827
protected void FireSyncSuccessEvent(List<Record> records) { if (mOnSyncSuccess != null) { mOnSyncSuccess(this, new SyncSuccessEventArgs(records)); } }
Uma situação bastante comum de uma chamada não segura para o manipulador de eventos. Entre a verificação da variável
mOnSyncSuccess para
null e a chamada do manipulador, um evento pode ser cancelado e seu valor se tornará zero. A probabilidade de tal cenário é pequena, mas ainda é melhor tornar o código mais seguro:
protected void FireSyncSuccessEvent(List<Record> records) { mOnSyncSuccess?.Invoke(this, new SyncSuccessEventArgs(records)); }
Existem outros erros semelhantes no código.
Avisos do PVS-Studio:- V3083 [CWE-367] Chamada não segura do evento 'mOnSyncFailure', NullReferenceException é possível. Considere atribuir um evento a uma variável local antes de invocá-lo. AWSSDK.CognitoSync.PCL Dataset.cs 839
- V3083 [CWE-367] Chamada insegura do evento, NullReferenceException é possível. Considere atribuir um evento a uma variável local antes de invocá-lo. AWSSDK.Core.PCL AmazonServiceClient.cs 332
- V3083 [CWE-367] Chamada insegura do evento, NullReferenceException é possível. Considere atribuir um evento a uma variável local antes de invocá-lo. AWSSDK.Core.PCL AmazonServiceClient.cs 344
- V3083 [CWE-367] Chamada insegura do evento, NullReferenceException é possível. Considere atribuir um evento a uma variável local antes de invocá-lo. AWSSDK.Core.PCL AmazonServiceClient.cs 357
- V3083 [CWE-367] Chamada não segura do evento 'mExceptionEvent', NullReferenceException é possível. Considere atribuir um evento a uma variável local antes de invocá-lo. AWSSDK.Core.PCL AmazonServiceClient.cs 366
- V3083 [CWE-367] Chamada insegura do evento, NullReferenceException é possível. Considere atribuir um evento a uma variável local antes de invocá-lo. AWSSDK.Core.PCL AmazonWebServiceRequest.cs 78
- V3083 [CWE-367] Chamada não segura do evento 'OnRead', NullReferenceException é possível. Considere atribuir um evento a uma variável local antes de invocá-lo. AWSSDK.Core.PCL EventStream.cs 97
- V3083 [CWE-367] Chamada insegura do evento, NullReferenceException é possível. Considere atribuir um evento a uma variável local antes de invocá-lo. AWSSDK.Core.Android NetworkReachability.cs 57
- V3083 [CWE-367] Chamada insegura do evento, NullReferenceException é possível. Considere atribuir um evento a uma variável local antes de invocá-lo. AWSSDK.Core.Android NetworkReachability.cs 94
- V3083 [CWE-367] Chamada insegura do evento, NullReferenceException é possível. Considere atribuir um evento a uma variável local antes de invocá-lo. AWSSDK.Core.iOS NetworkReachability.cs 54
Classe inacabadaAviso do PVS-Studio: V3126 O tipo 'JsonData' implementando a interface IEquatable <T> não substitui o método 'GetHashCode'. AWSSDK.Core.Net45 JsonData.cs 26
public class JsonData : IJsonWrapper, IEquatable<JsonData> { .... }
A classe
JsonData contém muito código, então eu não o
forneci por completo, limitando-me apenas à declaração. Essa classe realmente não contém um método
GetHashCode substituído, o que não é seguro, pois pode levar a um comportamento incorreto ao usar o tipo
JsonData para trabalhar, por exemplo, com coleções. Provavelmente não há nenhum problema no momento, mas a estratégia para usar esse tipo pode mudar no futuro. Este erro é descrito em mais detalhes na
documentação .
ConclusãoForam todos os erros interessantes que consegui detectar no código do AWS SDK for .NET usando o analisador estático PVS-Studio. Mais uma vez, enfatizo a qualidade do projeto. Encontrei um número muito pequeno de erros para 5 milhões de linhas de código. Embora, provavelmente, uma análise mais completa dos avisos emitidos me permita adicionar mais alguns erros a esta lista. Mas também é muito provável que, em vão, eu tenha atribuído alguns dos avisos descritos a erros. Nesse caso, conclusões inequívocas são sempre feitas apenas pelo desenvolvedor, que está no contexto do código que está sendo verificado.
Se você deseja compartilhar este artigo com um público que fala inglês, use o link para a tradução: Sergey Khrenov. Procurando por erros no código-fonte do Amazon Web Services SDK para .NET