PVS-Studio in the Clouds: GitLab CI / CD

الشكل 2

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

قائمة مقالاتنا الأخرى حول الاندماج في أنظمة CI السحابية:


معلومات حول البرنامج المستخدم


GitLab هي خدمة عبر الإنترنت مصممة لإدارة المستودعات. يمكنك استخدامه مباشرة في متصفح على الموقع الرسمي عن طريق تسجيل حسابك ، أو تثبيته ونشره على الخادم الخاص بك.

PVS-Studio هي أداة مصممة للكشف عن الأخطاء ونقاط الضعف المحتملة في التعليمات البرمجية المصدر للبرامج ، مكتوبة بلغات C و C ++ و C # و Java. يعمل في أنظمة 64 بت على أنظمة Windows و Linux و macOS ويمكنه تحليل الشفرة لأنظمة ARM 32 بت و 64 بت والمضمنة. إذا كانت هذه هي المرة الأولى التي تستخدم فيها المحلل للتحقق من مشاريعك ، فننصحك بقراءة المقال حول كيفية التحقق بسرعة من تحذيرات PVS-Studio الأكثر إثارة وتقييم قدرات الأداة.

سيتم استخدام مشروع OBS لإظهار قدرات محلل ثابت في السحابة. Open Broadcaster Software هي مجموعة برامج مجانية ومفتوحة لتسجيل الفيديو وتدفقه. يوفر OBS الجهاز في الوقت الحقيقي واعتراض المصدر ، وتكوين المشهد ، فك التشفير والتسجيل والبث. يتم نقل البيانات بشكل أساسي من خلال بروتوكول المراسلة في الوقت الحقيقي ، ويمكن إرسالها إلى أي مصدر يدعم RTMP - يحتوي البرنامج على تجهيزات مسبقة الصنع جاهزة للبث المباشر على منصات البث الأكثر شعبية.

ترتيب


لبدء العمل مع GitLab ، انتقل إلى موقع الويب وانقر فوق تسجيل :

الشكل 6

يمكنك التسجيل عن طريق ربط حسابات الخدمات الأخرى مثل GitHub أو Twitter أو Google أو BitBucket أو Saleforce أو ببساطة عن طريق ملء النموذج المفتوح. بعد الترخيص ، تدعونا GitLab إلى إنشاء مشروع:

الشكل 7

قائمة الأنظمة الأساسية المتاحة لاستيراد الملفات:

الشكل 33


لنقم بإنشاء مشروع فارغ للوضوح:

الشكل 17


بعد ذلك ، نحتاج إلى تحميل مشروعنا في المستودع الذي تم إنشاؤه. قم بذلك باستخدام التلميحات التي تظهر في نافذة المشروع الذي تم إنشاؤه.

الشكل 1


عند بدء المهمة ، يأخذ GitLab CI التعليمات من ملف .gitlab-ci.yml . يمكنك إضافته إما بالنقر فوق إعداد CI / CD ، أو ببساطة عن طريق إنشاء مستودع محلي وتحميله على الموقع. دعنا نتبع الخيار الأول:

الشكل 21



اصنع الآن غلافًا بسيطًا للبرنامج النصي:

image: debian job: script: 

قم بتنزيل الأداة المساعدة للبريد الإلكتروني والمحلل ، والتي سنحتاج إليها لاحقًا:

 - apt-get update && apt-get -y install wget gnupg - wget -O - https://files.viva64.com/etc/pubkey.txt | apt-key add - - wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - apt-get update && apt-get -y install pvs-studio sendemail 

بعد ذلك ، سنقوم بتثبيت التبعيات والأدوات المساعدة لبناء OBS:

 - apt-get -y install build-essential cmake make pkg-config libx11-dev libgl1-mesa-dev libpulse-dev libxcomposite-dev libxinerama-dev libv4l-dev libudev-dev libfreetype6-dev libfontconfig-dev qtbase5-dev libqt5x11extras5-dev libx264-dev libxcb-xinerama0-dev libxcb-shm0-dev libjack-jackd2-dev libcurl4-openssl-dev libavcodec-dev libqt5svg5 libavfilter-dev libavdevice-dev libsdl2-dev ffmpeg qt5-default qtscript5-dev libssl-dev qttools5-dev qttools5-dev-tools qtmultimedia5-dev libqt5svg5-dev libqt5webkit5-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-jackd2-dev libxrandr-dev libqt5xmlpatterns5-dev libqt5xmlpatterns5 coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls28-dev libselinux1-dev linux-libc-dev libtool autotools-dev libio-socket-ssl-perl libnet-ssleay-perl ca-certificates 

