我想检测应用程序执行逻辑中的错误。例如:
- 忘记调用
free()
返回的地址malloc()
- 没有关闭返回的文件句柄
open()
- 无效标志传递给
open()
- 传递给的文件句柄无效
poll()
write()
调用未打开写入的 fd- 将无效标志传递给
open()
例如 open("/etc/fstab", 4) - 调用
close()
无效的 fd - ...
我想还有数百个。
也许该工具可以类似于ftrace
或运行strace
,但包含错误调用的内核日志也足够了。
答案1
忘记调用
free()
返回的地址malloc()
好吧,malloc
这free
不是内核调用!什么malloc()
(这是一个 libc 库函数,普通用户进程代码!)
- 在内存池中查找是否有请求大小的可用块
- 如果是,则将其标记为已使用并将其返回给程序,如果不是,则调用
sbrk
(或等效地,匿名内存的分配很常见)向内核请求一定数量的新虚拟内存页,将它们添加到池中,然后满足程序的要求。
free
只是获取之前通过 ; 返回的 s 块内存malloc
;如果是,则将其标记为内存池中未使用的。 (如果没有,会发生未定义的行为,但大多数 libc 将在此时中止。)大多数实现free
甚至不会尝试将内存返回给操作系统!
现在,如果您想要内存清理,有一些工具(valgrindgcc -fsanitize
等)可以监视这些free
和malloc
调用,甚至可以跟踪 ed 内存块的地址是否malloc
仍然“保存”在程序中的某个位置,或者是否在例如当函数结束时,保存该地址的指针将不再存在,因此没有人会记得内存已分配。那将是一个真正的错误;只是不立即释放内存,或者将释放推迟到程序结束根本不是问题。重点malloc
是您获得的内存可能具有无限的生命周期! (提示:如果您担心这些事情,并且您这样做是正确的,请不要编写 C。使用允许正确跟踪对象生命周期的语言编写。这将是 Rust 或 C++ 等语言,但后者除非有人将 C++ 视为 C 的扩展。我有一些大型程序,在我的 C++ 代码中从未使用过,new
甚至更糟。malloc
智能指针可以解决你肩膀上的很多陷阱,即使是在 C++ 中,这也非常重要。允许您可以进行手动内存控制,但在现代变体中,通过提供零成本对象生命周期跟踪也非常鼓励您不要这样做。 )
没有关闭返回的文件句柄
open()
那不是问题!与内存相比,将文件保持打开状态直到程序结束是完全可以接受的,甚至是明智的。例如,如果您立即放弃文件锁定,则它们将不起作用。并且控制接口需要保持打开状态,直到程序关闭。
再说一遍,如果您担心在程序的控制流中,您可能会打开数千个文件并忘记关闭它们,请不要使用 C 语言编写,而是使用文件句柄具有生命周期并可以关闭文件句柄的语言编写。不再需要时的底层文件描述符。
只是:“有一个文件已打开,尚未关闭”根本不是问题,尤其不是在 POSIX 系统上,并发文件访问是正常的,并且在许多方面甚至是明确定义的。
无效标志传递给
open()
除了返回错误代码之外,你怎么知道这一点?
我的意思是,库检查文件是否可以以“写入+追加”模式打开是很正常的,但如果不能打开也不是问题。
如果您想观察系统调用的任何时间,获取其参数以及它“返回”到用户空间的内容,那么ptrace
系统调用就是您的朋友,例如流行strace
程序所使用的那样。其他选项包括编写 eBPF 探针或 uprobes,它们可用于非常高效甚至“智能过滤”此类事件的日志记录。
传递给的文件句柄无效
poll()
与之前的问题相同,这可能只是您的程序检查文件句柄是否可以被轮询;并非所有(伪)文件系统都是如此。
此外,poll
如果需要的话,实际上也是至少由 glibc 提供的包装函数(符号)的名称,并且“无效”可能与系统调用的“无效”不同poll
。
答案2
其中第一个,未能释放由 分配的内存块malloc()
,并不一定涉及内核 — 内存分配由 C 库处理。瓦尔格林德的内存检查可以检测到这些。
要跟踪内核返回的错误,您可以使用以下命令运行程序strace -Z
(自strace
5.2 起可用):这只会跟踪返回错误的系统调用。您仍然需要对结果进行后处理以查找例如等EBADFD
。EINVAL
请注意,未能调用free()
orclose()
并不一定是错误;无论如何,当程序退出时,这些资源都会被释放,因此在某些情况下,不显式放弃资源是完全可以接受的。
答案3
有各种静态代码分析工具可用于查找此类常见的编码错误。我不能说它是否涵盖了您的所有场景,但 SonarQube 就是这样一种支持 C 语言的工具:https://www.sonarqube.org/features/multi-languages/c
C 语言有数百条 SonarQube 规则:https://rules.sonarsource.com/c
如果静态代码分析不够,您可能需要动态分析来检查正在运行的程序:https://en.wikipedia.org/wiki/Dynamic_program_analysis