带有Haiku操作系统代码的PVS-Studio静态分析仪会议的历史可以追溯到2015年。 对于这两个项目的团队来说,这都是一次有趣的实验,并且是有益的经验。 为什么要进行实验? 那时没有针对Linux的分析器,也不会再有一年半的时间。 但是我们团队的发烧友的工作得到了回报:我们与Haiku开发人员会面,并提高了代码质量,为数据库补充了罕见的程序员错误并完善了分析器。 现在,检查Haiku的代码是否有错误非常容易。
引言
我们历史上的英雄是开源的
Haiku操作系统以及用于C,C ++,C#和Java的
PVS-Studio静态分析器。 4.5年前,当我们开始分析项目时,我们只需要使用编译的分析器可执行文件。 解析编译参数,启动预处理器,并行化分析等的整个基础结构。 取自C#中的
Compiler Monitoring UI实用程序,该实用程序已部分移植到Mono平台,以便在Linux上运行。 Haiku项目本身是使用交叉编译器在各种操作系统(Windows除外)下构建的。 我想再次指出Haiku程序集文档的便利性和完整性,并感谢Haiku开发人员在构建项目中的帮助。
现在,分析变得容易得多。 用于构建和分析项目的所有命令的列表如下所示:
cd /opt git clone https:
顺便说一下,项目分析是在Docker容器中进行的。 最近,我们准备了有关该主题的新文档:
在Docker中运行PVS-Studio 。 这可以大大简化某些公司使用静态项目分析技术的过程。
未初始化的变量
V614使用了未初始化的变量“ rval”。 1727年
int auto_fetch(int argc, char *argv[]) { volatile int argpos; int rval;
rval变量在声明时未初始化,因此将其与null值进行比较将导致不确定的结果。 如果情况失败,则
rval变量的未定义值可能会成为
auto_fetch函数的返回值。
V614使用了未初始化的指针“ res”。 命令.c 2873
struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; socklen_t ai_addrlen; char *ai_canonname; struct sockaddr *ai_addr; struct addrinfo *ai_next; }; static int sourceroute(struct addrinfo *ai, char *arg, char **cpp, int *lenp, int *protop, int *optp) { static char buf[1024 + ALIGNBYTES]; char *cp, *cp2, *lsrp, *ep; struct sockaddr_in *_sin; #ifdef INET6 struct sockaddr_in6 *sin6; struct ip6_rthdr *rth; #endif struct addrinfo hints, *res;
使用未初始化变量的类似情况,仅此处是未初始化指针
res 。
V506指向本地变量“归一化”的指针存储在该变量范围之外。 这样的指针将变为无效。 TextView.cpp 5596
void BTextView::_ApplyStyleRange(...., const BFont* font, ....) { if (font != NULL) { BFont normalized = *font; _NormalizeFont(&normalized); font = &normalized; } .... fStyles->SetStyleRange(fromOffset, toOffset, fText->Length(), mode, font, color); }
程序员可能需要通过中间变量对对象进行规范化。 但是现在,在
字体指针中
保存了指向
规范化临时对象的指针,该指针在离开创建该临时对象的作用域之后将被销毁。
V603对象已创建,但未被使用。 如果要调用构造函数,则应使用'this-> BUnicodeChar :: BUnicodeChar(....)'。 UnicodeChar.cpp 27
int8 BUnicodeChar::Type(uint32 c) { BUnicodeChar(); return u_charType(c); }
在C ++程序员中,一个非常普遍的错误是使用构造函数调用来初始化/归零类字段。 在这种情况下,不会修改类的字段,但是会创建此类的一个新的未命名对象,该对象将立即销毁。 不幸的是,有很多这样的地方:
- V603对象已创建,但未被使用。 如果要调用构造函数,则应使用'this-> BUnicodeChar :: BUnicodeChar(....)'。 UnicodeChar.cpp 37
- V603对象已创建,但未被使用。 如果要调用构造函数,则应使用'this-> BUnicodeChar :: BUnicodeChar(....)'。 UnicodeChar.cpp 49
- V603对象已创建,但未被使用。 如果要调用构造函数,则应使用'this-> BUnicodeChar :: BUnicodeChar(....)'。 UnicodeChar.cpp 58
- V603对象已创建,但未被使用。 如果要调用构造函数,则应使用'this-> BUnicodeChar :: BUnicodeChar(....)'。 UnicodeChar.cpp 67
- V603对象已创建,但未被使用。 如果要调用构造函数,则应使用'this-> BUnicodeChar :: BUnicodeChar(....)'。 UnicodeChar.cpp 77
- V603对象已创建,但未被使用。 如果要调用构造函数,则应使用'this-> BUnicodeChar :: BUnicodeChar(....)'。 UnicodeChar.cpp 89
- V603对象已创建,但未被使用。 如果要调用构造函数,则应使用'this-> BUnicodeChar :: BUnicodeChar(....)'。 UnicodeChar.cpp 103
- V603对象已创建,但未被使用。 如果要调用构造函数,则应使用'this-> BUnicodeChar :: BUnicodeChar(....)'。 UnicodeChar.cpp 115
- V603对象已创建,但未被使用。 如果要调用构造函数,则应使用'this-> BUnicodeChar :: BUnicodeChar(....)'。 UnicodeChar.cpp 126
- V603对象已创建,但未被使用。 如果要调用构造函数,则应使用'this-> BUnicodeChar :: BUnicodeChar(....)'。 UnicodeChar.cpp 142
- V603对象已创建,但未被使用。 如果要调用构造函数,则应使用'this-> BUnicodeChar :: BUnicodeChar(....)'。 UnicodeChar.cpp 152
- V603对象已创建,但未被使用。 如果要调用构造函数,则应使用'this-> BUnicodeChar :: BUnicodeChar(....)'。 UnicodeChar.cpp 163
- V603对象已创建,但未被使用。 如果要调用构造函数,则应使用'this-> BUnicodeChar :: BUnicodeChar(....)'。 UnicodeChar.cpp 186
- V603对象已创建,但未被使用。 如果要调用构造函数,则应使用'this-> BUnicodeChar :: BUnicodeChar(....)'。 UnicodeChar.cpp 196
- V603对象已创建,但未被使用。 如果要调用构造函数,则应使用'this-> BUnicodeChar :: BUnicodeChar(....)'。 UnicodeChar.cpp 206
- V603对象已创建,但未被使用。 如果要调用构造函数,则应使用'this-> BUnicodeChar :: BUnicodeChar(....)'。 UnicodeChar.cpp 214
- V603对象已创建,但未被使用。 如果要调用构造函数,则应使用'this-> BUnicodeChar :: BUnicodeChar(....)'。 UnicodeChar.cpp 222
- V603对象已创建,但未被使用。 如果要调用构造函数,则应使用'this-> BUnicodeChar :: BUnicodeChar(....)'。 UnicodeChar.cpp 230
V670未初始化的类成员'fPatternHandler'用于初始化'fInternal'成员。 请记住,成员是按照类内声明的顺序进行初始化的。 Painter.cpp 184
Painter::Painter() : fInternal(fPatternHandler), .... fPatternHandler(), .... { .... }; class Painter { .... private: mutable PainterAggInterface fInternal;
错误初始化的另一个示例。 按照在类本身中声明的顺序初始化类字段。 在此示例中,将首先使用未初始化的值
fPatternHandler初始化
fInternal字段。
可疑的#define
V523'then '语句等效于'else'语句。 subr_gtaskqueue.c 191
#define TQ_LOCK(tq) \ do { \ if ((tq)->tq_spin) \ mtx_lock_spin(&(tq)->tq_mutex); \ else \ mtx_lock(&(tq)->tq_mutex); \ } while (0) #define TQ_ASSERT_LOCKED(tq) mtx_assert(&(tq)->tq_mutex, MA_OWNED) #define TQ_UNLOCK(tq) \ do { \ if ((tq)->tq_spin) \ mtx_unlock_spin(&(tq)->tq_mutex); \ else \ mtx_unlock(&(tq)->tq_mutex); \ } while (0) void grouptask_block(struct grouptask *grouptask) { .... TQ_LOCK(queue); gtask->ta_flags |= TASK_NOENQUEUE; gtaskqueue_drain_locked(queue, gtask); TQ_UNLOCK(queue); }
除非您查看预处理器的结果,否则代码片段看起来并不可疑:
void grouptask_block(struct grouptask *grouptask) { .... do { if ((queue)->tq_spin) mtx_lock(&(queue)->tq_mutex); else mtx_lock(&(queue)->tq_mutex); } while (0); gtask->ta_flags |= 0x4; gtaskqueue_drain_locked(queue, gtask); do { if ((queue)->tq_spin) mtx_unlock(&(queue)->tq_mutex); else mtx_unlock(&(queue)->tq_mutex); } while (0); }
分析器是正确的
-if和
else分支相同。 但是
mtx_lock_spin和
mtx_unlock_spin函数
去哪里了 ? 宏
TQ_LOCK ,
TQ_UNLOCK和
grouptask_block函数
是在同一文件
中声明的,并且几乎并排声明,但是在某处存在替换。
对文件内容的搜索仅
找到具有以下内容的
mutex.h :
#define mtx_lock_spin(x) mtx_lock(x) #define mtx_unlock_spin(x) mtx_unlock(x)
对于项目开发人员而言,这种替换是否正确是值得检查的。 我在Linux上检查了该项目,这种替换对我来说似乎很可疑。
自由功能的错误
V575空指针传递到“免费”功能。 检查第一个参数。 setmime.cpp 727
void MimeType::_PurgeProperties() { fShort.Truncate(0); fLong.Truncate(0); fPrefApp.Truncate(0); fPrefAppSig.Truncate(0); fSniffRule.Truncate(0); delete fSmallIcon; fSmallIcon = NULL; delete fBigIcon; fBigIcon = NULL; fVectorIcon = NULL;
您可以将空指针传递给
free函数,但是这样的输入显然是可疑的。 因此,分析器发现了混乱的代码行。 首先,您必须使用指针
fVectorIcon释放内存,然后才将其设置为
NULL 。
V575空指针传递到“免费”功能。 检查第一个参数。 driver_settings.cpp 461
static settings_handle * load_driver_settings_from_file(int file, const char *driverName) { .... handle = new_settings(text, driverName); if (handle != NULL) {
将空指针显式传递给
free函数的另一个示例。 可以删除此行,因为 成功接收到指针后,函数将退出。
V575空指针传递到“免费”功能。 检查第一个参数。 PackageFileHeapWriter.cpp 166
void* _GetBuffer() { .... void* buffer = malloc(fBufferSize); if (buffer == NULL && !fBuffers.AddItem(buffer)) { free(buffer); throw std::bad_alloc(); } return buffer; }
这是一个错误。 必须使用||运算符代替&&运算符。 仅在这种情况下,如果不可能使用
malloc函数分配内存,则将引发
std :: bad_alloc()异常。
删除运算符错误
V611使用“ new T []”运算符分配了内存,但使用“ delete”运算符释放了内存。 考虑检查此代码。 最好使用'delete [] fMsg;'。 错误cpp 65
class Err { public: .... private: char *fMsg; ssize_t fPos; }; void Err::Unset() { delete fMsg;
fMsg指针用于分配用于存储字符数组的内存,而
delete运算符用于释放内存,而不是
delete [] 。
V611使用“ new”运算符分配了内存,但使用“ free”功能释放了内存。 考虑检查“ wrapperPool”变量后面的操作逻辑。 vm_page.cpp 3080
status_t vm_page_write_modified_page_range(....) { .... PageWriteWrapper* wrapperPool = new(malloc_flags(allocationFlags)) PageWriteWrapper[maxPages + 1]; PageWriteWrapper** wrappers = new(malloc_flags(allocationFlags)) PageWriteWrapper*[maxPages]; if (wrapperPool == NULL || wrappers == NULL) { free(wrapperPool);
这里的
malloc_flags是创建
malloc的函数。 然后,
placement-new在此处构造对象。 并且由于
PageWriteWrapper类
的实现如下:
class PageWriteWrapper { public: PageWriteWrapper(); ~PageWriteWrapper(); void SetTo(vm_page* page); bool Done(status_t result); private: vm_page* fPage; struct VMCache* fCache; bool fIsActive; }; PageWriteWrapper::PageWriteWrapper() : fIsActive(false) { } PageWriteWrapper::~PageWriteWrapper() { if (fIsActive) panic("page write wrapper going out of scope but isn't completed"); }
那么由于使用了
free函数来释放内存,此类的对象析构函数将不会被调用。
V611使用“ new T []”运算符分配了内存,但使用“ delete”运算符释放了内存。 考虑检查此代码。 最好使用'delete [] fOutBuffer;'。 检查行:26,45。PCL6Rasterizer.h 26
class PCL6Rasterizer : public Rasterizer { public: .... ~PCL6Rasterizer() { delete fOutBuffer; fOutBuffer = NULL; } .... virtual void InitializeBuffer() { fOutBuffer = new uchar[fOutBufferSize]; } private: uchar* fOutBuffer; int fOutBufferSize; };
使用
delete运算符而不是
delete []是一个非常常见的错误。 编写课程时犯错误的最简单方法是 析构函数代码通常位于远离内存分配位置的位置。 在这里,程序员错误地通过指针
fOutBuffer释放了析构函数中的内存。
V772为空指针调用“删除”运算符将导致未定义的行为。 哈希表.cpp 207
void Hashtable::MakeEmpty(int8 keyMode,int8 valueMode) { .... for (entry = fTable[index]; entry; entry = next) { switch (keyMode) { case HASH_EMPTY_DELETE:
除了在
delete /
delete []和
free之间选择错误之外,您还可以通过另一种方式向程序添加未定义的行为-尝试通过指向未定义类型
(void *)的指针清除内存。
没有返回值的功能
V591非无效函数应返回一个值。 h 228
BReference& operator=(const BReference<const Type>& other) { fReference = other.fReference; }
重写的赋值运算符没有足够的返回值。 在这种情况下,操作员将返回一个随机值,这可能会导致奇怪的错误。
此类的其他代码段中的类似问题:
- V591非无效函数应返回一个值。 h 233
- V591非无效函数应返回一个值。 h 239
V591非无效函数应返回一个值。 main.c 1010
void errx(int, const char *, ...) ; char * getoptionvalue(const char *name) { struct option *c; if (name == NULL) errx(1, "getoptionvalue() invoked with NULL name"); c = getoption(name); if (c != NULL) return (c->value); errx(1, "getoptionvalue() invoked with unknown option '%s'", name); }
这里的NOTREACHED用户评论没有任何意义。 要为此类情况正确编写代码,必须标记诸如noreturn之类的功能。 没有返回属性:标准和特定于编译器。 首先,编译器将这些属性考虑在内,以通过警告来正确生成代码或通知某些类型的错误。 各种静态分析工具还考虑了属性,以提高分析质量。
异常处理
V596对象已创建,但未使用。 'throw'关键字可能丢失:throw ParseException(FOO); Response.cpp 659
size_t Response::ExtractNumber(BDataIO& stream) { BString string = ExtractString(stream); const char* end; size_t number = strtoul(string.String(), (char**)&end, 10); if (end == NULL || end[0] != '\0') ParseException("Invalid number!"); return number; }
在这里,
throw关键字被意外遗忘了。 因此,不会
引发ParseException ,并且此类的对象在离开范围时将被简单地销毁。 之后,该功能将继续工作,就好像什么都没发生一样,好像输入了正确的数字一样。
V1022指针抛出异常。 考虑改为按值扔掉它。 gensyscallinfos.cpp 316
int main(int argc, char** argv) { try { return Main().Run(argc, argv); } catch (Exception& exception) {
分析器检测到指针抛出了
IOException 。 抛出指针将导致不捕获异常,因此最终通过引用捕获了异常。 另外,使用指针会强制拦截器调用
delete运算符以破坏创建的对象,这也没有完成。
代码的另外两个问题区域:
- V1022指针抛出异常。 考虑改为按值扔掉它。 gensyscallinfos.cpp 347
- V1022指针抛出异常。 考虑改为按值扔掉它。 gensyscallinfos.cpp 413
形式安全
V597编译器可能会删除“ memset”函数调用,该函数调用用于刷新“ f_key”对象。 memset_s()函数应用于擦除私有数据。 dst_api.c 1018
#ifndef SAFE_FREE #define SAFE_FREE(a) \ do{if(a != NULL){memset(a,0, sizeof(*a)); free(a); a=NULL;}} while (0) .... #endif DST_KEY * dst_free_key(DST_KEY *f_key) { if (f_key == NULL) return (f_key); if (f_key->dk_func && f_key->dk_func->destroy) f_key->dk_KEY_struct = f_key->dk_func->destroy(f_key->dk_KEY_struct); else { EREPORT(("dst_free_key(): Unknown key alg %d\n", f_key->dk_alg)); } if (f_key->dk_KEY_struct) { free(f_key->dk_KEY_struct); f_key->dk_KEY_struct = NULL; } if (f_key->dk_key_name) SAFE_FREE(f_key->dk_key_name); SAFE_FREE(f_key); return (NULL); }
分析仪检测到旨在安全清除私有数据的可疑代码。 不幸的是,
SAFE_FREE宏扩展为
memset ,
免费调用和
NULL分配,并不能使代码更安全,因为 在
O2优化期间,编译器将所有这些都删除。
顺便说一句,这与
CWE-14完全不同 :清除代码以清除缓冲区的编译器。
缓冲区实际未清除的完整列表:
- V597编译器可以删除“内存集”函数调用,该函数调用用于刷新“ encoded_block”缓冲区。 memset_s()函数应用于擦除私有数据。 dst_api.c 446
- V597编译器可以删除“内存集”函数调用,该函数调用用于刷新“ key_st”对象。 memset_s()函数应用于擦除私有数据。 dst_api.c 685
- V597编译器可以删除“内存集”函数调用,该函数调用用于刷新“ in_buff”缓冲区。 memset_s()函数应用于擦除私有数据。 dst_api.c 916
- V597编译器可以删除“内存集”函数调用,该函数调用用于刷新“ ce”对象。 memset_s()函数应用于擦除私有数据。 fs_cache.c 1078
无符号比较
V547表达式“剩余<0”始终为false。 无符号类型值永远不会小于0。DwarfFile.cpp 1947
status_t DwarfFile::_UnwindCallFrame(....) { .... uint64 remaining = lengthOffset + length - dataReader.Offset(); if (remaining < 0) return B_BAD_DATA; .... }
分析器发现了一个无符号变量与负值的显式比较。 也许您应该将
其余变量仅
与零进行比较,或者实施溢出检查。
V547表达式“ sleep((unsigned)secs)<0”始终为false。 无符号类型值永远不会小于0。misc.cpp 56
status_t snooze(bigtime_t amount) { if (amount <= 0) return B_OK; int64 secs = amount / 1000000LL; int64 usecs = amount % 1000000LL; if (secs > 0) { if (sleep((unsigned)secs) < 0)
要了解错误是什么,让我们来看一下
sleep和
usleep函数的签名:
extern unsigned int sleep (unsigned int __seconds); extern int usleep (__useconds_t __useconds);
如我们所见,
sleep函数返回一个无符号类型的值,并且在代码中的使用不正确。
危险的指针
V774释放内存后使用了“设备”指针。 xhci.cpp 1572
void XHCI::FreeDevice(Device *device) { uint8 slot = fPortSlots[device->HubPort()]; TRACE("FreeDevice() port %d slot %d\n", device->HubPort(), slot);
设备对象由
删除运算符清除。 这是名为
FreeDevice的函数的逻辑操作。 但是,由于某种原因,要释放代码中的其他资源,再次呼吁已经删除的对象。
这样的代码非常危险,并且会在其他几个地方发生:
- V774释放内存后使用了“自我”指针。 第884章
- V774释放内存后使用了“字符串”指针。 RemoteView.cpp 1269
- V774释放内存后使用了“ bs”指针。 mkntfs.c 4291
- V774释放内存后使用了“ bs”指针。 mkntfs.c 4308
- V774在重新分配内存后使用了“ al”指针。 inode.c 1155
V522可能会
取消引用空指针“数据”。 空指针将传递给“ malo_hal_send_helper”函数。 检查第三个论点。 检查行:350、394。if_malohal.c 350
static int malo_hal_fwload_helper(struct malo_hal *mh, char *helper) { .... error = malo_hal_send_helper(mh, 0, NULL, 0, MALO_NOWAIT);
过程间分析显示了一种情况,其中将
NULL传递给该函数,然后在
memcpy函数中取消引用具有此值的
数据指针。
V773在不释放'inputFileFile'指针的情况下退出了该函数。 可能发生内存泄漏。 command_recompress.cpp 119
int command_recompress(int argc, const char* const* argv) { .... BFile* inputFileFile = new BFile; error = inputFileFile->SetTo(inputPackageFileName, O_RDONLY); if (error != B_OK) { fprintf(stderr, "Error: Failed to open input file \"%s\": %s\n", inputPackageFileName, strerror(error)); return 1; } inputFile = inputFileFile; .... }
PVS-Studio可以检测到内存泄漏 。 发生某种错误时,
此处不会释放
inputFileFile的内存。 有人认为,如果出现错误,您不必费心释放内存-该程序仍将结束。 但这并非总是如此。 正确处理错误并继续工作-这是许多程序的要求。
V595在对nullptr进行验证之前,已使用“ fReply”指针。 检查行:49,52。ReplyBuilder.cpp 49
RPC::CallbackReply* ReplyBuilder::Reply() { fReply->Stream().InsertUInt(fStatusPosition, _HaikuErrorToNFS4(fStatus)); fReply->Stream().InsertUInt(fOpCountPosition, fOpCount); if (fReply == NULL || fReply->Stream().Error() == B_OK) return fReply; else return NULL; }
开发人员在检查指针之前多长时间取消引用指针一次。 诊断
V595几乎始终是项目中警告数量的领导者。 在这段代码中危险地使用
fReply指针。
V595在对nullptr进行验证之前,已使用了'mq'指针。 检查行:782、786。oce_queue.c 782
static void oce_mq_free(struct oce_mq *mq) { POCE_SOFTC sc = (POCE_SOFTC) mq->parent; struct oce_mbx mbx; struct mbx_destroy_common_mq *fwcmd; if (!mq) return; .... }
一个类似的例子。
mg指针在被检查为空值之前要取消引用几行。 项目中有很多类似的地方。 在某些地方,使用和检查指针相距很远,因此本文中包含两个示例。 其余的人将能够在完整的分析器报告中看到开发人员。
杂项错误
V645'strncat '函数调用可能导致'输出'缓冲区溢出。 边界不应包含缓冲区的大小,而应包含可以容纳的多个字符。 命名空间转储.cpp 101
static void dump_acpi_namespace(acpi_ns_device_info *device, char *root, int indenting) { char output[320]; char tabs[255] = ""; .... strlcat(tabs, "|--- ", sizeof(tabs)); .... while (....) { uint32 type = device->acpi->get_object_type(result); snprintf(output, sizeof(output), "%s%s", tabs, result + depth); switch(type) { case ACPI_TYPE_INTEGER: strncat(output, " INTEGER", sizeof(output)); break; case ACPI_TYPE_STRING: strncat(output, " STRING", sizeof(output)); break; .... } .... } .... }
对于
刚接触这些功能的人员来说,
strlcat和
strncat函数之间的区别并不完全明显。
strlcat函数将
整个缓冲区的
大小作为第三个参数,
strncat函数
将 缓冲区中的可用空间 的 大小作为参数,这需要在调用函数之前计算所需的值。 但是开发人员经常忘记或不知道。 将
strncat函数传递
给整个缓冲区
的大小可能会导致缓冲区溢出,因为 函数将把该值视为允许复制的字符数。
strlcat函数
不存在此问题,但是要使其正常工作,必须传递以终端零结尾的行。
带有行的危险场所的完整列表:
- V645'strncat'函数调用可能导致'输出'缓冲区溢出。 边界不应包含缓冲区的大小,而应包含可以容纳的多个字符。 命名空间转储.cpp 104
- V645'strncat'函数调用可能导致'输出'缓冲区溢出。 The bounds should not contain the size of the buffer, but a number of characters it can hold. NamespaceDump.cpp 107
- V645 The 'strncat' function call could lead to the 'output' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold. NamespaceDump.cpp 110
- V645 The 'strncat' function call could lead to the 'output' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold. NamespaceDump.cpp 113
- V645 The 'strncat' function call could lead to the 'output' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold. NamespaceDump.cpp 118
- V645 The 'strncat' function call could lead to the 'output' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold. NamespaceDump.cpp 119
- V645 The 'strncat' function call could lead to the 'output' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold. NamespaceDump.cpp 120
- V645 The 'strncat' function call could lead to the 'output' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold. NamespaceDump.cpp 123
- V645 The 'strncat' function call could lead to the 'output' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold. NamespaceDump.cpp 126
- V645 The 'strncat' function call could lead to the 'output' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold. NamespaceDump.cpp 129
- V645 The 'strncat' function call could lead to the 'output' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold. NamespaceDump.cpp 132
- V645 The 'strncat' function call could lead to the 'output' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold. NamespaceDump.cpp 135
- V645 The 'strncat' function call could lead to the 'output' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold. NamespaceDump.cpp 138
- V645 The 'strncat' function call could lead to the 'output' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold. NamespaceDump.cpp 141
- V645 The 'strncat' function call could lead to the 'output' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold. NamespaceDump.cpp 144
- V645 The 'strncat' function call could lead to the 'features_string' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold. VirtioDevice.cpp 283
- V645 The 'strncat' function call could lead to the 'features_string' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold. VirtioDevice.cpp 284
- V645'strncat'函数调用可能导致'features_string'缓冲区溢出。边界不应包含缓冲区的大小,而应包含可以容纳的多个字符。VirtioDevice.cpp 285
V792位于操作员'|'右边的'SetDecoratorSettings'功能 不管左操作数的值如何,都会被调用。也许最好使用“ ||”。桌面侦听器.cpp 324 class DesktopListener : public DoublyLinkedListLinkImpl<DesktopListener> { public: .... virtual bool SetDecoratorSettings(Window* window, const BMessage& settings) = 0; .... }; bool DesktopObservable::SetDecoratorSettings(Window* window, const BMessage& settings) { if (fWeAreInvoking) return false; InvokeGuard invokeGuard(fWeAreInvoking); bool changed = false; for (DesktopListener* listener = fDesktopListenerList.First(); listener != NULL; listener = fDesktopListenerList.GetNext(listener)) changed = changed | listener->SetDecoratorSettings(window, settings); return changed; }
最有可能的是,运算符'|' 和'||'。这样的错误导致对SetDecoratorSettings函数的不必要的调用。V627考虑检查表达式。sizeof()的参数是扩展为数字的宏。设备72 #define PCI_line_size 0x0c static status_t wb840_open(const char* name, uint32 flags, void** cookie) { .... data->wb_cachesize = gPci->read_pci_config(data->pciInfo->bus, data->pciInfo->device, data->pciInfo->function, PCI_line_size, sizeof(PCI_line_size)) & 0xff; .... }
将值0x0c传递给sizeof运算符看起来很可疑。也许您应该计算某些对象的大小,例如data。V562比较布尔类型值和18:0x12 == IsProfessionalSpdif()。第533章 typedef bool BOOL; virtual BOOL IsProfessionalSpdif() { ... } #define ECHOSTATUS_DSP_DEAD 0x12 ECHOSTATUS CEchoGals::ProcessMixerFunction(....) { .... if ( ECHOSTATUS_DSP_DEAD == IsProfessionalSpdif() )
IsProfessionalSpdif函数将返回bool类型的值,而在这种情况下,该函数的结果将与数字0x12进行比较。结论
我们错过了去年秋天发布的第一个Haiku测试版,因为正忙于发布Java语言的PVS-Studio。但是程序员错误的本质是,除非您寻找它们并且根本不关注代码的质量,否则它们不会到任何地方。开发人员尝试使用Coverity Scan,但是分析的最后一次运行是在大约两年前。这会使Haiku用户不高兴。尽管使用Coverity进行分析是在2014年进行的,但这并没有阻止我们在2015年撰写两篇带有错误评论的大型文章(第1 部分,第2部分)。那些读到最后的人都在等待对Haiku代码的再次审查,而Haiku代码的数量和新的错误一样多。完整的分析器报告将在发布代码审查之前发送给开发人员,因此可以修复一些错误。为了在出版物之间花费时间,建议您在项目上下载并尝试使用PVS-Studio。想尝试Haiku并有任何疑问吗? Haiku开发人员邀请您访问
电报频道 。

如果您想与讲英语的读者分享这篇文章,请使用以下链接:Svyatoslav Razmyslov。
如何在C和C ++中放纵自己。 Haiku OS食谱