从这个网页截图中,我们可以获取到以下关于漏洞的关键信息: 1. 漏洞描述: - btrfs_bbio_propagate_error() 函数在处理split BIOS时存在错误传播问题。 - 当split BIOS被发送到镜像设备时,btrfs_bbio_propagate_error() 函数会将错误传递给原始的 btrfs_bio,并告诉错误到上层。然而,在某些情况下,它没有很好地工作。 2. 错误传播问题: - Case 1:立即(或快速)结束 BIOS 时出现错误。 - 当 btrfs_bbio 被发送到镜像设备时,btrfs_bbio_end_io() 函数会在所有镜像 BIOS 完成后被调用。如果 btrfs_bbio 被分割,它将从 btrfs_clone_bioset 和其 end_io 函数是 btrfs_orig_write_end_io。在这种情况下,btrfs_bbio_propagate_error() 函数会访问 orig_bbio 的 bio 上下文来增加错误计数。 - 这在大多数情况下工作得很好。然而,如果 end_io 被调用得足够快,orig_bbio 的剩余部分(在分割后)可能不会在适当的时间被正确设置。由于 bio 上下文是在 orig_bbio(最后一个 btrfs_bio)发送到设备时设置的,这可能会导致 orig_bbio 的完成太晚,从而导致早期 split btrfs_bio 的完成。这将导致 NULL 指针解除引用。 - Case 2:镜像 BIOS 的早期完成。 - btrfs_bbio_propagate_error() 假设 end_io 函数是 split BIOS 中最后一个被调用的。在这种情况下,btrfs_orig_write_end_io() 设置 bio->bi_status 为 BLK_STS_IOERR 通过检查 bio->error。否则,orig_bio 的 bio->error 不会被任何人检查并返回 BLK_STS_OK 给上层。 - 实际上,这并不总是正确的。因为 orig_bio->bi_status 只增加了 orig_bio->errors,条件 "atomic_read(&bio->error) > bio->max_errors" 仍然不会满足,除非只有一个 split btrfs_bio 失败。 - Case 3:未镜像的 BIOS 的完成。 - 与上述情况相反,btrfs_bbio_propagate_error() 在未镜像的 orig_bbio 完成后工作得不好。它将 orig_bbio->bio->status 设置为 btrfs_bio 的错误。但是,这很容易被 orig_bbio 的完成状态覆盖。如果状态是 BLK_STS_OK,上层将不知道失败。 3. 解决方案: - 考虑上述情况,我们可以只将错误状态保存在 orig_bbio(分割后剩余部分)中,因为它总是可用的。此外,保存的错误状态应在所有 split btrfs_bios 完成时(即,bio->pending_ios == 0)被传播。 4. 修复: - 这个补丁引入了 "status" 到 btrfs_bbio,并将 split BIOS 的第一个错误保存到原始 btrfs_bio 的 "status" 变量。当所有 split BIOS 完成时,保存的状态被加载到原始 btrfs_bio 的状态。 5. 补丁内容: - 修改了 btrfs_bio_init() 函数,将 orig_bbio 的 status 设置为 BLK_STS_OK。 - 修改了 btrfs_bbio_propagate_error() 函数,将 split BIOS 的第一个错误保存到 orig_bbio 的 status 中。 通过这些修改,btrfs/146 在分区设备上不再出现 NULL 指针解除引用的问题。