A história de como o PVS-Studio encontrou um erro na biblioteca usada em ... PVS-Studio

Quadro 1

Esta é uma pequena história sobre como usar o PVS-Studio foi possível encontrar um erro no código fonte da biblioteca usada no PVS-Studio. Além disso, não teórico, mas real - o erro foi manifestado na prática ao usar a biblioteca no analisador.

No PVS-Studio_Cmd (assim como em alguns outros utilitários), usamos uma biblioteca especial para analisar argumentos de linha de comando - CommandLine.

Hoje eu estava envolvido no suporte ao novo modo no PVS-Studio_Cmd, e aconteceu que eu tive que usar essa biblioteca de argumentos de análise. No processo de escrever código, também estou depurando, pois tenho que trabalhar com APIs desconhecidas.

Então, o código é escrito, compilado, executado para execução, ii ...

Quadro 3


A execução do código passa para a biblioteca onde ocorre uma exceção do tipo NullReferenceException . Por um lado, não está muito claro - não passo nenhuma referência nula explícita ao método.

Apenas no caso, eu olho para os comentários sobre o método chamado. É muito improvável que eles descrevam as condições para uma exceção do tipo NullReferenceException (já que geralmente, como me parece, exceções desse tipo são imprevisíveis).

Quadro 2


Nos comentários do método, não há informações sobre nenhuma NullReferenceException (que, no entanto, é esperada).

Para ver o que exatamente causa a exceção (e onde), decidi baixar o código-fonte do projeto, compilar e conectar a versão de depuração da biblioteca ao analisador. O código fonte do projeto está disponível no GitHub . A versão 1.9.71 é necessária, pois é precisamente esse tipo que agora é usado no analisador.

Carrego a versão apropriada do código-fonte, coleciono, conecto a biblioteca de depuração ao analisador, executo o código para execução e consulte:

Quadro 4


Portanto, o local da exceção é claro - helpInfo é nulo , o que causa uma exceção do tipo NullReferenceException ao acessar a propriedade da instância Left .

E então fiquei pensativo. Recentemente, o PVS-Studio for C # foi bem aprimorado em várias áreas, inclusive no campo de pesquisa para desreferenciamento de referências potencialmente nulas. Em particular, a análise interprocedural foi aprimorada. Portanto, tornou-se imediatamente interessante verificar o código-fonte para ver se o PVS-Studio poderia encontrar o erro em discussão.

Eu verifiquei o código fonte e, entre outros avisos, vi exatamente o que estava esperando.

PVS-Studio Warning : V3080 Possível desreferência nula dentro do método em 'helpInfo.Left'. Considere inspecionar o segundo argumento: helpInfo. Parser.cs 405

Sim está aí! Exatamente o que você precisa. Vamos dar uma olhada no código fonte com mais detalhes.

private bool DoParseArgumentsVerbs( string[] args, object options, ref object verbInstance) { var verbs = ReflectionHelper.RetrievePropertyList<VerbOptionAttribute>(options); var helpInfo = ReflectionHelper.RetrieveMethod<HelpVerbOptionAttribute>(options); if (args.Length == 0) { if (helpInfo != null || _settings.HelpWriter != null) { DisplayHelpVerbText(options, helpInfo, null); // <= } return false; } .... } 

O analisador gera uma mensagem de aviso ao chamar o método DisplayHelpVerbText e avisa sobre o segundo argumento - helpInfo . Observe que esse método está na ramificação then da instrução if . A expressão condicional é composta de tal maneira que o ramo pode ser executado com os seguintes valores de variáveis:

  • helpInfo == null ;
  • _settings.HelpWriter! = null ;

Vamos ver o corpo do método DisplayHelpVerbText :

 private void DisplayHelpVerbText( object options, Pair<MethodInfo, HelpVerbOptionAttribute> helpInfo, string verb) { string helpText; if (verb == null) { HelpVerbOptionAttribute.InvokeMethod(options, helpInfo, null, out helpText); } else { HelpVerbOptionAttribute.InvokeMethod(options, helpInfo, verb, out helpText); } if (_settings.HelpWriter != null) { _settings.HelpWriter.Write(helpText); } } 

Como o verbo == null (veja a chamada do método), estamos interessados ​​no ramo então da instrução if . Embora a situação com o ramo else seja semelhante, consideraremos o ramo então , uma vez que em nosso caso particular a execução foi superada. Lembre-se de que helpInfo pode ser nulo .

Agora, vamos examinar o corpo do método HelpVerbOptionAttribute.InvokeMethod . Na verdade, você já viu na captura de tela acima:

 internal static void InvokeMethod( object target, Pair<MethodInfo, HelpVerbOptionAttribute> helpInfo, string verb, out string text) { text = null; var method = helpInfo.Left; if (!CheckMethodSignature(method)) { throw new MemberAccessException( SR.MemberAccessException_BadSignatureForHelpVerbOptionAttribute .FormatInvariant(method.Name)); } text = (string)method.Invoke(target, new object[] { verb }); } 

helpInfo.Left é chamado incondicionalmente, mesmo que helpInfo possa ser nulo . O analisador alertou sobre isso, e isso aconteceu.

Conclusão

Ficou engraçado que, com a ajuda do PVS-Studio, foi possível encontrar um erro no código da biblioteca usada no PVS-Studio. Penso que este é um tipo de continuação da resposta à pergunta “O PVS-Studio encontra erros no código do PVS-Studio?”. :) Pode encontrar erros não apenas no código do PVS-Studio, mas também no código das bibliotecas utilizadas.

Por fim, proponho fazer o download do analisador e tentar verificar seu projeto - e se você também encontrar algo interessante lá?



Se você deseja compartilhar este artigo com um público que fala inglês, use o link para a tradução: Sergey Vasiliev. A história de como o PVS-Studio encontrou um erro na biblioteca usada em ... PVS-Studio

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


All Articles