这是我们系列文章中有关将PVS-Studio静态分析仪与云CI系统结合使用的新内容。 今天,我们将看看另一个服务CircleCI。 我们将把Kodi媒体播放器应用程序作为测试项目,看看是否可以在其源代码中找到任何有趣的错误。
注意事项 先前有关将PVS-Studio与云CI系统集成的文章:
在设置工作环境和检查分析报告之前,我想对我们将要使用和检查的软件说几句话。
CircleCI是用于自动化软件构建,测试和部署的云CI服务。 它支持在容器中以及Windows,Linux和macOS上的虚拟机上的项目构建。
Kodi是一个免费的开源跨平台媒体播放器应用程序。 它允许用户播放和查看大多数流媒体,例如来自Internet的视频,音乐,播客和视频,以及来自本地和网络存储媒体的所有常见数字媒体文件。 它支持通过插件使用主题和外观以及功能扩展。 Kodi适用于Windows,Linux,macOS和Android。
PVS-Studio是一种静态分析器,用于检测用C,C ++,C#和Java编写的应用程序的源代码中的错误和潜在漏洞。 分析仪可在Windows,Linux和macOS上运行。
设定
首先,我们需要转到
CircleCI主页,然后单击“注册”
在下一页,我们提供使用GitHub或Bitbucket帐户进行授权的权限。 我们选择GitHub并转到CircleCI授权页面。
授权应用程序后(通过单击绿色按钮“ Authorize circleci”),我们将重定向到“ Welcome to CircleCI!”。 页面:
在这里,我们可以立即指定希望CircleCI构建的项目。 我们勾选存储库,然后单击“关注”。
添加存储库后,CircleCI将自动开始构建过程,但是由于我们的存储库中还没有配置文件,因此构建作业将中止并显示错误消息。
在添加配置文件之前,我们需要添加几个包含分析器许可证数据的变量。 为此,我们单击左侧工具栏上的“设置”,在“组织”部分中选择“项目”,然后单击我们项目名称右边的齿轮按钮。 将会出现一个设置窗口。
我们转到“环境变量”页面。 在这里,我们创建两个变量
PVS_USERNAME和
PVS_KEY ,它们包含用户名和分析器许可证密钥。
开始构建时,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:
然后我们建立依赖关系:
- run: sudo make -C tools/depends/target/flatbuffers PREFIX=/usr/local
之后,我们在构建目录中生成Makefile:
- run: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug ..
下一步是建立并开始项目分析。
首先,我们创建一个分析器许可证文件。 另一个命令将开始跟踪编译器的项目构建。
跟踪之后的下一个命令将按此方式运行分析。 如果使用PVS-Studio的演示版,请使用以下参数启动它:
--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:
此文件上传到资源库后,CircleCI将自动开始构建。
作业完成后,可以在“工件”选项卡上下载带有分析结果的文件。
分析结果
好的,现在让我们看一下分析仪输出的一些警告。
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是空指针,则该方法返回; 否则 ,返回false。
- 如果arttypes是非null指针, 则将清除articleMap向量,然后执行一些操作。
但是缺少';' 字符会破坏一切,实际的执行逻辑如下:
- 如果arttypes是空指针, 则将清除articleMap向量,并返回该方法;
- 如果arttypes是非null指针,则程序将执行接下来要执行的任何操作,但不会清除artsMapMap向量。
简而言之,这种情况看起来确实像个错误。 毕竟,您几乎不希望任何人编写诸如
return 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;
请注意标有
// <=的斑点。 为
lastsector变量分配值0,然后在两个
if语句中将其用作条件表达式。 由于该值在循环或赋值之间都不会改变,因此控制永远不会进入两个
if语句的
else分支。
但是,这也可能意味着开发人员尚未实现预期的功能(请注意
待办事项 )。
顺便说一句,您可能已经注意到,此摘要一次触发了三个警告。 但是,即使对于一个代码的许多警告,对于某些用户来说也无法令人信服,他们将继续相信分析器是错误的……我的一个团队成员在一篇帖子中对此进行了详细讨论:“
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 check在这里是多余的,因为此条件表达式将始终为
false 。 实际上,如果执行进入
switch语句的
case分支之一,则将两个元素添加到向量中,并且由于其最初为空,因此其大小自然将等于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),
将
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);
两种警告具有相同的模式:由
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函数返回的指针存储在
entry变量中,然后使用该变量而无需事先进行空检查(
entry-> 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指针的值。 但是此指针没有
删除运算符。 这意味着内存泄漏。
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语句通过在大小检查条件为true时使方法返回,从而将
m_vecPlayerConfigs向量的大小限制在一定范围内。 结果,当执行到达最后一个
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 *的不是三元运算符(
预订?“ S”:“ Uns” )返回的结果,而是
预订变量。 至少这看起来很奇怪。
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;
空格字符的代码为0x20,制表符字符的代码为0x09。 因此,
c =='\ t'子表达式将始终为
false,因为这种情况已被
c <''检查覆盖(如果为true,则将导致函数返回)。
结论
如本文所示,我们成功地通过PVS-Studio在另一个CI系统(CircleCI)上进行了分析。 我邀请您
下载并在自己的项目上
试用分析仪。 如果您对PVS-Studio的设置或使用有任何疑问,请随时
与我们联系 -我们将很乐意为您提供帮助。
并且,当然,我们希望您没有错误的代码。 :)