PVS-Studio走向云端:CircleCI

图片2

我们继续介绍在云CI系统中使用PVS-Studio静态分析器的系列文章。 今天,我们正在考虑另一项服务-CircleCI。 这次,Kodi媒体播放器将充当分析项目,我们将在其源代码中尝试找到有趣的地方。

注意事项 可以在此处找到有关将PVS-Studio集成到云CI系统中的其他文章:


在我们直接设置和分析分析仪警告之前,让我们先谈谈使用和分析的软件。

CircleCI是基于云的CI服务,用于自动化软件的组装,测试和发布。 它支持在容器以及运行Windows,Linux和macOS的虚拟机中的项目组装。

Kodi是一个免费的开源跨平台媒体播放器。 允许您播放家用计算机以及本地网络或Internet上的音频和视频文件。 它通过安装插件来支持主题和功能。 适用于Windows,Linux,macOS和Android。

PVS-Studio是静态代码分析器,用于搜索用C,C ++,C#和Java编写的代码中的错误和潜在漏洞。 适用于Windows,Linux和macOS。

客制化


转到CircleCI主页,然后单击“注册”按钮

图片1

在下一页上,将提示我们使用GitHub或Bitbucket帐户进行身份验证。 选择GitHub并转到CircleCI应用程序授权页面。

图片3

我们授权该应用程序(绿色按钮“ Authorize circleci”),然后将我们重定向到欢迎页面“ Welcome to CircleCI!”。

图片4

在此页面上,我们可以立即配置将在CircleCI中组装的项目。 我们标记我们的存储库,然后单击“关注”。

添加存储库后,CircleCI将自动开始构建,但是由于 存储库中还没有配置文件-构建任务将失败。

图片5

在添加配置文件之前,我们将添加包含分析仪许可证数据的项目变量。 为此,请单击左侧面板中的“设置”,然后在“组织”组中选择“项目”项,然后单击我们所需项目右侧的齿轮。 将打开一个设置窗口。

图片6

我们对“环境变量”部分感兴趣。 我们进入其中,并创建PVS_USERNAMEPVS_KEY变量,其中包含分析仪的用户名和许可证密钥。

图片7

启动项目构建时,CircleCI从存储库中的文件中沿路径.circleci / config.yml读取任务配置。 添加它。

首先,我们指示分析器将在其中启动的虚拟机的映像。 图像的完整列表在此处提供

version: 2 jobs: build: machine: image: ubuntu-1604:201903-01 

接下来,添加必要的存储库以apt并安装项目依赖项:

 steps: - checkout - run: sudo -- sh -c " add-apt-repository -y ppa:team-xbmc/xbmc-ppa-build-depends && add-apt-repository -y ppa:wsnipex/vaapi && add-apt-repository -y ppa:pulse-eight/libcec && apt-get update" - run: sudo apt-get install -y automake autopoint build-essential cmake curl default-jre gawk gdb gdc gettext git-core gperf libasound2-dev libass-dev libbluray-dev libbz2-dev libcap-dev libcdio-dev libcec4-dev libcrossguid-dev libcurl3 libcurl4-openssl-dev libdbus-1-dev libegl1-mesa-dev libfmt3-dev libfontconfig-dev libfreetype6-dev libfribidi-dev libfstrcmp-dev libgif-dev libgl1-mesa-dev libglu1-mesa-dev libiso9660-dev libjpeg-dev liblcms2-dev libltdl-dev liblzo2-dev libmicrohttpd-dev libmysqlclient-dev libnfs-dev libpcre3-dev libplist-dev libpng-dev libpulse-dev libsmbclient-dev libsqlite3-dev libssl-dev libtag1-dev libtinyxml-dev libtool libudev-dev libusb-dev libva-dev libvdpau-dev libxml2-dev libxmu-dev libxrandr-dev libxrender-dev libxslt1-dev libxt-dev mesa-utils nasm pmount python-dev python-imaging python-sqlite rapidjson-dev swig unzip uuid-dev yasm zip zlib1g-dev wget 

添加PVS-Studio存储库并安装分析器:

 - run: 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 - run: sudo -- sh -c "apt-get update && apt-get install pvs-studio -y" 

让我们收集项目依赖项:

 - run: sudo make -C tools/depends/target/flatbuffers PREFIX=/usr/local 

在程序集目录中生成Makefile:

 - run: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. 

下一步是配置并运行我们项目的静态分析。

首先,使用分析器许可证创建文件。 第二个命令开始编译项目程序集跟踪。

