微软的sdk以及azure_.NET的Azure SDK:关于困难错误搜索的故事

微软的sdk以及azure

Picture 2

When we decided to search for errors in the Azure SDK for .NET project, we were pleasantly surprised by its size. «Three and a half million lines of code,» we kept saying, studying the project's statistics. There might be so many findings. Alas and alack! The project turned out to be crafty. So what was the zest of the project and how it was checked — read in this article.

当我们决定在.NET项目的Azure SDK中搜索错误时,我们对它的大小感到惊讶。 我们一直说:“三百五十万行代码”,用于研究项目的统计数据。 可能有很多发现。 las! 该项目原来是狡猾的。 那么,什么是项目的热情以及如何对其进行检查-请阅读本文。

关于该项目 (About the project)

I'm writing this article following up my previous one, which was also about a project related to Microsoft Azure: Azure PowerShell: Mostly Harmless. So, this time I was betting on a solid number of diverse and interesting errors. After all, project size is a very important factor in terms of static analysis, in particular when checking a project for the first time. In fact, in practice, one-time checks application isn't the right approach. Nevertheless, if developers go for it, it only takes place at the stage of analyzer introduction. At the same time, no one works their butt off sorting out the enormous number of warnings and just dally them off as technical debt using mass warning suppressing mechanisms and storing them in special bases. Speaking of which, having a great number of warnings is fine when running the analyzer for the first time. As for us, we go for one-time checks for research purposes. For this reason, large projects are always more preferable for the following analysis as compared with small ones.

我写这篇文章是继上一篇文章之后,也是关于与Microsoft Azure相关的项目的: Azure PowerShell:几乎无害 。 因此,这次我押注大量多样且有趣的错误。 毕竟,就静态分析而言,项目规模是一个非常重要的因素,尤其是在首次检查项目时。 实际上,在实践中,一次性检查应用程序不是正确的方法。 但是,如果开发人员坚持使用它,则只能在分析仪引入阶段进行。 同时,没有人努力解决大量警告,而只是使用大规模警告抑制机制将它们作为技术债务清算,并将其存储在特殊的基础中。 可以说,第一次运行分析仪时,具有大量警告是可以的。 对于我们来说,出于研究目的,我们进行了一次检查。 因此,与小项目相比,大型项目总是更适合进行以下分析。

However, the Azure SDK for .NET project immediately proved to be an unviable test bed. Even its impressive size didn't help, but rather complicated working on it. The reason is given in the following project statistics:

但是,Azure SDK for .NET项目立即被证明是不可行的测试平台。 甚至其令人印象深刻的尺寸也无济于事,但处理起来却很复杂。 以下项目统计信息中给出了原因:

  • .cs source files (not including tests): 16 500

    .cs源文件(不包括测试):16 500
  • Visual Studio Solutions (.sln): 163

    Visual Studio解决方案(.sln):163
  • Non-empty lines of code: 3 462 000

    非空代码行:3 462 000
  • Of these auto-generated: about 3,300,000

    其中自动生成的:约3,300,000
  • The project repository is available on GitHub.

    项目存储库位于GitHub上

Approximately 95% of the code is generated automatically, and much of that code is repeated many times. Checking such projects with a static analyzer is usually time-consuming and useless, as there is a lot of workable, but illogical (at least at a first glance) and redundant code. This leads to a great number of false positives.

大约有95%的代码是自动生成的,并且许多代码会重复多次。 用静态分析器检查这样的项目通常是耗时且无用的,因为有很多可行的方法,但是不合逻辑(至少乍看之下)和冗余代码。 这导致大量误报。

All that amount of code scattered all over 163 Visual Studio solutions became the «cherry on top». It took some efforts to check the remaining code (not auto-generated). What really helped was the fact that all auto-generated code was stored in solutions subdirectories by the relative path "<Solution directory>\src\Generated". Also each .cs file of such type contains a special comment in the tag <auto-generated>:

