(取消)补丁回调¶
Livepatch (取消)补丁回调为 livepatch 模块提供了一种机制,以便在内核对象被(取消)打补丁时执行回调函数。 它们可以被认为是一个 强大的功能, 扩展了 livepatching 的能力,包括
安全更新全局数据
“补丁”到 init 和 probe 函数
修补其他无法修补的代码(即汇编)
在大多数情况下,(取消)补丁回调需要与内存屏障和内核同步原语(如互斥锁/自旋锁),甚至 stop_machine() 结合使用,以避免并发问题。
1. 动机¶
回调与现有的内核工具不同
禁用和重新启用补丁时,模块 init/exit 代码不会运行。
模块通知器无法阻止要打补丁的模块加载。
回调是 klp_object 结构的一部分,它们的实现特定于该 klp_object。其他 livepatch 对象可能会也可能不会被打补丁,这与目标 klp_object 的当前状态无关。
2. 回调类型¶
可以为以下 livepatch 操作注册回调
- 预补丁 (Pre-patch)
在 klp_object 打补丁之前
- 后补丁 (Post-patch)
在 klp_object 打补丁之后,并且在所有任务中都处于活动状态
- 预取消补丁 (Pre-unpatch)
在 klp_object 取消补丁之前(即,补丁代码处于活动状态),用于清理后补丁回调资源
- 后取消补丁 (Post-unpatch)
在 klp_object 打补丁之后,所有代码都已恢复,并且没有任务运行补丁代码,用于清理预补丁回调资源
3. 工作原理¶
每个回调都是可选的,省略一个并不妨碍指定任何其他回调。但是,livepatching 核心以对称方式执行处理程序:预补丁回调具有后取消补丁 counterpart,而后补丁回调具有预取消补丁 counterpart。仅当其对应的补丁回调已执行时,才会执行取消补丁回调。典型的用例是将获取和配置资源的补丁处理程序与拆卸和释放这些相同资源的取消补丁处理程序配对。
仅当其宿主 klp_object 已加载时,才会执行回调。对于内核中的 vmlinux 目标,这意味着当启用/禁用 livepatch 时,回调将始终执行。对于补丁目标内核模块,仅当目标模块已加载时,才会执行回调。当模块目标被(取消)加载时,仅当 livepatch 模块已启用时,才会执行其回调。
预补丁回调(如果指定)应返回状态代码(0 表示成功,-ERRNO 表示错误)。错误状态代码向 livepatching 核心指示,当前 klp_object 的补丁不安全,并停止当前的补丁请求。(如果未提供预补丁回调,则假定转换是安全的。)如果预补丁回调返回失败,则内核的模块加载器将
如果 livepatch 在目标代码之后加载,则拒绝加载 livepatch。
或
如果 livepatch 已成功加载,则拒绝加载模块。
如果对象由于预补丁回调失败或任何其他原因而未能打补丁,则不会为给定的 klp_object 执行任何后补丁、预取消补丁或后取消补丁回调。
如果补丁转换被反转,则不会运行任何预取消补丁处理程序(这遵循前面提到的对称性 - 只有在其相应的后补丁回调已执行时,才会发生预取消补丁回调)。
如果对象已成功打补丁,但由于某种原因(例如,如果另一个对象未能打补丁)而从未启动补丁转换,则只会调用后取消补丁回调。
4. 用例¶
演示回调 API 的示例 livepatch 模块可以在 samples/livepatch/ 目录中找到。这些示例已被修改用于 kselftests,可以在 lib/livepatch 目录中找到。
全局数据更新¶
预补丁回调可用于更新全局变量。例如,commit 75ff39ccc1bd (“tcp: make challenge acks less predictable”) 更改了全局 sysctl,以及修补了 tcp_send_challenge_ack() 函数。
在这种情况下,如果我们非常偏执,那么在补丁完成后使用后补丁回调修补数据可能更有意义,以便首先可以将 tcp_send_challenge_ack() 更改为使用 READ_ONCE 读取 sysctl_tcp_challenge_ack_limit。
支持 __init 和 probe 函数补丁¶
虽然 __init 和 probe 函数不能直接进行 livepatching,但可以通过预/后补丁回调实现类似的更新。
The commit 48900cb6af42 (“virtio-net: drop NETIF_F_FRAGLIST”) 更改了 virtnet_probe() 初始化其驱动程序的 net_device 特性的方式。 预/后补丁回调可以迭代所有此类设备,对其 hw_features 值进行类似的更改。(可能需要相应地更新该值的客户端函数。)