跟踪之后,我们直接开始静态分析。 使用试用许可证时,必须使用以下参数启动分析仪:

--disableLicenseExpirationCheck

最后一个命令将带有分析器结果的文件转换为html报告:

 - run: pvs-studio-analyzer credentials -o PVS.lic ${PVS_USER} ${PVS_KEY} - run: pvs-studio-analyzer trace -- make -j2 -C build/ - run: pvs-studio-analyzer analyze -j2 -l PVS.lic -o PVS-Studio.log --disableLicenseExpirationCheck - run: plog-converter -t html -o PVS-Studio.html PVS-Studio.log 

完成测试后,保存分析器报告:

 - run: mkdir PVS_Result && cp PVS-Studio.* ./PVS_Result/ - store_artifacts: path: ./PVS_Result 

.circleci / config.yml文件的全文:

 version: 2.1 jobs: build: machine: image: ubuntu-1604:201903-01 steps: - checkout - run: sudo -- sh -c " add-apt-repository -y ppa:team-xbmc/xbmc-ppa-build-depends && add-apt-repository -y ppa:wsnipex/vaapi && add-apt-repository -y ppa:pulse-eight/libcec && apt-get update" - run: sudo apt-get install -y automake autopoint build-essential cmake curl default-jre gawk gdb gdc gettext git-core gperf libasound2-dev libass-dev libbluray-dev libbz2-dev libcap-dev libcdio-dev libcec4-dev libcrossguid-dev libcurl3 libcurl4-openssl-dev libdbus-1-dev libegl1-mesa-dev libfmt3-dev libfontconfig-dev libfreetype6-dev libfribidi-dev libfstrcmp-dev libgif-dev libgl1-mesa-dev libglu1-mesa-dev libiso9660-dev libjpeg-dev liblcms2-dev libltdl-dev liblzo2-dev libmicrohttpd-dev libmysqlclient-dev libnfs-dev libpcre3-dev libplist-dev libpng-dev libpulse-dev libsmbclient-dev libsqlite3-dev libssl-dev libtag1-dev libtinyxml-dev libtool libudev-dev libusb-dev libva-dev libvdpau-dev libxml2-dev libxmu-dev libxrandr-dev libxrender-dev libxslt1-dev libxt-dev mesa-utils nasm pmount python-dev python-imaging python-sqlite rapidjson-dev swig unzip uuid-dev yasm zip zlib1g-dev wget - run: 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 - run: sudo -- sh -c "apt-get update && apt-get install pvs-studio -y" - run: sudo make -C tools/depends/target/flatbuffers PREFIX=/usr/local - run: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. - run: pvs-studio-analyzer credentials -o PVS.lic ${PVS_USER} ${PVS_KEY} - run: pvs-studio-analyzer trace -- make -j2 -C build/ - run: pvs-studio-analyzer analyze -j2 -l PVS.lic -o PVS-Studio.log --disableLicenseExpirationCheck - run: plog-converter -t html -o PVS-Studio.html PVS-Studio.log - run: mkdir PVS_Result && cp PVS-Studio.* ./PVS_Result/ - store_artifacts: path: ./PVS_Result 

我们将文件上传到存储库,CircleCI将自动开始项目的组装。

图片12

任务结束后,可以在“工件”选项卡上下载带有分析仪结果的文件。

图片11

分析结果


好了,现在让我们看一下分析仪在工作期间发出的一些警告。

PVS-Studio警告V504分号';'的可能性很大。 “ return”关键字后缺失。 AdvancedSettings.cpp:1476

 void CAdvancedSettings::SetExtraArtwork(const TiXmlElement* arttypes, std::vector<std::string>& artworkMap) { if (!arttypes) return artworkMap.clear(); const TiXmlNode* arttype = arttypes->FirstChild("arttype"); .... } 

根据代码的格式判断,假定以下执行逻辑:

  • 如果arttypes是空指针,则完成该方法的执行;
  • 如果arttypes是一个非零的指针,请清除ArtworkMap矢量,然后执行一些其他操作。

但是,缺少字符“;” 进行了调整; 结果,执行逻辑根本不对应于格式化。 结果,它变为以下内容:

  • 如果arttypes是一个空指针,则清除articleMap向量,然后退出该方法。
  • 如果arttypes是非零指针, 则将执行进一步的操作,但不会清除artsMapMap向量。

通常,没有错误的可能性很小。 而且几乎没有人会本着返回artworkMap.clear()的精神来编写表达式 :)。