نحتاج الآن إلى إنشاء الملف بترخيص محلل (افتراضيًا ، سيتم إنشاء ملف PVS-Studio.lic في الدليل ../.config/PVS-Studio). عند القيام بذلك ، لا يتعين عليك تحديد ملف الترخيص في المعلمات التي تعمل محلل ، وسيتم اكتشافه تلقائيًا):

 - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY 

هنا PVS_NAME و PVS_KEY هما أسماء المتغيرات التي نحددها في الإعدادات. سيقومون بتخزين تسجيل الدخول PVS-Studio ومفتاح الترخيص. لتعيين قيمهم ، اتبع: الإعدادات> CI / CD> المتغيرات.

الشكل 23


بناء المشروع ، باستخدام cmake:

 - cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=On /builds/Stolyarrrov/obscheck/ - make -j4 

بعد ذلك ، قم بتشغيل المحلل:

 - pvs-studio-analyzer analyze -o PVS-Studio.log 

سوف PVS-Studio.log تخزين نتائج التحليل. الملف الناتج مع التقرير غير مخصص للقراءة. من أجل إتاحتها للعين البشرية ، نحتاج إلى أداة تحويل plog. هذا البرنامج يحول سجل المحلل إلى صيغ مختلفة. لسهولة القراءة ، دعنا نحول السجل إلى تنسيق html:

 - plog-converter -t html PVS-Studio.log -o PVS-Studio.html 

يمكنك تصدير التقرير باستخدام المصنوعات اليدوية ، لكننا سنقوم بتغيير المسار وإرسال الملف مع نتائج محلل عن طريق البريد الإلكتروني باستخدام الأداة المساعدة sendemail:

 - sendemail -t $MAIL_TO -u "PVS-Studio report, commit:GITLAB_COMMIT" -m "PVS-Studio report, commit:GITLAB_COMMIT" -s $GMAIL_PORT -o tls=auto -f $MAIL_FROM -xu $MAIL_FROM -xp $MAIL_FROM_PASS -a PVS-Studio.log PVS-Studio.html 

Full .gitlab-ci.yml:

 image: debian job: script: - apt-get update && apt-get -y install wget gnupg - wget -O - https://files.viva64.com/etc/pubkey.txt | apt-key add - - wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - apt-get update && apt-get -y install pvs-studio sendemail - apt-get -y install build-essential cmake pkg-config libx11-dev libgl1-mesa-dev libpulse-dev libxcomposite-dev libxinerama-dev libv4l-dev libudev-dev libfreetype6-dev libfontconfig-dev qtbase5-dev libqt5x11extras5-dev libx264-dev libxcb-xinerama0-dev libxcb-shm0-dev libjack-jackd2-dev libcurl4-openssl-dev libavcodec-dev libqt5svg5 libavfilter-dev libavdevice-dev libsdl2-dev ffmpeg qt5-default qtscript5-dev libssl-dev qttools5-dev qttools5-dev-tools qtmultimedia5-dev libqt5svg5-dev libqt5webkit5-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-jackd2-dev libxrandr-dev libqt5xmlpatterns5-dev libqt5xmlpatterns5 coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls28-dev libselinux1-dev linux-libc-dev libtool autotools-dev make libio-socket-ssl-perl libnet-ssleay-perl ca-certificates - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY - cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=On /builds/Stolyarrrov/obscheck/ - make -j4 - pvs-studio-analyzer analyze -o PVS-Studio.log - plog-converter -t html PVS-Studio.log -o PVS-Studio.html - sendemail -t $MAIL_TO -u "PVS-Studio report, commit:GITLAB_COMMIT" -m "PVS-Studio report, commit:GITLAB_COMMIT" -s $GMAIL_PORT -o tls=auto -f $MAIL_FROM -xu $MAIL_FROM -xp $MAIL_FROM_PASS -a PVS-Studio.log PVS-Studio.html 

انقر فوق تغييرات الالتزام . إذا فعلنا كل شيء بشكل صحيح ، فسنرى الإخراج: هذا التكوين GitLab CI صالح. لتتبع التقدم ، دعنا ننتقل إلى علامة التبويب CI / CD> خطوط الأنابيب .

الشكل 5


انقر على التوالي . سنرى نافذة محطة الجهاز الظاهري حيث يعمل ملف التكوين الخاص بنا. بعد فترة ، تلقينا رسالة: نجحت الوظيفة.

الشكل 29


لذلك حان الوقت لفتح ملف html مع التحذيرات المرسلة عبر البريد.

