macOS 沙盒文件限制

2021-08-08 22:51:29

最近在 Twitter 上重新讨论了 macOS 沙箱对应用​​程序可以同时访问的打开文件数量的限制。由于这个限制,应用程序仍然遇到问题,并且在 Apple 之外没有很多技术细节可用,所以我想分享我对它的理解。 macOS 只允许沙盒应用程序一次访问有限数量的文件,但应用程序无法查询它可以打开多少个文件,或者是否接近限制。实际上,此限制取决于计算机中安装的 RAM 量以及其他应用程序打开的文件数。由于此限制仅影响可以批量处理数千个文件的应用程序以及想要这样做的用户,因此尽管它已经影响了 Apple 自己的一些应用程序,但许多用户和开发人员仍然没有意识到这一点。我第一次意识到这个限制是在 2012 年初。我正在开发一个可以激活字体文件的应用程序,虽然大多数用户一次只使用它来处理少数字体,但在限制之后我们很快就收到了用户的支持请求在 OS X 10.7 的更新中引入。然而,在这种情况下,导致问题的原因并不明显。在前几百次激活后,字体激活将开始失败并显示通用错误代码。系统日志报告沙箱拒绝我们应用程序的文件读取数据,但我们的应用程序没有被沙箱化,所以这似乎是一个错误。尽管我们试图通过自己和 Apple 开发人员技术支持的帮助来解决这个问题,但直到几个月后的 WWDC 2012,我们才能够得到为什么会发生这种情况的答案。在 WWDC,我尽快为实验室排队,并最终与一位从事字体激活工作的工程师交谈。在与他分享了一些演示问题的代码后,他能够准确地找到字体守护程序中发生故障的位置。尝试打开字体文件时,守护程序收到无法打开文件的一般错误。然而,他不确定该与谁讨论这个问题,所以他给出了一页详细的笔记,以及其他工程师的建议,以及他们将在哪些实验室。所有工程师都提供了帮助并想弄清这个谜底,但他们都不得不建议其他工程师与之交谈,直到最终我被推荐给安全实验室的一名工程师。天色已晚,实验室即将关闭。我清楚地记得我跑过房间,跑到安全实验室被封锁的部分,我胳膊下夹着笔记本电脑。在那里,我遇到了一位非常乐于助人的工程师,他能够解开这个谜团。 macOS 使用安全范围的书签来授予应用程序访问它们通常无法访问的文件的权限。为了促进这一点,macOS 必须保留允许哪些应用程序访问哪些文件路径的映射,并且出于安全原因,该映射必须存在于内核内存中。内核内存已连接:内核无法访问虚拟内存。四分之一的物理 RAM 为内核保留,在该四分之一内,内核为文件映射分配一个百分比。他从来没有直接说出来,但我的印象是安全团队刚刚得知这最近导致应用程序出现问题,只是因为 iPhotos 团队通知了他们,但他们不知道这导致字体守护程序出现问题.我们的雷达、DTS 票证以及与 Apple 代表的电子邮件未能将问题传达给安全团队。顺便说一句,这就是 WWDC 实验室如此重要和有价值的原因。通常不可能让 Apple 内部的适当工程师注意到问题。即使他们无法解决问题,实验室中的工程师通常也可以提供信息和解决方法。

工程师迟到了,帮我向安全团队和字体团队提交了关于这个问题的雷达,但他警告说,潜在的问题可能不会很快得到解决。苹果内部的应用程序开发人员和其他团队需要解决限制造成的问题。它是 macOS 应用沙箱设计的基础,只有找到一种安全且高效的方式在用户空间中存储此映射时才能修复,这是不太可能的。 Apple 不会为了消除这个限制而抛弃沙箱或损害其安全性。考虑到今天这仍然是一个问题,他是对的。在 Apple 之外,应用沙盒自 2011 年首次亮相以来似乎几乎没有变化。许多例外权利仍然是“暂时的”。当引入这些临时例外时,Apple 鼓励开发人员提交雷达,解释他们为什么需要例外,表明 Apple 将来会扩展沙箱以涵盖需要这些例外的用例,然后删除临时权利。据我所知,虽然沙箱中已经修补了安全漏洞,并添加了新的权利,但沙箱从未扩展到包括这些用例,也从未删除过临时例外,使“临时”有点用词不当。由于从未在 Apple 工作过,我将避免对为什么会这样进行毫无根据的猜测。考虑到此限制仍然影响 Microsoft Office 等备受瞩目的应用程序,我认为 Apple 不会没有动力解决此问题,但考虑到它仍然存在多长时间,直到 Apple 另行宣布,我们不能假设它会消失任何时间很快。应用程序开发人员应该尽可能地解决这个问题。有两种方法可以处理这个问题。第一个是尽快放弃安全范围的书签,第二个是提示用户打开文件夹,而不是文件。可以使用 NSURL 的 stopAccessingSecurityScopedResource 方法放弃安全范围的书签。此方法释放内核中文件的条目。如果您的应用一次只需要访问几个文件,您可以在完成每个文件后立即调用此方法。如果这不起作用,您可以提示用户打开文件夹而不是文件。在 NSOpenPanel 中打开文件夹时,只会在内核内存中添加一个条目,但您的应用程序仍然可以访问该文件夹中的文件。但是,您可能需要将自己限制为使用字符串路径而不是 NSURL 访问这些文件的 API。打开文件夹后,您的应用程序将需要枚举文件夹的内容并提供自己的文件列表供用户选择文件。通过 NSOpenPanel 打开的任何文件都会消耗内核内存。就我而言,由于我在 WWDC 2012 上获得的帮助,我能够了解到字体守护程序在使用 CoreText API 激活字体时正确地放弃了书签,而不是我们使用的新弃用的 ATS API。我们能够切换到 CoreText,但是所有使用 ATS 的 Carbon 应用程序将无法看到所有激活的字体。看起来这些应用程序在收到字体激活通知后,会与字体守护程序通信以使用 ATS 功能获取字体数据,但这些应用程序仍然没有足够快地放弃书签。据我所知,这个问题从未得到解决,但由于 Carbon 从未完全移植到 64 位,Carbon 应用程序的使用量下降,直到 macOS 10.15 中删除了 32 位支持,使问题变得毫无意义。

然而,当时,许多流行的应用程序都是使用 Carbon 编写的,或者至少包含足够的 Carbon 代码以受到此问题的影响,包括 Microsoft Office 和 Adob​​e Creative Suite 的部分内容。当用户尝试激活大量字体时,我们不得不向我们的应用程序添加警报,说明并非所有字体都可以在这些应用程序中使用。一些用户苦苦地抱怨,但我们有一个引人注目的演示来证明问题不是我们的应用程序的错。如果用户简单地将所有字体文件移动到 ~/Library/Fonts 中,他们可以确认这些字体在 TextEdit 中都可用,但在 Word 中不可用。