PVS-Studio警告

  • V547表达式“ lastsector”始终为false。 udf25.cpp:636
  • V547表达式“ lastsector”始终为false。 udf25.cpp:644
  • V571定期检查。 'if(lastsector)'条件已在第636行中得到验证。udf25.cpp:644

 int udf25::UDFGetAVDP( struct avdp_t *avdp) { .... uint32_t lastsector; .... lastsector = 0; // <= .... for(;;) { .... if( lastsector ) { // <= V547 lbnum = lastsector; terminate = 1; } else { //! @todo Find last sector of the disc (this is optional). if( lastsector ) // <= V547 lbnum = lastsector - 256; else return 0; } } .... } 

请注意标有// <=的地方。 将值0写入lastsector变量,然后将其两次用作if语句的条件表达式。 由于变量的值在循环或这些赋值之间都不会改变,因此两个if语句的分支都不会执行。

确实可能尚未实现必要的功能(请注意todo )。

顺便说一下,正如您所看到的,分析仪立即为该代码发出3条警告。 但是,有时甚至没有几个警告是不够的,并且用户仍然认为分析仪是错误的...一位同事在“ PVS-Studio用户支持的一天文中写道:

PVS-Studio 警告V547表达式'values.size()!= 2'始终为false。 GUIControlSettings.cpp:1174

 bool CGUIControlRangeSetting::OnClick() { .... std::vector<CVariant> values; SettingConstPtr listDefintion = settingList->GetDefinition(); switch (listDefintion->GetType()) { case SettingType::Integer: values.push_back(m_pSlider-> GetIntValue(CGUISliderControl::RangeSelectorLower)); values.push_back(m_pSlider-> GetIntValue(CGUISliderControl::RangeSelectorUpper)); break; case SettingType::Number: values.push_back(m_pSlider-> GetFloatValue(CGUISliderControl::RangeSelectorLower)); values.push_back(m_pSlider-> GetFloatValue(CGUISliderControl::RangeSelectorUpper)); break; default: return false; } if (values.size() != 2) return false; SetValid(CSettingUtils::SetList(settingList, values)); return IsValid(); } 

在这种情况下,检查values.size()!= 2是多余的,因为条件表达式的结果将始终为false 。 实际上,如果执行进入switch语句case分支之一,则将2个元素添加到向量中,并且由于该向量为空,因此其大小将等于2; 否则(执行默认分支时),该方法将退出。

PVS-Studio 警告V547表达式' prio == 0x7fffffff'始终为true。 DBusReserve.cpp:57

 bool CDBusReserve::AcquireDevice(const std::string& device) { .... int prio = INT_MAX; .... res = dbus_bus_request_name( m_conn, service.c_str(), DBUS_NAME_FLAG_DO_NOT_QUEUE | (prio == INT_MAX ? 0 : DBUS_NAME_FLAG_ALLOW_REPLACEMENT), // <= error); .... } 

prio变量使用值INT_MAX初始化,此后在比较prio == INT_MAX中也用在三元运算符中。 但是,在初始化和使用之间,其值不会改变,因此,表达式prio == INT_MAX的值为true ,三元运算符将始终返回0。

PVS-Studio警告

  • V575潜在的空指针被传递到“ memcpy”函数中。 检查第一个参数。 检查行:39,38。DVDOverlayImage.h:39
  • V575潜在的空指针被传递到“ memcpy”函数中。 检查第一个参数。 检查行:44,43。DVDOverlayImage.h:44

 CDVDOverlayImage(const CDVDOverlayImage& src) : CDVDOverlay(src) { Data = (uint8_t*)malloc(src.linesize * src.height); memcpy(data, src.data, src.linesize * src.height); // <= if(src.palette) { palette = (uint32_t*)malloc(src.palette_colors * 4); memcpy(palette, src.palette, src.palette_colors * 4); // <= } .... } 

两种警告的模式相同-通过调用malloc函数获得的指针将在memcpy函数中进一步使用,而无需先检查NULL

有人可能希望在以下关键字中反对这些警告: malloc永远不会返回空指针,如果确实如此,则会让应用程序更好地运行。 这是一个长时间讨论的主题,但是我建议采用一种或多种方式阅读同事的笔记-“ 检查malloc函数返回的重要性为什么很重要 ”。

如果需要,可以将分析器配置为以不认为malloc可以返回空指针的方式运行-则不会出现此类警告。 在此处阅读有关此内容的更多信息。

PVS-Studio警告V522可能会取消引用潜在的空指针“入口”。 检查行:985、981。emu_msvcrt.cpp:985

 struct dirent *dll_readdir(DIR *dirp) { .... struct dirent *entry = NULL; entry = (dirent*) malloc(sizeof(*entry)); if (dirData->curr_index < dirData->items.Size() + 2) { if (dirData->curr_index == 0) strncpy(entry->d_name, ".\0", 2); .... } 

这种情况类似于上述情况。 由于调用malloc而获得的指针被写入到入口变量中,然后使用该指针而不检查NULLentry-> d_name )。

PVS-Studio警告V773在没有释放内存的情况下退出了'progressHandler'指针的可见性范围。 可能发生内存泄漏。 PVRGUIChannelIconUpdater.cpp:94

 void CPVRGUIChannelIconUpdater::SearchAndUpdateMissingChannelIcons() const { .... CPVRGUIProgressHandler* progressHandler = new CPVRGUIProgressHandler(g_localizeStrings.Get(19286)); for (const auto& group : m_groups) { const std::vector<PVRChannelGroupMember> members = group->GetMembers(); int channelIndex = 0; for (const auto& member : members) { progressHandler->UpdateProgress(member.channel->ChannelName(), channelIndex++, members.size()); .... } progressHandler->DestroyProgress(); } 

progressHandler指针包含通过调用运算符new获得的值。 但是,此指针未调用运算符delete ,这会导致内存泄漏。

PVS-Studio警告V557阵列可能超限。 'idx'索引指向数组边界之外。 PlayerCoreFactory.cpp:240

 std::vector<CPlayerCoreConfig *> m_vecPlayerConfigs; bool CPlayerCoreFactory::PlaysVideo(const std::string& player) const { CSingleLock lock(m_section); size_t idx = GetPlayerIndex(player); if (m_vecPlayerConfigs.empty() || idx > m_vecPlayerConfigs.size()) return false; return m_vecPlayerConfigs[idx]->m_bPlaysVideo; } 

由于条件表达式, if语句对向量m_vecPlayerConfigs的大小施加了限制,如果为true,则退出方法。 结果,如果代码执行到达最后一个return语句 ,则向量m_vecPlayerConfigs的大小在指定范围内:[1; idx]。 但是,下面的几行是idx 调用m_vecPlayerConfigs [idx]-> m_bPlaysVideo 。 结果,如果idx等于向量的大小,它将超出允许范围的边界。

最后,看看关于Platinum库代码的一些警告。

PVS-Studio警告V542请考虑检查一个奇怪的类型转换:'bool'到'char *'。 PltCtrlPoint.cpp:1617

 NPT_Result PLT_CtrlPoint::ProcessSubscribeResponse(...) { .... bool subscription = (request.GetMethod().ToUppercase() == "SUBSCRIBE"); .... NPT_String prefix = NPT_String::Format(" PLT_CtrlPoint::ProcessSubscribeResponse %ubscribe for service \"%s\" (result = %d, status code = %d)", (const char*)subscription?"S":"Uns", // <= (const char*)service->GetServiceID(), res, response?response->GetStatusCode():0); .... } 

在这种情况下,操作的优先级会混淆。 const char *不是计算三元运算符( 预订?“ S”:“ Uns” )的结果,而是变量subscription 。 至少看起来很奇怪。

PVS-Studio 警告V560条件表达式的一部分始终为false:c =='\ t'。 NptUtils.cpp:863

 NPT_Result NPT_ParseMimeParameters(....) { .... case NPT_MIME_PARAMETER_PARSER_STATE_NEED_EQUALS: if (c < ' ') return NPT_ERROR_INVALID_SYNTAX; // END or CTLs are invalid if (c == ' ' || c == '\t') continue; // ignore leading whitespace .... } 

空格代码为0x20,制表符代码为0x09。 因此,子表达式c =='\ t'将始终为false ,因为这种情况已经由表达式c <''覆盖(如果为true,则该函数将退出)。

结论


从本文中可以看到,在下一个CI系统(CircleCI)上,我们设法使用PVS-Studio配置项目验证。 我建议您下载并在项目上试用分析仪。 如果您对配置或使用分析仪有任何疑问,请随时给我们写信 ,我们将竭诚为您服务。

当然,还有朋友,你粗心的代码。 :)



如果您想与讲英语的读者分享这篇文章,请使用以下链接:Sergey Vasiliev,Ilya Gainulin。 云中的PVS-Studio:CircleCI

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


All Articles