نتائج التحليل


دعنا نلقي نظرة على بعض التحذيرات من التقرير ، ونكشف عن الأخطاء في مشروع Open Broadcaster Software للحصول على جوهر تحليل الشفرة الثابتة. نظرًا لأن الغرض الرئيسي من المقالة هو وصف مبادئ التفاعل بين PVS-Studio و GitLab CI / CD ، فقد تم اختيار أمثلة غير تقليدية فقط. نحن على استعداد لمنح مؤلفي المشروع ترخيصًا مؤقتًا ، وإذا رغبوا في ذلك ، فنحن نرحب بهم لإجراء تحليل أكثر شمولاً للمشروع. بالإضافة إلى ذلك ، يمكنهم استخدام إحدى الطرق للحصول على ترخيص PVS-Studio مجانًا .

كما يمكن للجميع الحصول على مفتاح تجريبي لاستكشاف إمكانيات PVS-Studio والتحقق من مشاريعهم.

لذلك ، دعنا ننتبه لبعض أمثلة الأخطاء التي تم العثور عليها في Open Broadcaster Software.

تحذير N1

V547 التعبير 'back_size' صحيح دائمًا. circlebuf.h (138)

 struct circlebuf { .... size_t capacity; }; static inline void circlebuf_place(struct circlebuf *cb, size_t position,....,const void *data, size_t size) { .... size_t data_end_pos; data_end_pos = position + size; if (data_end_pos > cb->capacity) { size_t back_size = data_end_pos - cb->capacity; if (back_size) { memcpy((uint8_t *)cb->data + position, data, loop_size); } .... } 

الخط إذا كانت (data_end_pos> cb-> السعة) تستحق بالتأكيد إلقاء نظرة فاحصة عليها. إذا كان الشرط صحيحًا ، فسيظل متغير الحجم الخلفي ، المحدد في السطر أدناه ، دائمًا أكبر من الصفر ، حيث نتعامل هنا مع طرح القيمة الأصغر شهرةً من القيمة الأكبر. في النهاية ، ستكون الحالة ، التي يوجد سطرين أدناه ، صحيحة دائمًا. شرط التكرار ليس ضارًا جدًا عندما يتبعه الكود ، مما يؤدي إلى تغيير البيانات.

تحذيرات N2 ، N3

V629 خذ بعين الاعتبار فحص تعبير '1 << المسافة البادئة. تحويل البت لقيمة 32 بت مع توسع لاحق إلى نوع 64 بت. profiler.c (610)

 static void profile_print_entry(uint64_t active, unsigned indent, ....) { .... active &= (1 << indent) - 1; .... } 

تبدو العمليات المشوشة على أنواع 32 بت و 64 بت مشبوهة هنا. أولاً ، يقوم المبرمج بتقييم القناع ، باستخدام أنواع 32 بت (تعبير (1 << المسافة البادئة) - 1 ) ، بعد ذلك يتم توسيعه ضمنيًا إلى نوع 64 بت في التعبير النشط & = .... على الأرجح ، عند تقييم القناع ، كان مطلوبًا أيضًا استخدام أنواع 64 بت.

إصدار الكود الصحيح:

 active &= ((uint64_t)(1) << indent) - 1; 

أو:

 active &= (1ull << indent) - 1; 

بالمناسبة ، إصدار نسخة لصق هذه الكتلة أدناه ، أصدر المحلل أيضًا التحذير الخاص به: V629 خذ بعين الاعتبار فحص تعبير "المسافة البادئة 1". تحويل البت لقيمة 32 بت مع توسع لاحق إلى نوع 64 بت. profiler.c (719)

تحذير N4

V761 تم العثور على أربعة كتل مماثلة من النص. "obs-audio-controls.c" (353)

 static float get_true_peak(....) { .... peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); .... } 

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

 for(size_t i = 0; i < 3; i++) { peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); } 

تحذير n5

V560 جزء من التعبير الشرطي خطأ دائمًا: '! Modifiers'. obs-hotkey.c (662)

 typedef struct obs_key_combination obs_key_combination_t; struct obs_key_combination { uint32_t modifiers; obs_key_t key; }; static inline void load_binding(....) { obs_key_combination_t combo = {0}; uint32_t *modifiers = &combo.modifiers; load_modifier(modifiers, data, "shift", INTERACT_SHIFT_KEY); load_modifier(modifiers, data, "control", INTERACT_CONTROL_KEY); load_modifier(modifiers, data, "alt", INTERACT_ALT_KEY); load_modifier(modifiers, data, "command", INTERACT_COMMAND_KEY); if (!modifiers && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE)) { .... } .... } 

