在Linux5.1及更新版本中修复损坏的AppleTalk内核模块

2020-08-02 08:46:34

几周前,我在看我的一台经典Mac电脑时,注意到我的Ubuntu18.04 netatalk服务器不再出现在选择器中。如果您不熟悉netatalk,那么它是在类Unix操作系统(如Linux和NetBSD)上运行的Apple filing Protocol(AFP)的实现。它允许其他操作系统充当Mac文件服务器。我使用的2.x版支持古老的AppleTalk协议。这使得它可以在甚至没有安装TCP/IP协议栈的老式Mac电脑上使用。在3.x版中删除了对AppleTalk的支持,这就是我仍然使用2.x的原因。

嗯,…。.为什么根本不会跑呢?我继续尝试重新启动netatalk:

Doug@miniserver:~$sudo服务netatalk为netatalk.service重新启动作业失败,因为控制进程退出并返回错误代码。有关详细信息,请参阅";systemctl status netatalk.service";和";Jouralctl-xe";。

Doug@miniserver:~$systemctl status netatalk.service netatalk.service已加载:已加载(/etc/init.d/netatalk;Generated)ACTIVE:自星期六2020-08-01 10:29:36 PDT以来失败(结果:退出代码);54秒前文档:MAN:SYSTEM D-SYSV-GENERATOR(8)进程:1320 ExecStart=/etc/init.d/netatalk start(代码=已退出,状态=1/。Aug 01 10:29:36微型服务器netatalk[1320]:正在启动Netatalk服务(这将需要一段时间):Socket:协议不支持的地址系列Aug 01 10:29:36微型服务器Netatalk[1320]:Socket:协议不支持的地址系列Aug 01 10:29:36微型服务器Netatalk[1320]:atalkd:无法获取接口,正在退出。Aug 01 10:29:36微型服务器systemd[1]:netatalk.service:控制进程已退出,code=exted status=1 Aug 01 10:29:36微型服务器systemd[1]:netatalk.service:失败,返回结果';退出代码';。Aug 01 10:29:36微型服务器SYSTEM D[1]:无法启动netatalk.service。

这个装置一直都在工作。有什么可能改变的呢?我一直在更新我所有的Ubuntu更新。由于另一个错误,我已经需要手动修补netatalk二进制文件。也许我需要重新贴上补丁?但是没有,netatalk二进制文件还没有更新。不是这样的。

我尝试了一些Google搜索,注意到最近AppleTalk已经打了补丁,所以没有CAP_NET_RAW功能就不能创建原始套接字,所以我使用setcap在atalkd二进制文件上设置了该功能,但这似乎没有修复任何问题,所以我撤消了我测试的所有功能更改。

经过进一步的实验,我意识到没有加载AppleTalk内核模块:

啊哈!这才是真正的问题所在。为什么它不能分配内存呢?我想知道这是不是这台机器特有的东西。为了测试我的理论,我前往我的桌面Linux机器并运行相同的modProbe命令。它失败了,出现了完全相同的错误。

在试图做更多研究之后,我放弃了一段时间,因为我有更重要的事情要担心。搜索关于这类问题的信息有点困难,因为几乎没有人再使用Linux中的AppleTalk网络层。“我们有几十个人!”

我终于回来了,做了更多的故障排除。因为我知道它以前是有效的,所以我尝试在VM中安装各种内核版本。果然,Ubuntu的5.0.0内核运行良好。所以这绝对是一个核心问题,如果我还没有被说服的话。

接下来,我尝试了一些上游内核版本。我将问题范围缩小到5.0和5.1-rc1内核之间的某个时间。然后,我按照Ubuntu维基上的说明将上游内核一分为二,在这些版本之间运行了一次git二等分。我还使用了“make localmodconfig”(然后在“make menuconfig”中启用了AppleTalk)来加快编译过程,因为我注意到大部分编译时间都花在构建我本来不会加载的内核模块上。

一分为二的过程花了相当长的时间。我可能应该想出一种使用QEMU实现自动化的方法,使用的策略与这篇优秀的博客文章中使用的策略类似。但尽管如此,它最终还是确定了从2019年3月开始的这一承诺是问题的开始:

这个COMMIT只是更好地检查atalk_init调用的函数的返回值,并在它们失败时进行适当的清理。特别是,现在检查以前被忽略的这些函数的返回值,以确保它们成功:

进一步检查发现atalk_proc_init才是真正的罪魁祸首。Atalk_proc_init的重构碰巧是对上面链接的提交的前一次提交,它意外地使代码处于一种状态,如果成功,它将返回-ENOMEM而不是0。因此,无论成功还是失败,它总是返回-ENOMEM。这解释了当我尝试插入AppleTalk模块时报告“无法分配内存”错误的原因。

有了这些信息,我做了一件非常恶心的事。我在/tmp中复制了内核模块,并通过在十六进制编辑器中调整字节来破解它。Atalk_proc_init的反汇编显示一行代码,该行代码在EAX寄存器退出之前将值0xFFFFFFF4(-12)加载到EAX寄存器中。ENOMEM被定义为12,所以这一行导致它返回-ENOMEM。我只是简单地破解了这一行,将0加载到EAX中。这基本上使逻辑的工作方式与应用上述两个补丁之前的工作方式相同,因为返回值之前无论如何都会被忽略。

Doug@miniserver:~$sudo inmod/tmp/appletalk.ko inmod:错误:无法插入模块/tmp/appletalk.ko:密钥被服务拒绝

这里的问题是Ubuntu的内核模块是签名的。破解二进制文件后,它不再与其签名匹配。这是我的黑客行为有多丑陋的一个指标。所以我做了一件更难看的事:我把签名完全从模块中剥离出来:

成功!我的dmesg日志报告了一个关于签名失败的错误,从而污染了内核:

Doug@miniserver:~$sudo服务netatalk重新启动Doug@miniserver:~$ps ax|grep atalkd 1698?S 0:00/usr/sbin/atalkd 1716分/0 S+0:00 grep--color=auto atalkd。

现在,我可以将经过黑客攻击的模块版本存储在/lib/module的正确子目录中,重新引导时一切都会自动工作。耶!

不过,这是一个非常难看的修复方法。每当我升级我的内核时,我都必须手动打补丁。也就是说,在补丁到达主线内核之前,我可以希望Ubuntu将补丁放入他们的内核中。我在这里记录二进制补丁的原因是,实际上,内核补丁的发布将永远需要很长时间。我不知道是否有可能说服Ubuntu在这个补丁发布后将其放入他们的内核中。这不是一个高优先级的错误修复,因为现在没有人再使用AppleTalk了。我不想仅仅因为这个愚蠢的原因而将自己限制在一个旧内核上。

因此,这里的第一步是将补丁提交到linux-netdev邮件列表,并将补丁合并到主线内核中。我搜索了邮件列表,发现我不是第一个遇到这个错误的人。实际上,两个不同的人都曾试图修复它,但他们的补丁都因为一些次要的原因而被拒绝:

[Patch]net/AppleTalk:Restore Success Case for atalk_proc_init()在这种情况下,10个月前有人提交了补丁,并被要求调整补丁。然后生活就成了拦路虎(我完全理解),大约9个月后,他终于再试了一次:

他还被要求调整第二个补丁,因为一个小的样式问题(这一点我也能理解)。截至撰写本文时,尚未尝试第三次尝试提交修补程序。

[补丁程序1/2]AppleTalk:修复atalk_proc_init返回路径这是其他人最近的一次尝试,大约在撰写本文时的一周半之前。作者将此补丁与另一个无关的AppleTalk补丁一起提交,补丁集因各种小问题而被拒绝(这也是可以理解的)。截至撰写本文时,第二个版本尚未提交。

显然,现在使用AppleTalk内核模块的人不多,否则人们会为它是如何从5.1内核开始就被破坏而大发雷霆的。我会提供我自己的第三次尝试来合并这个补丁,但由于上个月刚刚进行了两次尝试,我怀疑其中一次很快就会成功。不管怎样,我希望如此。在此期间,我想我将继续对我的内核模块进行二进制修补。

我认为还有一个比这个内核修复更大的长期解决方案。在AppleTalk中使用netatalk的经典Mac用户需要联合起来解决一些问题。Netatalk2.x已经死了,但是它的最后一个版本在Linux上已经崩溃了,至少是它的AppleTalk部分--这是我仍然使用它的唯一原因。也许我们应该分叉netatalk2.x?此外,我非常肯定Linux内核中的AppleTalk子系统充满了小问题。对于大多数需要Netatalk支持的人来说,它工作得足够好了,但我知道它在其他方面有缺陷。也许这个关于可移植用户空间AppleTalk堆栈的项目想法在未来会获得一些吸引力。如果有的话,也许我们可以在netatalk fork中添加对它的支持。

无论如何,我想分享这次故障排除之旅和最终解决方案可能会很有趣。我刚刚测试了从运行System7.1的旧MacIIci连接到我的Ubuntu18.04服务器,一切都再次完美地工作了,…。直到下一次内核升级,也就是说!