分散在163种Visual Studio解决方案中的所有代码量成为了“顶头樱桃”。 它花了点力气检查剩余的代码(不是自动生成的)。 真正有用的是所有自动生成的代码都通过相对路径“ <解决方案目录> \ src \ Generated”存储在解决方案子目录中的事实。 同样,每个这种类型的.cs文件都在标记<自动生成>中包含特殊注释:

// <auto-generated>
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for
// license information.
//
// Code generated by Microsoft (R) AutoRest Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is
// regenerated.
// </auto-generated>

For the purity of the experiment, I patchily checked about ten randomly selected auto-generated solutions. I'll tell about the result later.

为了保证实验的纯度,我零星检查了大约十种随机选择的自动生成的溶液。 稍后再讲结果。

So, despite the small amount of remaining «honest» code, I still managed to find a number of errors from what remained. This time I'm not going to cite warnings in the order of PVS-Studio diagnostics' codes. Instead, I'll group the messages on the solutions in which they've been found.

因此,尽管剩下很少的“诚实”代码,但我仍然设法从剩余的代码中发现了许多错误。 这次我不会按照PVS-Studio诊断代码的顺序列出警告。 相反,我会将消息分组在找到它们的解决方案上。

Well, let's see what I managed to find in the Azure SDK for .NET code.

好吧,让我们看看我设法在.NET代码的Azure SDK中找到了什么。

Microsoft.Azure.Management.Advisor (Microsoft.Azure.Management.Advisor)

This is one of many solutions that contains auto-generated code. As I said earlier, I randomly checked about a dozen of such solutions. In each case, warnings were the same, and, as expected, useless. Here is a couple of examples.

这是包含自动生成的代码的许多解决方案之一。 如前所述,我随机检查了大约十二种此类解决方案。 在每种情况下,警告都是相同的,并且正如预期的那样是无用的。 这是几个例子。

V3022 Expression 'Credentials != null' is always true. AdvisorManagementClient.cs 204 V3022表达式'Credentials!= null'始终为true。 AdvisorManagementClient.cs 204
// Code generated by Microsoft (R) AutoRest Code Generator.
....
public ServiceClientCredentials Credentials { get; private set; }
....
public AdvisorManagementClient(ServiceClientCredentials credentials,
  params DelegatingHandler[] handlers) : this(handlers)
{
  if (credentials == null)
  {
    throw new System.ArgumentNullException("credentials");
  }
  Credentials = credentials;
  if (Credentials != null)    // <=
  {
    Credentials.InitializeServiceClient(this);
  }
}

Obviously, this code is redundant and the Credentials != null check is pointless. Nevertheless, the code works. And is auto-generated. For this reason, no complaints here.

显然,此代码是多余的, 凭据!= null检查毫无意义。 尽管如此,代码仍然有效。 并自动生成。 因此,这里没有任何投诉。

V3022 Expression '_queryParameters.Count > 0' is always false. ConfigurationsOperations.cs 871 V3022表达式'_queryParameters.Count> 0'始终为false。 ConfigurationsOperations.cs 871
// Code generated by Microsoft (R) AutoRest Code Generator.
....
public async Task<AzureOperationResponse<IPage<ConfigData>>>
  ListBySubscriptionNextWithHttpMessagesAsync(....)
{
  ....
  List<string> _queryParameters = new List<string>();
  if (_queryParameters.Count > 0)
  {
    ....
  }
  ....
}

Again, it seems like an illogical construction. For some reason, code authors check the size of the newly created empty list. In fact, it's all correct. At this point, the check makes no sense, but in case if developers add list generation, for example, based on another collection, the check will definitely be worth-while. Again — no claims to the code, of course, with regards to its origin.

同样,这似乎是不合逻辑的。 由于某些原因,代码作者检查新创建的列表的大小。 实际上,这都是正确的。 在这一点上,检查没有任何意义,但是如果开发人员添加了列表生成功能(例如,基于另一个集合),则检查绝对值得。 再次重申-当然,对于代码的起源没有任何要求。

Hundreds of similar warnings have been issued for each auto-generated solution. Given their futility, I think there is no point in further discussing such cases. Next, only real errors in the «normal» code will be considered.