تعريف وظيفة load_modifier :

 static inline void load_modifier(uint32_t *modifiers, obs_data_t *data, const char *name, uint32_t flag) { if (obs_data_get_bool(data, name)) *modifiers |= flag; } 

كما نرى ، المعدلات هي مؤشر ، تتم تهيئته بواسطة عنوان حقل المعدلات في بنية التحرير والسرد . نظرًا لأن قيمتها لا تتغير حتى يتم التحقق ، فستظل غير خالية. علاوة على ذلك ، بعد التهيئة قبل التحقق ، يتم استخدام المؤشر عند استدعاء وظيفة load_modifier ، حيث يتم إلغاء ترجمتها . وفقًا لذلك ، فإن اختيار Modifiers لا معنى له ، نظرًا لأن عامل التشغيل && سنحصل دائمًا على خطأ عند تقييم التعبير المنطقي. أعتقد أن المبرمج أراد التحقق من قيمة عددية حسب العنوان الذي تم تخزينه في مؤشر المعدلات ، ولكن نسيت إلغاء تحديد هذا المؤشر.

لذلك يبدو لي أن الشيك يجب أن يكون على النحو التالي:

 if (!*modifiers && ....) Or like this: if (!combo.modifiers && ....) 

تحذير N6

V575 يتم تمرير المؤشر الفارغ المحتمل في وظيفة "strncpy". تفقد الحجة الأولى. خطوط الفحص: 2904 ، 2903. rtmp.c (2904)

 static int PublisherAuth(....) { .... ptr = malloc(r->Link.app.av_len + pubToken.av_len); strncpy(ptr, r->Link.app.av_val, r->Link.app.av_len); .... } 

غالبًا ما تكون هذه التعليمة البرمجية غير آمنة ، حيث إنها تتجاهل إمكانية قيام malloc بإرجاع مؤشر فارغ. في حالة إرجاع malloc NULL ، سيحدث سلوك غير معرف في هذه الحالة ، حيث سيكون للوسيطة الأولى للدالة strncpy قيمة NULL .

لمزيد من المعلومات حول سبب أهمية التحقق من القيمة المرجعة للدالة malloc ، راجع المقالة ذات الصلة .

تحذيرات N7 ، N8 ، N9

تخمين الحالات التي تحتوي على حسابات غير صحيحة:

 class OBSProjector : public OBSQTDisplay { .... float sourceX, sourceY, ....; .... } .... void OBSProjector::OBSRenderMultiview(....) { OBSProjector *window = (OBSProjector *)data; .... auto calcBaseSource = [&](size_t i) { switch (multiviewLayout) { case MultiviewLayout::HORIZONTAL_TOP_24_SCENES: window->sourceX = (i % 6) * window->scenesCX; window->sourceY = window->pvwprgCY + (i / 6) * window->scenesCY; break; case MultiviewLayout::VERTICAL_LEFT_8_SCENES: window->sourceX = window->pvwprgCX; window->sourceY = (i / 2) * window->scenesCY; if (i % 2 != 0) { window->sourceX += window->scenesCX; } break; case MultiviewLayout::VERTICAL_RIGHT_8_SCENES: window->sourceX = 0; window->sourceY = (i / 2) * window->scenesCY; if (i % 2 != 0) { window->sourceX = window->scenesCX; } break; case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = 0; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->scenesCY; } break; default:// MultiviewLayout::HORIZONTAL_TOP_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = window->pvwprgCY; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->pvwprgCY + window->scenesCY; } } } .... } 

تحذيرات المحلل:

  • V636 تم التعبير عن التعبير "i / 6" ضمنيًا من النوع "size_t" إلى النوع "float". النظر في استخدام يلقي نوع واضح لتجنب فقدان جزء كسور. مثال: double A = (double) (X) / Y؛ window-projector.cpp (330)
  • V636 تم التعبير عن التعبير "i / 2" ضمنيًا من النوع "size_t" إلى النوع "float". النظر في استخدام يلقي نوع واضح لتجنب فقدان جزء كسور. مثال: double A = (double) (X) / Y؛ window-projector.cpp (334)
  • V636 تم التعبير عن التعبير "i / 2" ضمنيًا من النوع "size_t" إلى النوع "float". النظر في استخدام يلقي نوع واضح لتجنب فقدان جزء كسور. مثال: double A = (double) (X) / Y؛ window-projector.cpp (340)

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

استنتاج


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

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


All Articles