قصة كيف وجدت PVS-Studio خطأ في المكتبة المستخدمة في ... PVS-Studio

الصورة 1

هذه قصة قصيرة حول كيف ساعدنا PVS-Studio في العثور على خطأ في الكود المصدري للمكتبة المستخدمة في PVS-Studio. ولم يكن خطأً نظريًا ، ولكنه كان خطأً فعليًا - ظهر الخطأ في الممارسة عند استخدام المكتبة في المحلل.

في PVS-Studio_Cmd (وكذلك بعض الأدوات المساعدة الأخرى) نستخدم مكتبة خاصة لتحليل وسيطات سطر الأوامر - CommandLine.

لقد دعمت اليوم الوضع الجديد في PVS-Studio_Cmd ، وقد حدث أن اضطررت إلى استخدام هذه المكتبة لتحليل وسيطات سطر الأوامر. أثناء كتابة التعليمات البرمجية ، أقوم أيضًا بتصحيحها لأنني مضطر للعمل مع واجهات برمجة التطبيقات غير المألوفة.

لذلك ، يتم كتابة التعليمات البرمجية ، وتجميعها ، وتنفيذها و ...

الصورة 3

تنفيذ التعليمات البرمجية داخل المكتبة حيث يحدث استثناء من نوع NullReferenceException . الأمر ليس واضحًا تمامًا من الجانب - فأنا لا أنقل أي إشارات فارغة إلى الطريقة.

بالتأكيد ، أنا أنظر إلى التعليقات على الطريقة المستدقة. لا يكاد يكون من المحتمل أن يصفوا شروط حدوث استثناء لنوع NullReferenceException (كما يبدو لي عادةً ما لم يتم توفير استثناءات من هذا النوع).

الصورة 2

لا توجد معلومات حول NullReferenceException في التعليقات على الطريقة (والتي ، مع ذلك ، من المتوقع).

لمعرفة سبب الاستثناء (ومكان حدوثه) بالضبط ، قررت تنزيل شفرة مصدر المشروع وإنشائه وإضافة مرجع إلى إصدار تصحيح المكتبة للمحلل. الكود المصدري للمشروع متاح في جيثب . نحتاج إلى الإصدار 1.9.71 من المكتبة. هذا هو واحد يستخدم في محلل الآن.

أقوم بتنزيل الإصدار المقابل من الكود المصدري ، وبناء المكتبة ، وإضافة مرجع إلى مكتبة التصحيح إلى المحلل ، وتنفيذ الكود ورؤية:

صورة 4

لذلك ، يكون المكان الذي يحدث فيه الاستثناء واضحًا - helpInfo له قيمة فارغة ، مما يسبب استثناءًا لنوع NullReferenceException عند الوصول إلى الخاصية Left .

بدأت أفكر في الأمر. في الآونة الأخيرة ، تم تحسين PVS-Studio for C # جيدًا في العديد من الجوانب ، بما في ذلك البحث عن إلغاء تسجيل المراجع التي قد تكون خالية. على وجه الخصوص ، تم تحسين تحليل interprocedural في عدد من الطرق. لهذا السبب كنت مهتمًا على الفور بفحص الكود المصدري لفهم ما إذا كان بإمكان PVS-Studio العثور على الخطأ قيد المناقشة.

راجعت الكود المصدري ومن بين التحذيرات الأخرى رأيت بالضبط ما كنت آمل.

تحذير PVS-Studio : V3080 dereference فارغة محتملة داخل الأسلوب في 'helpInfo.Left'. النظر في فحص الوسيطة 2: helpInfo. Parser.cs 405

نعم ، هذا كل شيء! هذا هو بالضبط ما نحتاجه. دعونا نلقي نظرة أكثر تفصيلا على شفرة المصدر.

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 . انتبه إلى أن هذه الطريقة موجودة في الفرع الخاص ببيان if . يتكون التعبير الشرطي بطريقة يمكن من خلالها تنفيذ الفرع آنذاك عند القيم التالية للمتغيرات:

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

دعنا نرى نص أسلوب 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); } } 

منذ الفعل == null (انظر استدعاء الأسلوب) ، نحن مهتمون آنذاك بفرع العبارة if . على الرغم من أن الوضع مشابه للفرع الآخر ، فلننظر في ذلك الفرع لأنه في حالتنا الخاصة تم تنفيذ الإعدام فيه. تذكر أن helpInfo قد تكون خالية .

الآن دعونا نلقي نظرة على نص 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 يسمى دون قيد أو شرط ، في حين أن helpInfo قد تكون خالية . لقد حذر المحلل من ذلك ، وهذا ما حدث.

استنتاج

من الجيد أننا تمكنا من العثور على خطأ في الكود المصدري للمكتبة المستخدمة في PVS-Studio بمساعدة PVS-Studio. أعتقد أن هذا هو نوع من الإجابة على السؤال "هل يجد PVS-Studio أخطاء في شفرة مصدر PVS-Studio؟". :) يمكن للمحلل العثور على أخطاء ليس فقط في كود PVS-Studio ولكن أيضًا في كود المكتبات المستخدمة.

أخيرًا ، أقترح عليك تنزيل المحلل ومحاولة التحقق من مشروعك - ماذا لو وجدت شيئًا مثيرًا للاهتمام أيضًا؟

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


All Articles