对于每个自动生成的解决方案,已经发出了数百个类似的警告。 考虑到它们是徒劳的,我认为没有必要进一步讨论这种情况。 接下来,将仅考虑“正常”代码中的实际错误。

Azure核心 (Azure.Core)

V3001 There are identical sub-expressions 'buffer.Length' to the left and to the right of the '<' operator. AzureBaseBuffersExtensions.cs 30 V3001在'<'运算符的左侧和右侧有相同的子表达式'buffer.Length'。 AzureBaseBuffersExtensions.cs 30
public static async Task WriteAsync(...., ReadOnlyMemory<byte> buffer, ....)
{
  byte[]? array = null;
  ....
  if (array == null || buffer.Length < buffer.Length)  // <=
  {
    if (array != null)
      ArrayPool<byte>.Shared.Return(array);
    array = ArrayPool<byte>.Shared.Rent(buffer.Length);
  }
  if (!buffer.TryCopyTo(array))
    throw new Exception("could not rent large enough buffer.");
  ....
}

The error in the condition was probably the result of copy-paste. According to the fact that buffer is copied in array, the check should look like:

条件中的错误可能是复制粘贴的结果。 根据将缓冲区复制到array的事实,检查应类似于:

if (array == null || array.Length < buffer.Length)

Anyway, as I always say, the code author should deal with fixing such errors.

无论如何,就像我经常说的那样,代码作者应该处理这些错误。

V3083 Unsafe invocation of event '_onChange', NullReferenceException is possible. Consider assigning event to a local variable before invoking it. ClientOptionsMonitor.cs 44 V3083事件'_onChange'的不安全调用,可能发生NullReferenceException。 请考虑在调用事件之前将事件分配给局部变量。 ClientOptionsMonitor.cs 44
private event Action<TOptions, string> _onChange;
....
private void InvokeChanged(....)
{
  ....
  if (_onChange != null)
  {
    _onChange.Invoke(options, name);
  }
}

Not critical, but an error is here. The consumer might unsubscribe from the event between checking the event for null and its invocation. Then the _onChange variable will be null and an exception will be thrown. This code has to be rewritten in a safer way. For example, as follows:

不是很关键,但是这里有一个错误。 在检查事件是否为及其调用之间,使用者可以取消订阅该事件。 然后, _onChange变量将为null并将引发异常。 必须以更安全的方式重写此代码。 例如,如下:

private void InvokeChanged(....)
{
  ....
  _onChange?.Invoke(options, name);
}

Azure.Messaging.EventHubs (Azure.Messaging.EventHubs)

V3080 Possible null dereference. Consider inspecting 'eventPropertyValue'. AmqpMessageConverter.cs 650 V3080可能为空的取消引用。 考虑检查“ eventPropertyValue”。 AmqpMessageConverter.cs 650
private static bool TryCreateEventPropertyForAmqpProperty(
  object amqpPropertyValue,
  out object eventPropertyValue)
{
  eventPropertyValue = null;
  ....
  switch (GetTypeIdentifier(amqpPropertyValue))
  {
    case AmqpProperty.Type.Byte:
    ....
    case AmqpProperty.Type.String:
      eventPropertyValue = amqpPropertyValue;
      return true;
    ....
  }
  ....
  switch (amqpPropertyValue)
  {
    case AmqpSymbol symbol:
      eventPropertyValue = ....;
      break;

    case byte[] array:
      eventPropertyValue = ....;
      break;

    case ArraySegment<byte> segment when segment.Count == segment.Array.Length:
      eventPropertyValue = ....;
      break;

    case ArraySegment<byte> segment:
      ....
      eventPropertyValue = ....;
      break;

    case DescribedType described when (described.Descriptor is AmqpSymbol):
      eventPropertyValue = ....;
      break;

    default:
      var exception = new SerializationException(
        string.Format(...., eventPropertyValue.GetType().FullName));  // <=
      ....
  }

  return (eventPropertyValue != null);
}

Let's see what happens with the eventPropertyValue variable value in the given code fragment. The variable is assigned null at the beginning of the method. Further, in one of the first switch conditions, the variable is initialized, after which the method exits. The second switch block contains many conditions, in each of which the variable also receives a new value. Whereas in the default block, the eventPropertyValue variable is used without any check, which is a mistake, as the variable is null at this moment.

