يذهب برنامج PVS-Studio إلى السحب - تحليل الإطلاق على Travis CI

في الوقت الحالي ، تعد أنظمة Cloud CI خدمة شائعة جدًا. في هذه المقالة سوف نوضح كيف ، باستخدام الأدوات المتاحة في PVS-Studio ، يمكنك دمج تحليل شفرة المصدر مع نظام CI السحابي باستخدام خدمة Travis CI كمثال.

الصورة 1


لماذا ننظر إلى سحب طرف ثالث ولا نصنع غيومنا؟ هناك عدد من الأسباب ، والسبب الرئيسي هو أن SaaS هو إجراء مكلف وصعب إلى حد ما. في الواقع ، يعد دمج تحليل PVS-Studio مباشرة مع نظام أساسي سحابي تابع لجهة خارجية (سواء أكان ذلك منصات مفتوحة مثل CircleCI أو Travis CI أو GitLab أو بعض حلول المؤسسات المتخصصة المستخدمة في شركة واحدة محددة فقط) مهمة بسيطة إلى حد ما. وهذا يعني أنه يمكننا القول أن PVS-Studio متوفر بالفعل "في السحب" . هناك مسألة مختلفة تمامًا وهي تنظيم وتوفير البنية التحتية لمثل هذا العمل على مدار الساعة طوال أيام الأسبوع. هذه مهمة مختلفة تمامًا ، وليس لدى PVS-Studio أي خطط لتوفير منصة سحابة خاصة بها مباشرة لإجراء التحليل عليها.

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


Travis CI هي خدمة لإنشاء واختبار البرامج التي تستخدم GitHub كتخزين. لا يتطلب Travis CI تغيير رمز البرنامج لاستخدام الخدمة ، فكل الإعدادات تحدث في ملف .travis.yml الموجود في جذر المخزون.

سنتخذ LXC (حاويات Linux) كمشروع اختبار للاختبار باستخدام PVS-Studio. إنه نظام ظاهري على مستوى نظام التشغيل لتشغيل مثيلات متعددة لنظام التشغيل Linux على عقدة واحدة.

المشروع صغير ، لكن أكثر من كافٍ للتوضيح. إخراج الأمر cloc:
لغة
ملفات
فراغ
تعليق
قانون
C
124
11937
6758
50836
C / C ++ Header
65
1117
3676
3774
ملاحظة: يستخدم مطورو LXC بالفعل Travis CI ، لذلك سنتخذ ملف التكوين الخاص بهم كأساس ونقوم بتحريره لأغراضنا.

تعديل


للبدء في Travis CI ، اتبع الرابط والمصادقة باستخدام حساب GitHub.

صورة 17

في النافذة التي تفتح ، تحتاج إلى تفويض Travis CI.

الصورة 16

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

  • تفعيل المستودعات ؛
  • أضف الملف .travis.yml إلى المستودع ؛
  • تشغيل البناء الأول.

صورة 18

سنبدأ في تنفيذ هذه النقاط.

لإضافة مستودعنا إلى Travis CI ، انتقل إلى إعدادات الملف الشخصي عبر الرابط وانقر على زر "تنشيط".

صورة 19

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

صورة 38

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

قم بإنشاء المتغيرات التي سنستخدمها على الفور لإنشاء ملف ترخيص المحلل وإرسال تقاريره. للقيام بذلك ، انتقل إلى صفحة الإعدادات - زر "الإعدادات" على يمين المستودع المرغوب.

صورة 39

سيتم فتح نافذة الإعدادات.

صورة 41

وصف قصير للإعدادات:

  • القسم "عام" - تحديد مشغلات مهام التشغيل التلقائي ؛
  • قسم "الإلغاء التلقائي" - يسمح لك بتكوين التجميع التلقائي للإلغاء ؛
  • يتيح لك قسم "متغيرات البيئة" - تحديد متغيرات البيئة التي تحتوي على كل من المعلومات العامة والسرية ، مثل بيانات الاعتماد أو مفاتيح ssh ؛
  • قسم "وظائف كرون" - تحديد جدول إطلاق المهمة.

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

الصورة 5

قم بإنشاء المتغيرات MAIL_USER و MAIL_PASSWORD فورًا التي تحتوي على اسم المستخدم وكلمة المرور لصندوق البريد ، والتي سنستخدمها لإرسال التقارير.

صورة 4

عند بدء المهمة ، يأخذ Travis CI تعليمات من ملف .travis.yml الموجود في جذر المخزون.

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

