﻿# История о том, как PVS\-Studio нашёл ошибку в библиотеке, используемой в\.\.\. PVS\-Studio

Это небольшая история о том, как с помощью PVS\-Studio удалось найти ошибку в исходном коде библиотеки, используемой в PVS\-Studio\. Причём не теоретическую, а фактическую \- ошибка проявлялась на практике при использовании библиотеки в анализаторе\.

![0654_CMDParserBug_ru/image1.png](https://import.viva64.com/docx/blog/0654_CMDParserBug_ru/image1.png)

В PVS\-Studio\_Cmd \(а также некоторых других утилитах\) мы используем специальную библиотеку для разбора аргументов командной строки \- CommandLine\.

Сегодня я занимался поддержкой нового режима в PVS\-Studio\_Cmd, и как раз так получилось, что пришлось использовать эту библиотеку разбора аргументов\. В процессе написания кода также отлаживаю его, так как приходится работать с незнакомыми API\.

Итак, код написан, компилируется, запускаю на исполнение, иии\.\.\.

![0654_CMDParserBug_ru/image2.png](https://import.viva64.com/docx/blog/0654_CMDParserBug_ru/image2.png)

Исполнение кода переходит внутрь библиотеки, где возникает исключение типа _NullReferenceException_\. Со стороны не очень понятно \- явных никаких нулевых ссылок в метод я не передаю\.

На всякий случай смотрю комментарии к вызываемому методу\. Очень маловероятно, что в них описаны условия возникновения исключения типа _NullReferenceException_ \(так как обычно, как мне кажется, исключения этого типа \- непредусмотренные\)\.

![0654_CMDParserBug_ru/image3.png](https://import.viva64.com/docx/blog/0654_CMDParserBug_ru/image3.png)

В комментариях к методу информации ни о каком _NullReferenceException_ нет \(что, впрочем, ожидаемо\)\.

Чтобы посмотреть, из\-за чего конкретно возникает исключение \(и где\), решил загрузить исходный код проекта, собрать и подключить к анализатору отладочную версию библиотеки\. Исходный код проекта [доступен на GitHub](https://github.com/commandlineparser/commandline)\. Необходима версия 1\.9\.71, так как именно такая сейчас используется в анализаторе\.

Загружаю соответствующую версию исходного кода, собираю, подключаю отладочную библиотеку к анализатору, запускаю код на исполнение и вижу:

![0654_CMDParserBug_ru/image4.png](https://import.viva64.com/docx/blog/0654_CMDParserBug_ru/image4.png)

Итак, место возникновения исключения понятно \- _helpInfo_ имеет значение _null_, из\-за чего возникает исключение типа _NullReferenceException_ при обращении к экземплярному свойству _Left_\.

И тут я призадумался\. В последнее время PVS\-Studio для C\# был неплохо улучшен в различных областях, в том числе \- в области поиска разыменования потенциально нулевых ссылок\. В частности \- был порядком улучшен межпроцедурный анализ\. Поэтому мне сразу же стало интересно проверить исходный код, чтобы понять, сможет ли PVS\-Studio найти обсуждаемую ошибку\.

Я проверил исходный код, и среди прочих предупреждений увидел именно то, на которое надеялся\.

**Предупреждение PVS\-Studio**: [V3080](https://pvs-studio.ru/ru/docs/warnings/v3080/) Possible null dereference inside method at 'helpInfo\.Left'\. Consider inspecting the 2nd argument: helpInfo\. Parser\.cs 405

Да, вот оно\! Именно то, что нужно\. Посмотрим на исходный код чуть детальнее\.

```cpp
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;
  }
  ....
}
```

Анализатор выдаёт предупреждение на вызов метода _DisplayHelpVerbText_ и предупреждает о втором аргументе \- _helpInfo_\. Обратите внимание, что этот метод находится в _then_\-ветви оператора _if_\. Условное выражение составлено таким образом, что _then_\-ветвь может быть исполнена при следующих значениях переменных:

* _helpInfo \=\= null_;
* _\_settings\.HelpWriter \!\= null_;

Посмотрим тело метода _DisplayHelpVerbText_:

```cpp
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);
  }
}
```

Так как _verb \=\= null_ \(см\. вызов метода\) нас интересует _then_\-ветвь оператора _if_\. Хотя с _else_ ветвью ситуация будет аналогична, рассматривать будем _then_\-ветвь, так как в нашем частном случае именно через неё шло исполнение\. Помним, что _helpInfo_ может иметь значение _null_\.

Теперь посмотрим на тело метода _HelpVerbOptionAttribute\.InvokeMethod_\. Собственно, его вы уже видели на скриншоте выше:

```cpp
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_ вызывается безусловно, при том, что _helpInfo_ может иметь значение _null_\. Об этом предупреждал анализатор, это и произошло\.

**Заключение**

Забавно получилось, что с помощью PVS\-Studio удалось найти ошибку в коде библиотеки, которая используется в PVS\-Studio\. Я думаю, это своего рода продолжение ответа на вопрос "Находит ли PVS\-Studio ошибки в коде PVS\-Studio?"\. :\) Может находить ошибки не только в коде PVS\-Studio, но и в коде используемых библиотек\.

Напоследок предложу [скачать анализатор](https://pvs-studio.ru/ru/pvs-studio/download/) и попробовать проверить свой проект \- вдруг и там удастся найти что\-нибудь интересное?