让我们看看给定代码片段中的eventPropertyValue变量值会发生什么。 在方法开始时将变量分配为null 。 此外,在第一个开关条件之一中,变量被初始化,此后方法退出。 第二个开关块包含许多条件,在每个条件中,变量还接收一个新值。 而在默认块中,使用eventPropertyValue变量时不作任何检查,这是一个错误,因为此刻该变量为null

V3066 Possible incorrect order of arguments passed to 'EventHubConsumer' constructor: 'partitionId' and 'consumerGroup'. TrackOneEventHubClient.cs 394 V3066传递给“ EventHubConsumer”构造函数的参数的可能错误顺序:“ partitionId”和“ consumerGroup”。 TrackOneEventHubClient.cs 394
public override EventHubConsumer CreateConsumer(....)
{
  return new EventHubConsumer
  (
    new TrackOneEventHubConsumer(....),
    TrackOneClient.EventHubName,
    partitionId,                  // <= 3
    consumerGroup,                // <= 4
    eventPosition,
    consumerOptions,
    initialRetryPolicy
  );
}

The analyzer suspected confused order of the third and fourth arguments when calling the EventHubConsumer class constructor. So let's check this constructor declaration out:

当调用EventHubConsumer类构造函数时,分析器怀疑第三和第四参数的顺序混乱。 因此,让我们检查一下此构造函数声明:

internal EventHubConsumer(TransportEventHubConsumer transportConsumer,
                          string eventHubName,
                          string consumerGroup,         // <= 3
                          string partitionId,           // <= 4
                          EventPosition eventPosition,
                          EventHubConsumerOptions consumerOptions,
                          EventHubRetryPolicy retryPolicy)
{
  ....
}

Indeed, arguments are mixed up. I would venture to suggest how the error was made. Perhaps, incorrect code formatting is to blame here. Just take another look at the EventHubConsumer constructor declaration. Due to the fact that the first transportConsumer parameter is on the same line with the class name, it may seem that the partitionId parameter is at the third place, not the fourth (my comments with the parameter numbers are not available in the original code). That's just a guess, but I'd change the constructor code formatting to the following:

确实,争论参差不齐。 我敢建议错误是怎么发生的。 也许,错误的代码格式应归咎于此。 只需再看一下EventHubConsumer构造函数声明即可。 由于第一个transportConsumer参数与类名称在同一行,因此似乎partitionId参数位于第三位,而不是第四位(我的参数编号注释在原始代码中不可用) 。 这只是一个猜测,但是我将构造函数的代码格式更改为以下内容:

internal EventHubConsumer
(
  TransportEventHubConsumer transportConsumer,
  string eventHubName,
  string consumerGroup,
  string partitionId,
  EventPosition eventPosition,
  EventHubConsumerOptions consumerOptions,
  EventHubRetryPolicy retryPolicy)
{
  ....
}

Azure存储 (Azure.Storage)

V3112 An abnormality within similar comparisons. It is possible that a typo is present inside the expression 'ContentLanguage == other.ContentEncoding'. BlobSasBuilder.cs 410 V3112类似比较中的异常。 表达式“ ContentLanguage == other.ContentEncoding”内可能存在错字。 BlobSasBuilder.cs 410
public struct BlobSasBuilder : IEquatable<BlobSasBuilder>
{
  ....
  public bool Equals(BlobSasBuilder other) =>
    BlobName == other.BlobName &&
    CacheControl == other.CacheControl &&
    BlobContainerName == other.BlobContainerName &&
    ContentDisposition == other.ContentDisposition &&
    ContentEncoding == other.ContentEncoding &&         // <=
    ContentLanguage == other.ContentEncoding &&         // <=
    ContentType == other.ContentType &&
    ExpiryTime == other.ExpiryTime &&
    Identifier == other.Identifier &&
    IPRange == other.IPRange &&
    Permissions == other.Permissions &&
    Protocol == other.Protocol &&
    StartTime == other.StartTime &&
    Version == other.Version;
}