لنقم بإنشاء تكوين لتشغيل المحلل في جهاز افتراضي.

للتجميع والاختبار ، سنستخدم جهازًا افتراضيًا يستند إلى Ubuntu Trusty ، ويمكن العثور على وصفه هنا .

بادئ ذي بدء ، نشير إلى أن المشروع مكتوب بلغة C وأننا ندرج قائمة المترجمين الذين سنستخدمهم للتجميع:

language: c compiler: - gcc - clang 

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

قبل بدء الإنشاء ، نحتاج إلى إضافة مستودع محلل وتثبيت التبعيات وحزم إضافية:

 before_install: - sudo add-apt-repository ppa:ubuntu-lxc/daily -y - wget -q -O - https://files.viva64.com/etc/pubkey.txt | sudo apt-key add - - sudo wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - sudo apt-get update -qq - sudo apt-get install -qq coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls-dev libselinux1-dev linux-libc-dev pvs-studio libio-socket-ssl-perl libnet-ssleay-perl sendemail ca-certificates 

قبل بناء المشروع ، تحتاج إلى إعداد البيئة:

 script: - ./coccinelle/run-coccinelle.sh -i - git diff --exit-code - export CFLAGS="-Wall -Werror" - export LDFLAGS="-pthread -lpthread" - ./autogen.sh - rm -Rf build - mkdir build - cd build - ../configure --enable-tests --with-distro=unknown 

بعد ذلك ، نحتاج إلى إنشاء ملف بترخيص وتشغيل تحليل المشروع.

ينشئ الأمر الأول ملف ترخيص للمحلل. تؤخذ البيانات للمتغيرات $ PVS_USERNAME و $ PVS_KEY من إعدادات المشروع.

 - pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic 

يبدأ الأمر التالي في تتبع تجميع المشروع:

 - pvs-studio-analyzer trace -- make -j4 

بعد أن نبدأ التحليل الثابت.
ملاحظة: عند استخدام ترخيص تجريبي ، يجب عليك تحديد المعلمة - disableLicenseExpirationCheck .

  - pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic -o PVS-Studio-${CC}.log –-disableLicenseExpirationCheck 

باستخدام الأمر الأخير ، يتم تحويل ملف نتائج المحلل إلى تقرير html.

 - plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html 

نظرًا لأن TravisCI لا يسمح بتغيير تنسيق إعلامات البريد الإلكتروني ، فسوف نستخدم حزمة البريد الإلكتروني لإرسال التقارير في الخطوة الأخيرة:

 - sendemail -t mail@domain.com -u "PVS-Studio $CC report, commit:$TRAVIS_COMMIT" -m "PVS-Studio $CC report, commit:$TRAVIS_COMMIT" -s smtp.gmail.com:587 -xu $MAIL_USER -xp $MAIL_PASSWORD -o tls=yes -f $MAIL_USER -a PVS-Studio-${CC}.log PVS-Studio-${CC}.html 

النص الكامل لملف التكوين لتشغيل المحلل في جهاز افتراضي:

 language: c compiler: - gcc - clang before_install: - sudo add-apt-repository ppa:ubuntu-lxc/daily -y - wget -q -O - https://files.viva64.com/etc/pubkey.txt | sudo apt-key add - - sudo wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - sudo apt-get update -qq - sudo apt-get install -qq coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls-dev libselinux1-dev linux-libc-dev pvs-studio libio-socket-ssl-perl libnet-ssleay-perl sendemail ca-certificates script: - ./coccinelle/run-coccinelle.sh -i - git diff --exit-code - export CFLAGS="-Wall -Werror" - export LDFLAGS="-pthread -lpthread" - ./autogen.sh - rm -Rf build - mkdir build - cd build - ../configure --enable-tests --with-distro=unknown - pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic - pvs-studio-analyzer trace -- make -j4 - pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic -o PVS-Studio-${CC}.log --disableLicenseExpirationCheck - plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html - sendemail -t mail@domain.com -u "PVS-Studio $CC report, commit:$TRAVIS_COMMIT" -m "PVS-Studio $CC report, commit:$TRAVIS_COMMIT" -s smtp.gmail.com:587 -xu $MAIL_USER -xp $MAIL_PASSWORD -o tls=yes -f $MAIL_USER -a PVS-Studio-${CC}.log PVS-Studio-${CC}.html 

