云中的PVS-Studio-在Travis CI上运行分析

目前,云CI系统是一项需求很高的服务。 在本文中,我们将告诉您如何使用PVS-Studio中已经提供的工具将源代码分析集成到CI云平台中。 作为示例,我们将使用Travis CI服务。

图片1


为什么我们考虑使用第三方云而不自己做? 原因有很多,主要的原因是SaaS实施是一个昂贵且困难的过程。 实际上,将PVS-Studio分析直接集成到第三方云平台中是简单而琐碎的任务-无论是CircleCI,Travis CI,GitLab之类的开放平台,还是仅在某家公司中使用的特定企业解决方案。 因此,可以说PVS-Studio已经“在云端”可用。 另一个问题是实施和确保对基础设施24/7的访问。 这是一个更复杂的任务。 PVS-Studio不会直接提供自己的云平台来在其上运行分析。

有关二手软件的一些信息


Travis CI是用于构建和测试使用GitHub作为存储的软件的服务。 Travis CI不需要更改程序代码即可使用该服务。 所有设置都在存储库根目录中的文件.travis.yml中进行。

我们将把LXC (Linux容器)作为PVS-Studio的测试项目。 它是操作系统级别的虚拟化系统,用于在一个节点上启动Linux OS的多个实例。

该项目很小,但足以进行演示。 cloc命令的输出:
语言能力
档案
空白
评论
代号
ç
124
11937
6758
50836
C / C ++标题
65岁
1117
3676
3774
注意: LXC开发人员已经在使用Travis CI,因此我们将以他们的配置文件为基础并对其进行编辑。

构型


要开始使用Travis CI,请点击链接并使用Gi​​tHub帐户登录。

图片17

在打开的窗口中,我们需要登录Travis CI。

图片16

授权后,它将重定向到欢迎页面“第一次来这里? 让您开始吧!” 我们在其中提供了简短的说明,然后才能开始进行操作:

  • 启用存储库;
  • 在存储库中添加.travis.yml文件;
  • 开始第一个构建。

图片18

让我们开始执行这些操作。

要将我们的存储库添加到Travis CI中,请通过链接转到配置文件设置,然后按“激活”。

图片19

单击后,将打开一个窗口,以选择可以访问Travis CI应用程序的存储库。

注意:要提供对存储库的访问权限,您的帐户必须具有管理员权限。

图片38

之后,我们选择正确的存储库,单击“批准并安装”按钮确认选择,然后我们将被重定向回配置文件设置页面。

让我们添加一些变量,这些变量将用于创建分析器的许可证文件并发送其报告。 为此,我们将转到设置页面-所需存储库右侧的“设置”按钮。

图片39

设置窗口将打开。

图片41

设置的简要说明;

  • “常规”部分-配置自动启动任务触发器;
  • “自动取消”部分允许配置构建自动取消;
  • “环境变量”部分允许定义既包含开放信息又包含机密信息的环境变量,例如登录信息,ssh-key;
  • “ Cron Jobs”部分是任务运行计划的配置。

在“环境变量”部分,我们将创建变量PVS_USERNAMEPVS_KEY,分别包含静态分析器的用户名和许可证密钥。 如果您没有永久性的PVS-Studio许可证,则可以申请试用许可证

图片5

在这里,我们将创建变量MAIL_USERMAIL_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-report。

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

由于TravisCI不允许您更改电子邮件通知的格式,因此在最后一步,我们将使用sendemail包发送报告:

 - 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--security-opt seccomp:unconfined ,因为ptrace系统调用用于编译器跟踪。

接下来,我们将配置文件加载到存储库的根目录中,并看到Travis CI已收到有关项目更改的通知,并已自动开始构建。

可以在控制台中查看构建进度和分析器检查的详细信息。

图片2

测试结束后,我们将收到两封电子邮件:第一封-带有使用gcc构建项目的静态分析结果,第二封-分别针对clang。

简要介绍检查结果


总的来说,该项目非常干净,分析仪仅发出24个高不确定性警告和46个中等不确定性警告。 让我们看几个有趣的通知:

if中的冗余条件


V590考虑检查'ret!=(-1)&& ret == 1'表达式。 表达式过多或打印错误。 附件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'表达式。 表达式过多或打印错误。 附件579
  • V590考虑检查'ret!=(-1)&& ret == 1'表达式。 表达式过多或打印错误。 附件c.583

高位丢失


V784位掩码的大小小于第一个操作数的大小。 这将导致丢失更高的位。 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位掩码的大小小于第一个操作数的大小。 这将导致丢失更高的位。 1933年

可疑循环


V612循环内无条件的“返回”。 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。 网络.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函数将返回负值。 如果一切顺利,最后一个元素将写入一个终端null。 但是,如果读取0个字节,则索引将超出缓冲区范围,从而导致未定义的行为。

分析仪发出了另一个类似的警告:

  • V557阵列欠载是可能的。 “字节-1”索引的值可能达到-1。 网络.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等。

有用的链接


  • 有关在Linux和MacOS上运行PVS-Studio的更多信息,请单击链接
  • 您还可以通过链接阅读有关使用已安装的PVS-Studio静态代码分析器创建,设置和使用容器的信息。
  • TravisCI文档

Source: https://habr.com/ru/post/zh-CN458064/


All Articles