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 o PVS-Studio nos ajudou a encontrar um erro no código fonte da biblioteca usada no PVS-Studio. E não foi um erro teórico, mas um erro real - o erro apareceu 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 apoiei o novo modo no PVS-Studio_Cmd e aconteceu que eu tive que usar essa biblioteca para analisar argumentos de linha de comando. Ao escrever o código, eu também o depuro porque tenho que trabalhar com APIs desconhecidas.

Então, o código é escrito, compilado, executado e ...

Quadro 3

A execução do código entra na biblioteca em que ocorre uma exceção do tipo NullReferenceException . Não é tão claro do lado - não passo nenhuma referência nula no método.

Para ter certeza, eu olho para os comentários do método chamado. É pouco provável que eles descrevam as condições de ocorrência de uma exceção do tipo NullReferenceException (como me parece geralmente não há exceções desse tipo).

Quadro 2

Não há informações sobre NullReferenceException nos comentários do método (o que, no entanto, é esperado).

Para ver o que exatamente causa a exceção (e onde ela ocorre), decidi baixar o código-fonte do projeto, construí-lo e adicionar uma referência à versão de depuração da biblioteca no analisador. O código fonte do projeto está disponível no GitHub . Precisamos da versão 1.9.71 da biblioteca. É o usado no analisador agora.

Faço o download da versão correspondente do código fonte, construo a biblioteca, adiciono uma referência à biblioteca de depuração no analisador, executo o código e consulte:

Quadro 4

Portanto, o local onde a exceção ocorre é clara - helpInfo possui um valor nulo , o que causa uma exceção do tipo NullReferenceException ao acessar a propriedade Left .

Comecei a pensar nisso. Recentemente, o PVS-Studio for C # foi bem aprimorado em vários aspectos, incluindo a busca por desreferenciamento de referências potencialmente nulas. Em particular, a análise interprocedural foi aprimorada de várias maneiras. Por isso, fiquei imediatamente interessado em verificar o código fonte para entender 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 esperava.

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

Sim, é isso! É exatamente disso que precisamos. Vamos dar uma olhada mais detalhada no código fonte.

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 emite um aviso para chamar o método DisplayHelpVerbText e avisa sobre o segundo argumento - helpInfo . Preste atenção que este método está localizado na ramificação então da instrução if . A expressão condicional é composta de tal maneira que o ramo then pode ser executado nos próximos valores das 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 da instrução if . Embora a situação seja semelhante ao ramo else , vamos considerar o ramo porque, no nosso caso particular, a execução passou por ele. Lembre-se de que helpInfo pode ser nulo .

Agora vamos ver o corpo do HelpVerbOptionAttribute . Método 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, enquanto helpInfo pode ser nulo . O analisador alertou sobre isso, e foi o que aconteceu.

Conclusão

É bom que tenhamos encontrado um erro no código fonte da biblioteca usada no PVS-Studio com a ajuda do PVS-Studio. Acho que esse é um tipo de resposta para a pergunta "O PVS-Studio encontra erros no código-fonte do PVS-Studio?". :) O analisador pode encontrar erros não apenas no código do PVS-Studio, mas também no código das bibliotecas usadas.

Por fim, sugiro que você baixe o analisador e tente verificar seu projeto - e se você também encontrar algo interessante lá?

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


All Articles