مخاطر استخدام ثوابت متعددة الشخصيات

الصورة 1

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

مقدمة


يتم تعريف التنفيذ الحرفي متعدد الأحرف ، لذلك يمكن للمترجمين المختلفين ترميز هذه الحرفيات بطرق مختلفة. على سبيل المثال ، حددت GCC و Clang قيمة بناءً على ترتيب الأحرف في الحرفي ، بينما تقوم MSVC بنقلها وفقًا لنوع الحرف (عادي أو هروب).

على سبيل المثال ، سيتم ترميز الحرفي 'T \ x65s \ x74' بطرق مختلفة ، اعتمادًا على المترجم. كان لابد من إضافة منطق مشابه إلى المحلل. نتيجة لذلك ، قمنا بإنشاء قاعدة تشخيصية جديدة V1039 لتحديد هذه الحرف في الكود. هذه القيم الحرفية خطيرة في المشاريع المشتركة للنظام الأساسي التي تستخدم مترجمين متعددين للتجميع.

التشخيص V1039


النظر في مثال. الكود أدناه ، الذي تم تجميعه من قِبل مترجمين مختلفين ، سوف يتصرف بشكل مختلف:

#include <stdio.h> void foo(int c) { if (c == 'T\x65s\x74') // <= V1039 { printf("Compiled with GCC or Clang.\n"); } else { printf("It's another compiler (for example, MSVC).\n"); } } int main(int argc, char** argv) { foo('Test'); return 0; } 

سيقوم برنامج يجمعه مترجمون مختلفون بطباعة رسائل مختلفة على الشاشة.

بالنسبة لمشروع يستخدم برنامج التحويل البرمجي المحدد ، فلن يكون هذا ملحوظًا ، ولكن يمكن أن تسبب عملية النقل مشاكل ، لذلك يجب استبدال هذه القيم الحرفية بثوابت رقمية بسيطة ، على سبيل المثال ، قم بتغيير "اختبار" إلى 0x54657374.

لإظهار الفرق بين المترجمين ، نكتب أداة مساعدة صغيرة حيث يتم أخذ تسلسل من 3 و 4 أحرف ، على سبيل المثال ، "GHIJ" و "GHI" ، وتمثيلها في الذاكرة بعد عرض الترجمة.

رمز المنفعة:

 #include <stdio.h> typedef int char_t; void PrintBytes(const char* format, char_t lit) { printf("%20s : ", format); const unsigned char *ptr = (const unsigned char*)&lit; for (int i = sizeof(lit); i--;) { printf("%c", *ptr++); } putchar('\n'); } int main(int argc, char** argv) { printf("Hex codes are: G(%02X) H(%02X) I(%02X) J(%02X)\n",'G','H','I','J'); PrintBytes("'GHIJ'", 'GHIJ'); PrintBytes("'\\x47\\x48\\x49\\x4A'", '\x47\x48\x49\x4A'); PrintBytes("'G\\x48\\x49\\x4A'", 'G\x48\x49\x4A'); PrintBytes("'GH\\x49\\x4A'", 'GH\x49\x4A'); PrintBytes("'G\\x48I\\x4A'", 'G\x48I\x4A'); PrintBytes("'GHI\\x4A'", 'GHI\x4A'); PrintBytes("'GHI'", 'GHI'); PrintBytes("'\\x47\\x48\\x49'", '\x47\x48\x49'); PrintBytes("'GH\\x49'", 'GH\x49'); PrintBytes("'\\x47H\\x49'", '\x47H\x49'); PrintBytes("'\\x47HI'", '\x47HI'); return 0; } 

إخراج الأداة المساعدة المترجمة بواسطة Visual C ++:

 Hex codes are: G(47) H(48) I(49) J(4A) 'GHIJ' : JIHG '\x47\x48\x49\x4A' : GHIJ 'G\x48\x49\x4A' : HGIJ 'GH\x49\x4A' : JIHG 'G\x48I\x4A' : JIHG 'GHI\x4A' : JIHG 'GHI' : IHG '\x47\x48\x49' : GHI 'GH\x49' : IHG '\x47H\x49' : HGI '\x47HI' : IHG 

إخراج أداة مساعدة تم تجميعها بواسطة GCC أو Clang:

 Hex codes are: G(47) H(48) I(49) J(4A) 'GHIJ' : JIHG '\x47\x48\x49\x4A' : JIHG 'G\x48\x49\x4A' : JIHG 'GH\x49\x4A' : JIHG 'G\x48I\x4A' : JIHG 'GHI\x4A' : JIHG 'GHI' : IHG '\x47\x48\x49' : IHG 'GH\x49' : IHG '\x47H\x49' : IHG '\x47HI' : IHG 

استنتاج


تمت إضافة تشخيصات V1039 إلى الإصدار 7.03 من محلل PVS-Studio ، والذي تم إصداره مؤخرًا. يمكنك تنزيل أحدث إصدار من المحلل على صفحة التنزيل .



إذا كنت ترغب في مشاركة هذه المقالة مع جمهور يتحدث الإنجليزية ، فالرجاء استخدام الرابط الخاص بالترجمة: Svyatoslav Razmyslov. مخاطر استخدام ثوابت متعددة الشخصيات

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


All Articles