关于PVS-Studio如何在用于... PVS-Studio的库中发现错误的故事

Picture 1

This is a short story about how PVS-Studio helped us find an error in the source code of the library used in PVS-Studio. And it was not a theoretical error but an actual one — the error appeared in practice when using the library in the analyzer.

这是关于PVS-Studio如何帮助我们在PVS-Studio中使用的库的源代码中发现错误的简短故事。 这不是理论错误,而是实际错误–在分析仪中使用该库时,实际上会出现该错误。

In PVS-Studio_Cmd (as well as some other utilities) we use a special library for parsing command line arguments — CommandLine.

在PVS-Studio_Cmd(以及其他一些实用程序)中,我们使用一个特殊的库来解析命令行参数-CommandLine。

Today I supported the new mode in PVS-Studio_Cmd and it so happened that I had to use this library for parsing command line arguments. While writing the code, I also debug it because I have to work with unfamiliar APIs.

今天,我在PVS-Studio_Cmd中支持新模式,碰巧我不得不使用该库来解析命令行参数。 在编写代码时,我还调试了它,因为我必须使用不熟悉的API。

So, the code is written, compiled, executed and...

因此,代码被编写,编译,执行并...

Picture 3

Code execution goes inside the library where an exception of the NullReferenceException type occurs. It's not so clear from the side — I don't pass any null references into the method.

代码执行在发生NullReferenceException类型的异常的库中进行。 从侧面看还不太清楚-我没有将任何null引用传递给该方法。

To be sure, I look at comments to the callee method. It is hardly probable that they describe the conditions of occurrence of an exception of the NullReferenceException type (as it seems to me usually exceptions of this type are not provided for).

可以肯定的是,我查看对被调用方方法的注释。 它们几乎不可能描述NullReferenceException类型的异常的发生条件(在我看来,通常不提供这种类型的异常)。

Picture 2

There is no information about NullReferenceException in the comments to the method (which, however, is expected).

在方法的注释中没有有关NullReferenceException的信息(但是,这是预期的)。

To see what exactly causes the exception (and where it occurs), I decided to download the project's source code, build it and add a reference to the debug version of the library to the analyzer. The source code of the project is available at GitHub. We need the version 1.9.71 of the library. It is the one used in the analyzer now.

为了查看导致异常的确切原因(及其发生的位置),我决定下载该项目的源代码,进行构建,然后将对库的调试版本的引用添加到分析器。 该项目的源代码可从GitHub获得 。 我们需要该库的1.9.71版本。 现在是分析仪中使用的一种。

I download the corresponding version of the source code, build the library, add a reference to the debug library to the analyzer, execute the code and see:

我下载了相应版本的源代码,构建了库,将对调试库的引用添加到了分析器,执行了代码并看到:

Picture 4

So, the place where the exception occurs is clear — helpInfo has a null value, which causes an exception of the NullReferenceException type when accessing the Left property.

因此,发生异常的地方很清楚— helpInfo具有null值,当访问Left属性时,它将导致NullReferenceException类型的异常。

I started thinking about it. Recently, PVS-Studio for C# has been well improved in various aspects, including the search for dereferencing of potentially null references. In particular, the interprocedural analysis was improved in a number of ways. That's why I was immediately interested in checking the source code to understand if PVS-Studio could find the error under discussion.

我开始考虑它。 最近,用于C#的PVS-Studio在各个方面都得到了很好的改进,包括对可能为空的引用进行解引用的搜索。 特别是,过程间分析在许多方面得到了改进。 这就是为什么我立即对检查源代码感兴趣,以了解PVS-Studio是否可以找到正在讨论的错误。

I checked the source code and among other warnings I saw exactly what I hoped for.

我检查了源代码,并在其他警告中看到了我所希望的。

PVS-Studio警告 (PVS-Studio warning)

:

V3080 Possible null dereference inside method at 'helpInfo.Left'. Consider inspecting the 2nd argument: helpInfo. Parser.cs 405 V3080在'helpInfo.Left'的方法内部可能存在空取消引用。 考虑检查第二个参数:helpInfo。 解析器405

Yeah, this is it! That's exactly what we need. Let's take a more detailed look at the source code.

是的,就是这样! 这正是我们所需要的。 让我们更详细地看一下源代码。

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

The analyzer issues a warning for calling the DisplayHelpVerbText method and warns about the second argument — helpInfo. Pay attention that this method is located in the then-branch of the if statement. The conditional expression is composed in such a way that the then-branch can be executed at the next values of the variables:

分析器会发出警告,提示调用DisplayHelpVerbText方法并警告第二个参数helpInfo 。 请注意,此方法位于if语句的then -branch中。 条件表达式的编写方式使得可以在变量的下一个值处执行then -branch:

  • helpInfo == null;

    helpInfo == null ;

  • _settings.HelpWriter != null;

    _settings.HelpWriter!= null ;

Let's see the body of the DisplayHelpVerbText method:

让我们看一下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);
  }
}

Since verb == null (see method call) we are interested in then-branch of the if statement. Although the situation is similar with the else branch, let's consider then-branch because in our particular case the execution went through it. Remember that helpInfo may be null.

由于动词== null (请参见方法调用),因此我们对if语句的then -branch感兴趣。 尽管形势与else分支类似, 那么,让我们来考虑分枝因为在我们的特定情况下执行通过它去。 请记住, helpInfo可以为null

Now let's look at the body of the HelpVerbOptionAttribute.InvokeMethod method. Actually, you have already seen it on the screenshot above:

现在让我们看一下HelpVerbOptionAttribute的主体。 InvokeMethod方法。 实际上,您已经在上面的屏幕截图中看到了它:

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 is called unconditionally, while helpInfo.Left是无条件调用的,而 helpInfo may be helpInfo可以为 null. The analyzer warned about it, and that's what happened. null 。 分析仪对此进行了警告,事实就是这样。

结论 (Conclusion)

It is nice that we managed to find an error in the source code of the library used in PVS-Studio with the help of PVS-Studio. I think this is a kind of the answer to the question «Does PVS-Studio find errors in PVS-Studio source code?». :) The analyzer can find errors not only in PVS-Studio code but also in the code of the used libraries.

很高兴我们借助PVS-Studio设法在PVS-Studio中使用的库的源代码中发现错误。 我认为这是对“ PVS-Studio是否在PVS-Studio源代码中发现错误?”这个问题的答案。 :)分析仪不仅可以在PVS-Studio代码中发现错误,而且可以在所用库的代码中发现错误。

Finally, I suggest you download the analyzer and try to check your project — what if you can find something interesting there too?

最后,我建议您下载分析器并尝试检查您的项目-如果在那里也可以找到有趣的东西怎么办?

翻译自: https://habr.com/en/company/pvs-studio/blog/462947/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值