A mistake made by inattention. Finding such an error with code review is quite difficult. Here is the correct version of code:

由于疏忽而犯的错误。 用代码审查发现这样的错误是非常困难的。 这是正确的代码版本:

....
    ContentEncoding == other.ContentEncoding &&
    ContentLanguage == other.ContentLanguage &&
    ....
V3112 An abnormality within similar comparisons. It is possible that a typo is present inside the expression 'ContentLanguage == other.ContentEncoding'. FileSasBuilder.cs 265 V3112类似比较中的异常。 表达式“ ContentLanguage == other.ContentEncoding”内可能存在错字。 FileSasBuilder.cs 265
public struct FileSasBuilder : IEquatable<FileSasBuilder>
{
  ....
  public bool Equals(FileSasBuilder other)
    => CacheControl == other.CacheControl
    && ContentDisposition == other.ContentDisposition
    && ContentEncoding == other.ContentEncoding         // <=
    && ContentLanguage == other.ContentEncoding         // <=
    && ContentType == other.ContentType
    && ExpiryTime == other.ExpiryTime
    && FilePath == other.FilePath
    && Identifier == other.Identifier
    && IPRange == other.IPRange
    && Permissions == other.Permissions
    && Protocol == other.Protocol
    && ShareName == other.ShareName
    && StartTime == other.StartTime
    && Version == other.Version
    ;

There is exactly the same error in a very similar piece of code. The code might have been copied and partially changed. But the error remained.

在非常相似的代码段中存在完全相同的错误。 该代码可能已被复制并部分更改。 但是错误仍然存​​在。

Microsoft.Azure.Batch (Microsoft.Azure.Batch)

V3053 An excessive expression. Examine the substrings 'IList' and 'List'. PropertyData.cs 157 V3053表达式过多。 检查子字符串“ IList”和“ List”。 PropertyData.cs 157 V3053 An excessive expression. Examine the substrings 'List' and 'IReadOnlyList'. PropertyData.cs 158 V3053表达式过多。 检查子字符串“列表”和“ IReadOnlyList”。 PropertyData.cs 158
public class PropertyData
{
  ....
  public bool IsTypeCollection => this.Type.Contains("IList") ||
                                  this.Type.Contains("IEnumerable") ||
                                  this.Type.Contains("List") ||        // <=
                                  this.Type.Contains("IReadOnlyList"); // <=
}

The analyzer issued two warnings about pointless or erroneous checks. In the first case, search for the «List» substring after searching for «IList» looks redundant. It's true, this condition:

分析仪发出两个关于无意义或错误检查的警告。 在第一种情况下,搜索“ IList”后搜索“ List”子串看起来很多余。 的确如此,这种情况:

this.Type.Contains("IList") || this.Type.Contains("List")

can be well changed for the following:

可以很好地进行以下更改:

this.Type.Contains("List")

In the second case, search for the «IReadOnlyList» substring is pointless, as previously a shorter substring «List» is searched for.

在第二种情况下,搜索«IReadOnlyList»子字符串是没有意义的,因为以前搜索的是较短的子字符串«List»。

There is also a chance that search substrings themselves have errors and there should be something else. Anyway, only the code author is to suggest the correct code version taking into account both comments.

搜索子字符串本身也有错误,并且应该还有其他可能。 无论如何,只有代码作者才能在考虑两个注释的情况下建议正确的代码版本。

V3095 The 'httpRequest.Content.Headers' object was used before it was verified against null. Check lines: 76, 79. BatchSharedKeyCredential.cs 76 V3095在对null进行验证之前,已使用'httpRequest.Content.Headers'对象。 检查行:76,79。BatchSharedKeyCredential.cs 76
public override Task ProcessHttpRequestAsync(
  HttpRequestMessage httpRequest, ....)
{
  ....
  signature.Append(httpRequest.Content != null
    && httpRequest.Content.Headers.Contains("Content-Language") ? .... :  
                                                                  ....;

  long? contentLength = httpRequest.Content?.Headers?.ContentLength;
  ....
}

The httpRequest.Content.Headers variable is first used without any checks, but later it is addressed using the conditional access operator.

首先使用httpRequest.Content.Headers变量,而无需进行任何检查,但随后使用条件访问运算符对其进行寻址。

V3125 The 'omPropertyData' object was used after it was verified against null. Check lines: 156, 148. CodeGenerationUtilities.cs 156 V3125在对null进行验证之后,使用了“ omPropertyData”对象。 检查行:156,148。CodeGenerationUtilities.cs 156
private static string GetProtocolCollectionToObjectModelCollectionString(
  ...., PropertyData omPropertyData, ....)
{
  if (IsMappedEnumPair(omPropertyData?.GenericTypeParameter, ....))
  {
    ....
  }

  if (IsTypeComplex(omPropertyData.GenericTypeParameter))
  ....
}

And here is a reverse situation. One code block contains safe access variant to the omPropertyData potentially null reference. Further in the code, this reference is handled without any checks.

这是相反的情况。 一个代码块包含对omPropertyData可能为空引用的安全访问变体。 进一步在代码中,无需任何检查即可处理此引用。

V3146 Possible null dereference of 'value'. The 'FirstOrDefault' can return default null value. BatchSharedKeyCredential.cs 127 V3146可能对“值”进行空引用。 “ FirstOrDefault”可以返回默认的空值。 BatchSharedKeyCredential.cs 127
public override Task
  ProcessHttpRequestAsync(HttpRequestMessage httpRequest, ....)
{
  ....
  foreach (string canonicalHeader in customHeaders)
  {
    string value = httpRequest.Headers.
                   GetValues(canonicalHeader).FirstOrDefault();
    value = value.Replace('\n', ' ').Replace('\r', ' ').TrimStart();
    ....
  }
  ....
}

Due to the FirstOrDefault method, if the search fails, the default value will be returned, which is null for the string type. The value will be assigned to the value variable, which is then used in the code with the Replace method without any checks. The code should be made safer. For example, as follows:

由于使用FirstOrDefault方法,如果搜索失败,则将返回默认值,该对于字符串类型为null 。 该值将分配给value变量,然后在代码中使用Replace方法,而无需进行任何检查。 该代码应更安全。 例如,如下:

foreach (string canonicalHeader in customHeaders)
{
  string value = httpRequest.Headers.
                 GetValues(canonicalHeader).FirstOrDefault();
  value = value?.Replace('\n', ' ').Replace('\r', ' ').TrimStart();
  ....
}

Microsoft.Azure.ServiceBus (Microsoft.Azure.ServiceBus)

V3121 An enumeration 'BlocksUsing' was declared with 'Flags' attribute, but does not set any initializers to override default values. Fx.cs 69 V3121已使用'Flags'属性声明了枚举'BlocksUsing',但未设置任何初始化程序来覆盖默认值。 Fx.cs 69
static class Fx
{
  ....
  public static class Tag
  {
    ....
    [Flags]
    public enum BlocksUsing
    {
      MonitorEnter,
      MonitorWait,
      ManualResetEvent,
      AutoResetEvent,
      AsyncResult,
      IAsyncResult,
      PInvoke,
      InputQueue,
      ThreadNeutralSemaphore,
      PrivatePrimitive,
      OtherInternalPrimitive,
      OtherFrameworkPrimitive,
      OtherInterop,
      Other,

      NonBlocking,
    }
    ....
  }
  ....
}

The enumeration is declared with the Flags attribute. At the same time, constant values are left by default (MonitorEnter = 0, MonitorWait = 1, ManualResetEvent = 2 and so on). This may result in the following case: when trying to use flags combination, for example, the second and the third constants MonitorWait (=1) | ManualResetEvent (=2), not a unique value will be received, but the constant with the value 3 by default (AutoResetEvent). This may come as a surprise for the caller code. If the BlocksUsing enumeration is really to be used for setting flags combinations (bit field), constants should be given values, equal to number that are powers of two.

枚举使用Flags属性声明。 同时,默认情况下会保留常量值( MonitorEnter = 0MonitorWait = 1ManualResetEvent = 2 ,依此类推)。 这可能导致以下情况:例如,当尝试使用标志组合时,第二个常数和第三个常数MonitorWait(= 1) | ManualResetEvent(= 2) ,不是唯一值,而是默认值为3的常数(AutoResetEvent )。 这可能会使调用者代码感到惊讶。 如果确实将BlocksUsing枚举用于设置标志组合(位字段),则应为常量提供值,该值等于2的幂。

[Flags]
public enum BlocksUsing
{
  MonitorEnter = 1,
  MonitorWait = 2,
  ManualResetEvent = 4,
  AutoResetEvent = 8,
  AsyncResult = 16,
  IAsyncResult = 32,
  PInvoke = 64,
  InputQueue = 128,
  ThreadNeutralSemaphore = 256,
  PrivatePrimitive = 512,
  OtherInternalPrimitive = 1024,
  OtherFrameworkPrimitive = 2048,
  OtherInterop = 4096,
  Other = 8192,

  NonBlocking = 16384,
}
V3125 The 'session' object was used after it was verified against null. Check lines: 69, 68. AmqpLinkCreator.cs 69 V3125在验证了null之后使用了“会话”对象。 检查行:69,68。AmqpLinkCreator.cs 69
public async Task<Tuple<AmqpObject, DateTime>> CreateAndOpenAmqpLinkAsync()
{
  ....
  AmqpSession session = null;
  try
  {
    // Create Session
    ....
  }
  catch (Exception exception)
  {
    ....
    session?.Abort();
    throw AmqpExceptionHelper.GetClientException(exception, null,
      session.GetInnerException(), amqpConnection.IsClosing());
  }
  ....
}

Pay attention to the session variable handling in the catch block. The Abort method is called safely by the conditional access operator. But after the GetInnerException method is called unsafely. In doing so, NullReferenceException might be thrown instead of an exception of the expected type. This code has to be fixed. The AmqpExceptionHelper.GetClientException method supports passing the null value for the innerException parameter:

注意catch块中的会话变量处理。 条件访问运算符会安全地调用Abort方法。 但是在GetInnerException方法被安全调用之后。 这样做可能会引发NullReferenceException而不是预期类型的​​异常。 此代码必须固定。 AmqpExceptionHelper.GetClientException方法支持传递innerException参数的值:

public static Exception GetClientException(
  Exception exception, 
  string referenceId = null, 
  Exception innerException = null, 
  bool connectionError = false)
{
  ....
}

Therefore, one can use only the conditional access operator when calling session.GetInnerException():

因此,在调用session.GetInnerException()时只能使用条件访问运算符:

public async Task<Tuple<AmqpObject, DateTime>> CreateAndOpenAmqpLinkAsync()
{
  ....
  AmqpSession session = null;
  try
  {
    // Create Session
    ....
  }
  catch (Exception exception)
  {
    ....
    session?.Abort();
    throw AmqpExceptionHelper.GetClientException(exception, null,
      session?.GetInnerException(), amqpConnection.IsClosing());
  }
  ....
}

结论 (Conclusion)

As you can see, a large project size doesn't always guarantee a lot of errors. However, we stay alert since we can always find something. Even in a project as structurally complex as Azure SDK for .NET. Finding some crucial defects requires additional efforts. But the more difficulties the more pleasant the result. On the other hand, to avoid undue efforts, we recommend using static analysis right on developers' computers when writing new code. This is the most effective approach. Download and try PVS-Studio in action. Good luck in fighting bugs!

如您所见,大型项目并不总能保证很多错误。 但是,我们始终保持警惕,因为我们总能找到一些东西。 即使在结构上与.NET的Azure SDK一样复杂的项目中。 发现一些关键缺陷需要付出额外的努力。 但是,困难越多,结果越令人愉悦。 另一方面,为避免不必要的工作,我们建议在编写新代码时在开发人员的计算机上使用静态分析。 这是最有效的方法。 下载并尝试使用PVS-Studio 。 祝您战胜虫子!

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

微软的sdk以及azure

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值