过去几年,各种实时内核补丁技术已在系统管理员中流行起来,他们努力确保系统尽可能高的正常运行时间。
为了使这一过程成为可能,人们需要准备定制补丁,然后通常将其分发给付费客户,有时甚至免费分发给家庭用户。
为什么不能利用正在运行的内核版本和最新可用的源代码之间的差异来自动创建这些补丁?据我了解,可以从中获利最多的服务器内核通常每隔几年才会进行一次重大更改,否则只会收到重大错误修复和安全更新,这似乎使这变得更加容易。同样,如果担心稳定性,那么建立一个系统似乎很简单,运行重要性相对较低的机器的志愿者将首先构建补丁,并自动报告它们的工作情况。
然而,这一切都没有发生。我错过了什么才导致这种情况?
答案1
我们喜欢将运行程序视为创建它们的静态源代码。但它们确实在不断变化。同样,内存中的内核与磁盘上的内核不同。
引用迪杰斯特拉在信中的话“goto 被认为是有害的“……
我的第一句话是,尽管程序员的活动在他构建了正确的程序后就结束了,但在程序控制下发生的过程才是他活动的真正主题,因为正是这个过程必须达到预期的效果;正是这个过程的动态行为必须满足所需的规范。然而,一旦程序制作完成,相应过程的“制作”就委托给机器了。
我的第二句话是,我们的智力更适合掌握静态关系,而我们可视化随时间演变的过程的能力相对发展得相对较差。因此,我们应该(明智的程序员意识到我们的局限性)尽最大努力缩短静态程序和动态过程之间的概念差距,使程序(在文本空间中展开)和进程(在时间上展开)之间的对应关系尽可能简单
由此我推断,将不是从磁盘加载内核的程序或内核放在内存中是个坏主意。如果你不想知道其他事情,能重新启动并最终获得与您现在运行的内核相同的内核。
作为一名系统管理员,您想知道您最终得到的是一个真正的常规内核,而不是一些弗兰肯斯坦的怪物,因为您的实时内核与他们修补的内核有细微的差异。
实时修补确实非常困难。自动生成实时补丁在技术上是不可能的
重要的是要理解程序代码可以有效地重写自身。
int X = 10;
void run(){
X=5;
}
在此代码示例中,X=10
数字从未作为代码执行。编译器将数字10
放置在位置“X”中。在运行时执行第 3 行时,它会替换位置“X”处的值。它实际上会覆盖该值,这意味着数字10
会从正在运行的程序代码中完全消失。
现在我们尝试使用以下命令实时修补此问题:
int X = 20;
void run(){
X=15;
}
X 应该修补什么到 20 或 15?我们应该修补它还是直接留下它?我们不只是在这里更改代码,我们正在更改动态生成的值。您可能会认为,因为它们是动态生成的,所以您可能不需要更改它们,但如果我们不更改它们,我们是否知道 5 或 10 在新代码中仍然是有效值?这不能自动完成!
简而言之,有一些技术和相关工具可以创建实时补丁,但使用它们并测试结果需要专家。发布这些工具并期望家庭用户了解如何使用它们,对于许多家庭用户来说是搞砸他们的系统的好方法。
答案2
任何可用于向正在运行的内核引入补丁的善意机制也可能用于作恶,即向正在运行的内核引入 rootkit。
如果恶意代码是通过适当的其他网络级漏洞引入的,则可能根本没有本地修改的文件可以作为入侵的证据。
另外,诸如此类的事情内核地址空间布局随机化 (KASLR)旨在使对内核的攻击更加困难,也会使实时修补变得复杂,因为它试图保护的信息(内存中各种内核例程的地址)也是成功实时修补所需的信息。也许可以巧妙地管理这些信息,使其仅可用于合法目的,但我认为这是一个棘手的问题。
现代“云”应用程序设计原则强调虚拟化、并行性和多个较小的服务器而不是单个大型服务器,因此单个服务器的正常运行时间可能变得不像以前那么重要了?如果应用程序架构包括一组负载平衡的多台服务器,并且该组的大小是可变的,您可以启动一个新的已打补丁的虚拟服务器并拆除一个旧的未打补丁的虚拟服务器,然后重复此操作,直到所有服务器都已打补丁。已被更新的替换。如果您可以在应用程序堆栈的所有级别执行此操作,则根本不需要实时修补,因为问题可以通过另一种方式解决。
答案3
内核是基于 *nix 的操作系统的核心。它控制操作系统的所有行为。因此,实时修补会阻碍当前正在进行的操作。这就是它无法实时修补的原因。