لتشغيل محلل ثابت في حاوية ، قم أولاً بإنشائه باستخدام Dockerfile التالي:

 FROM docker.io/ubuntu:trusty ENV CFLAGS="-Wall -Werror" ENV LDFLAGS="-pthread -lpthread" RUN apt-get update && apt-get install -y software-properties-common wget \ && wget -q -O - https://files.viva64.com/etc/pubkey.txt | sudo apt-key add - \ && wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list \ && apt-get update \ && apt-get install -yqq coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls-dev libselinux1-dev linux-libc-dev pvs-studio git libtool autotools-dev automake pkg-config clang make libio-socket-ssl-perl libnet-ssleay-perl sendemail ca-certificates \ && rm -rf /var/lib/apt/lists/* 

في هذه الحالة ، قد يبدو ملف التكوين كما يلي:

 before_install: - docker pull docker.io/oandreev/lxc env: - CC=gcc - CC=clang script: - docker run --rm --cap-add SYS_PTRACE -v $(pwd):/pvs -w /pvs docker.io/oandreev/lxc /bin/bash -c " ./coccinelle/run-coccinelle.sh -i && git diff --exit-code && ./autogen.sh && mkdir build && cd build && ../configure CC=$CC && pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic && pvs-studio-analyzer trace -- make -j4 && pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic -o PVS-Studio-$CC.log --disableLicenseExpirationCheck && plog-converter -t html -o PVS-Studio-$CC.html PVS-Studio-$CC.log && sendemail -t mail@domain.com -u 'PVS-Studio $CC report, commit:$TRAVIS_COMMIT' -m 'PVS-Studio $CC report, commit:$TRAVIS_COMMIT' -s smtp.gmail.com:587 -xu $MAIL_USER -xp $MAIL_PASSWORD -o tls=yes -f $MAIL_USER -a PVS-Studio-${CC}.log PVS-Studio-${CC}.html" 

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

ملاحظة : عند بدء تشغيل الحاوية ، يجب عليك تحديد المعلمة --cap-add SYS_PTRACE أو - Sececp-opt seccomp: معلمة غير محصورة ، حيث يتم استخدام استدعاء نظام ptrace لتجميع التتبع.

نقوم بتحميل ملف التكوين في جذر المستودع ونرى أن Travis CI تلقى إخطارًا عن وجود تغييرات في المشروع وبدأنا التجميع تلقائيًا.

يمكن الاطلاع على معلومات مفصلة حول تقدم التجميع والتحقق من محلل في وحدة التحكم.

الصورة 2

بعد الانتهاء من الاختبارات ، سنتلقى حرفين في البريد: أحدهما مع نتائج التحليل الثابت لبناء المشروع باستخدام gcc ، والثاني مع clang ، على التوالي.

باختصار عن نتائج الاختبار


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

الظروف الزائدة في حالة


V590 فكر في فحص تعبير 'ret! = (- 1) && ret == 1'. التعبير مفرط أو يحتوي على خطأ مطبعي. attach.c 107

 #define EOF -1 static struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid) { .... while (getline(&line, &line_bufsz, proc_file) != -1) { ret = sscanf(line, "CapBnd: %llx", &info->capability_mask); if (ret != EOF && ret == 1) // <= { found = true; break; } } .... } 

إذا كان ret == 1 ، فهو بالتأكيد لا يساوي -1 (EOF). التحقق المفرط من الصحة ، ret! = EOF يمكن إزالتها.

تم إصدار تحذيرين آخرين:

  • V590 فكر في فحص تعبير 'ret! = (- 1) && ret == 1'. التعبير مفرط أو يحتوي على خطأ مطبعي. attach.c 579
  • V590 فكر في فحص تعبير 'ret! = (- 1) && ret == 1'. التعبير مفرط أو يحتوي على خطأ مطبعي. attach.c 583

فقدان بت عالية


