PVS-Studio走向云端:GitLab CI / CD

图2

本文是有关在云系统中使用PVS-Studio的一系列出版物的延续。 这次,我们将结合GitLab Inc.的产品GitLab CI一起分析分析仪。 通过将静态分析器集成到CI系统中,您可以在项目的构建阶段之后立即识别错误,这是降低错误检测成本的非常有效的方法。

有关集成到云CI系统中的其他文章列表:


有关所用软件的信息


GitLab是用于管理存储库的在线服务。 您可以通过注册帐户直接在官方网站的浏览器中使用它,也可以在您自己的服务器上安装和部署它。

PVS-Studio是一种静态代码分析工具,旨在检测用C,C ++,C#和Java编写的程序中的错误和潜在漏洞。 它可以在Windows,Linux和macOS上的64位系统上运行,并且可以分析为32位,64位和嵌入式ARM平台设计的代码。 如果这是您第一次尝试使用静态代码分析来测试项目,则建议您阅读有关如何快速查看最有趣的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: 

下载分析器和sendemail实用程序,我们将来会需要它:

 - 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_NAMEPVS_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-converter实用程序。 该程序将分析仪日志转换为各种格式。 为了便于阅读,我们将转换为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 

完整的.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软件中发现的错误示例。

警告N1

V547表达式“ back_size”始终为true。 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-> Capacity) ,则条件的满足将意味着在下面的行中定义的变量back_size始终大于零,这是因为从明显较大的值中减去了明显较小的值,这意味着条件为下面的另一行将始终为true 。 当超出条件是修改数据的代码时,它并不是那么无害。

警告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 << indent)-1 )计算掩码,然后在表达式active&= ...中将其隐式扩展为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条件表达式的一部分始终为false:“!修饰符”。 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函数时会使用该指针,并在此取消引用。 因此,检查!修饰符是没有意义的,因为&&运算符的结果是,我们在评估逻辑表达式时总是会得到false 。 我认为程序员想检查位于修饰符指针中存储的地址处的整数值,但忘了取消引用该指针。

即 在我看来,支票应如下所示:

 if (!*modifiers && ....)  : 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系统。 检查代码将使您能够在构建后立即发现问题,这将有助于在编辑的复杂性和成本仍然很小的情况下解决问题。



如果您想与讲英语的读者分享这篇文章,请使用以下链接:Vladislav Stolyarov。 云中的PVS-Studio:GitLab CI / CD

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


All Articles