PVS-Studio in the Clouds - تشغيل التحليل على Travis CI

في الوقت الحالي ، تعد أنظمة Cloud CI خدمة مطلوبة بشدة. في هذه المقالة ، سوف نخبرك بكيفية دمج تحليل شفرة المصدر في منصة سحابة CI مع الأدوات المتوفرة بالفعل في PVS-Studio. على سبيل المثال ، سوف نستخدم خدمة 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 ؛
  • يعد قسم "Cron Jobs" بمثابة تكوين لجدول تشغيل المهام.

في قسم "متغيرات البيئة" ، سنقوم بإنشاء متغيرات 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 

لتشغيل PVS-Studio في حاوية ، فلننشئها مسبقًا باستخدام 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 ، والثانية - لكلانج ، على التوالي.

باختصار حول التحقق من النتائج


بشكل عام ، كان المشروع نظيفًا تمامًا ، حيث أصدر المحلل 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 بت بعد الانعكاس في اتجاه البت. قد تضيع بت عالية من هذا القناع.

سأريها باستخدام مثال:

 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 .

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


All Articles