我正在尝试了解对memcpy的调用可能会静默失败的情况,因为无效的指针将导致访问冲突/段错误。同样,在指针重叠的情况下也会有问题。除此之外,memcpy调用还会失败吗?或者,我们可以认为它会一直无误地通过。如何验证?
答案 0 :(得分:3)
memcpy
的前提是存储区域不会重叠。如果您违反了该前提条件,则调用undefined behavior。
类似地,如果您读取了源缓冲区的边界或写入了目标缓冲区的边界,则也会调用未定义的行为。这是标准规定的。
当您调用未定义的行为时,您无法预测程序在将来(甚至过去)的行为。它可能会崩溃,它可能会输出奇怪的结果,或者它似乎可以正常工作。
使用valgrind之类的工具对识别程序何时违反各种内存约束(例如,通过缓冲区末尾进行读取或写入,使用未初始化的值,取消引用空指针或执行双精度释放)非常有帮助。
答案 1 :(得分:1)
如果为它提供了不重叠的有效指针,并且没有通过读取/写入操作使缓冲区溢出,则不会失败。如果您执行其中某些操作,它可能仍然不会失败,但是可能会执行意外的操作。它将始终返回目标。
答案 2 :(得分:1)
我正在尝试了解调用memcpy可能会静默失败的情况,因为无效的指针将导致访问冲突/段错误。
通常,当发生访问冲突/段错误时,软件会大声失败。如果为memcpy()
赋予了狡猾的指针或错误的大小(包括“正确的指针但堆已损坏”)(例如,malloc()
/ free()
用于跟踪分配的元数据),则会发生这种情况区域被错误覆盖,导致free()
将应该保留的区域的底层虚拟RAM返还给内核,并导致memcpy()
因访问冲突而失败,因为它应该访问某个区域'已经可以访问了。”
其他情况是外部故障情况。如果操作系统决定将一些数据发送到交换空间,但是在尝试访问交换空间时尝试从交换空间取回数据时从设备读取错误,则操作系统对此无能为力(您的过程并且使用该数据的任何其他过程均无法正确继续)。如果您使用的是ECC RAM,并且内存控制器说您使用的RAM存在不可纠正的错误,则类似。操作系统也可能使用“惰性页面分配”(例如,当您写入内存页面时分配了内存页面,而不是您认为分配它们时分配了内存)和“过度提交”(假装分配的页面超出了可提供的数量) ),这样当memcpy()
写入分配的区域时,操作系统将无法处理它(例如,它触发“ OOM /内存不足杀手”,终止进程以释放一些RAM)。最后,代码可能会被破坏(例如,没有ECC的RAM出现故障,诸如“ Rowhammer”之类的恶意攻击,共享库被破坏等),因此(例如)使用memcpy()
会导致SIGILL
。当然,所有这些都不与memcpy()
本身相关,并且可以很容易地在其他任何地方发生。
此外,在指针重叠的情况下也会出现问题。
是的。 memcpy()
的某些(大多数)实现已优化为复制较大的块(例如,优化为在80x86上使用SSE并一次移动16个字节),如果区域重叠,则数据将受到破坏。 memcpy()
的某些(大多数)实现方式假定它可以沿一个特定的方向复制数据,如果区域以错误的方式重叠(例如,如果该实现方式使用“最低地址优先”的方向,并且目标区域重叠并且位于比源区域高的地址处,然后写入目标位置将覆盖尚未复制的源数据。)
除了这些以外,memcpy调用还有其他失败方法吗?
不,我想我已经涵盖了上面所有可能的失败案例。
或者我们可以认为它会一直无误地通过。如何验证?
对于“重叠区域”问题,应该很容易在memcpy()
周围编写一个包装程序,该包装程序会检测到重叠并产生错误(这样就不会默默地破坏数据)。不幸的是,这只会在运行时发现问题(为时已晚-例如可能在发布并在最终用户的计算机上运行之后)。在某些情况下,使用静态源代码分析器可能很容易检测“重叠区域”,但这些情况很可能是“在发布软件之前通过运行时测试轻松检测到的”情况。
对于某些事情(狡猾的指针,损坏的堆),有一些工具(valgrind)可以检测到问题。不幸的是,它们仅在问题实际发生时才发现问题(并且不会检测到在测试过程中未发生但在最终用户的计算机上运行软件时才发生的问题)。
对于其余部分(操作系统故障和硬件故障),如果您不信任操作系统或硬件,那么您将无法假设任何验证任何内容的代码都可以正常工作。