V784 حجم قناع البت أقل من حجم المعامل الأول. هذا سوف يسبب فقدان أعلى بت. conf.c 1879

 struct mount_opt { char *name; int clear; int flag; }; static void parse_mntopt(char *opt, unsigned long *flags, char **data, size_t size) { struct mount_opt *mo; /* If opt is found in mount_opt, set or clear flags. * Otherwise append it to data. */ for (mo = &mount_opt[0]; mo->name != NULL; mo++) { if (strncmp(opt, mo->name, strlen(mo->name)) == 0) { if (mo->clear) { *flags &= ~mo->flag; // <= } else { *flags |= mo->flag; } return; } } .... } 

ضمن Linux ، يعد long متغيرًا صحيحًا 64 بت ، علامة mo>> متغير عدد صحيح 32 بت. إن استخدام علامة mo>> كقناع نقطي سينتج عنه فقد 32 بت. صب ضمني من قناع البت إلى متغير عدد صحيح 64 بت بعد إجراء انعكاس bitwise. البتات العالية لهذا القناع ستكون صفرا.

شرح مع مثال:

 unsigned long long x; unsigned y; .... x &= ~y; 

الصورة 3


الكود الصحيح هو:

 *flags &= ~(unsigned long)(mo->flag); 

أصدر المحلل تحذيرا مماثلا آخر:

  • V784 حجم قناع البت أقل من حجم المعامل الأول. هذا سوف يسبب فقدان أعلى بت. Conf.c 1933

دورة مشبوهة


V612 "عودة" غير مشروطة داخل حلقة. conf.c 3477

 #define lxc_list_for_each(__iterator, __list) \ for (__iterator = (__list)->next; __iterator != __list; \ __iterator = __iterator->next) static bool verify_start_hooks(struct lxc_conf *conf) { char path[PATH_MAX]; struct lxc_list *it; lxc_list_for_each (it, &conf->hooks[LXCHOOK_START]) { int ret; char *hookname = it->elem; ret = snprintf(path, PATH_MAX, "%s%s", conf->rootfs.path ? conf->rootfs.mount : "", hookname); if (ret < 0 || ret >= PATH_MAX) return false; ret = access(path, X_OK); if (ret < 0) { SYSERROR("Start hook \"%s\" not found in container", hookname); return false; } return true; // <= } return true; } 

تبدأ الدورة وعند أول تكرار لها. ربما هذا كان المقصود ، ولكن بعد ذلك يمكن حذف الدورة.

تجاوز حدود مجموعة


V557 مصفوفة غير ممكنة. قد تصل قيمة مؤشر البايتات -1 إلى -1. network.c 2570

 static int lxc_create_network_unpriv_exec(const char *lxcpath, const char *lxcname, struct lxc_netdev *netdev, pid_t pid, unsigned int hooks_version) { int bytes; char buffer[PATH_MAX] = {0}; .... bytes = lxc_read_nointr(pipefd[0], &buffer, PATH_MAX); if (bytes < 0) { SYSERROR("Failed to read from pipe file descriptor"); close(pipefd[0]); } else { buffer[bytes - 1] = '\0'; } .... } 

تتم قراءة بايت من الإخراج في المخزن المؤقت. في حالة حدوث خطأ ، ستُرجع الدالة lxc_read_nointr قيمة سالبة. إذا سارت الأمور على ما يرام ، فسيتم كتابة المحطة الخالية باعتبارها العنصر الأخير. ومع ذلك ، إذا تمت قراءة 0 بايت ، فسيخرج المخزن المؤقت من الحدود ، مما يؤدي إلى سلوك غير محدد.

أصدر المحلل تحذيرا مماثلا آخر:

  • V557 مصفوفة غير ممكنة. قد تصل قيمة مؤشر البايتات -1 إلى -1. network.c 2725

تجاوز سعة المخزن المؤقت


V576 تنسيق غير صحيح. النظر في التحقق من الوسيطة الفعلية الثالثة للدالة 'sscanf'. من الخطير استخدام محددات السلسلة دون تحديد مواصفات العرض. تجاوز سعة المخزن المؤقت أمر ممكن. lxc_unshare.c 205

 static bool lookup_user(const char *oparg, uid_t *uid) { char name[PATH_MAX]; .... if (sscanf(oparg, "%u", uid) < 1) { /* not a uid -- perhaps a username */ if (sscanf(oparg, "%s", name) < 1) // <= { free(buf); return false; } .... } .... } 

قد يكون استخدام sscanf في هذه الحالة أمرًا خطيرًا ، لأنه إذا كان طول المخزن المؤقت لـ oparq أكبر من طول المخزن المؤقت للاسم ، فسوف ينتقل إلى الخارج عند تكوين المخزن المؤقت للاسم .

استنتاج


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

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

روابط مفيدة


  • يمكن العثور على معلومات إضافية حول إطلاق PVS-Studio على نظامي التشغيل Linux و MacOS هنا .
  • يمكنك أن تقرأ عن إنشاء وتكوين واستخدام الحاويات مع محلل ثابت PVS-Studio المثبت هنا .
  • وثائق TravisCI .



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

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


All Articles