问题

为什么特殊阿拉伯字符串能造成iOS系统和OS X下应用崩溃?解决方案是什么?15年重现bug。?

回答
你提到的“特殊阿拉伯字符串能造成iOS系统和OS X下应用崩溃”的bug,在2015年确实引起了广泛的关注,并且被俗称为 "Unicode9 Bug" 或 "ZeroWidth Joiner (ZWJ) Bug"。这是一个相当复杂的bug,涉及Unicode字符编码、字符串渲染以及操作系统处理文本的机制。

下面我将详细解释这个bug的原理,以及当时可能的解决方案:

bug 的原理:Unicode 字符编码和字符串渲染

要理解这个bug,我们需要先了解一些与文本渲染相关的基本概念:

1. Unicode: 是一种全球性的字符编码标准,它为世界上几乎所有的字符都分配了一个唯一的数字码点。这使得计算机能够以一致的方式表示和处理来自不同语言的文本。
2. 阿拉伯字符的连接性: 阿拉伯语是一种从右到左(RTL)书写的语言,其字符的形态会根据其在单词中的位置而变化(孤立形、词首形、词中形、词末形)。为了实现这种连接,阿拉伯字母通常会根据上下文自动连接。
3. 零宽连接符 (ZeroWidth Joiner, ZWJ): ZWJ 是一个特殊的 Unicode 控制字符,它本身不显示任何可见的字符,但它的作用是指示它前后两个字符应该连接(如果可能的话)。
4. 零宽非连接符 (ZeroWidth NonJoiner, ZWNJ): 相反,ZWNJ 是另一个 Unicode 控制字符,它指示它前后两个字符不应该连接(即使它们通常会连接)。
5. 文本布局引擎 (Text Layout Engine): 操作系统和应用程序都需要一个文本布局引擎来处理字符的显示。这个引擎负责解析 Unicode 码点,应用语言特定的规则(如连接性、组合字符、书写方向等),最终将字符渲染到屏幕上。

那么,为什么特殊的阿拉伯字符串会导致崩溃呢?

在2015年左右,许多操作系统的文本布局引擎在处理某些由 ZWJ 字符与其他特定阿拉伯字符组合 的字符串时,存在一个严重的bug。具体来说,当字符串中包含一系列以特定方式排列的阿拉伯字符,并且其中穿插了 ZWJ 时,文本布局引擎可能会陷入一个 无限循环或深度递归 的状态。

这是因为:

错误的上下文判断: 文本布局引擎在解析这些字符串时,会尝试根据 ZWJ 和阿拉伯字符的连接规则来确定每个字符的正确形态。然而,在存在特定组合的情况下,引擎可能会错误地判断某个字符的连接状态,从而触发了进一步的连接尝试。
逻辑缺陷导致循环: 引擎的算法可能没有充分考虑到 ZWJ 与某些阿拉伯字母组合可能产生的复杂连接逻辑,导致在尝试解析这些组合时,不断地重复检查和处理相同的字符序列,最终消耗大量的 CPU 时间和内存,甚至导致堆栈溢出,进而引起应用程序或系统崩溃。
特定渲染链: bug 往往存在于将 Unicode 码点转换为屏幕上可见字形的渲染链中。如果渲染引擎在处理某个特定组合时,内部状态管理出现问题,就可能导致崩溃。

“15年重现bug” 的说法表明,这个bug在当时已经被发现并被某些安全研究人员或开发者复现。通常这类bug会成为安全漏洞的一部分,可能被用于拒绝服务攻击(DenialofService, DoS),因为攻击者可以通过发送一个包含这些特殊字符串的消息或文本来使目标应用程序或设备崩溃。

解决方案

当时的解决方案主要集中在以下几个方面:

1. 操作系统和框架级别的补丁:
Apple 的修复: Apple 作为受影响的操作系统(iOS 和 OS X)的开发者,会通过系统更新来修复这个问题。这些更新会针对其内部的文本渲染库(如 Core Text)进行改进,使其能够正确处理包含 ZWJ 和特定阿拉伯字符的组合,避免陷入无限循环或崩溃。
第三方库更新: 如果应用程序依赖于第三方文本渲染库,这些库也需要进行更新以包含相应的修复。

2. 应用程序级别的解决方案(在系统补丁不可用或需要额外防御时):
输入过滤: 在应用程序接收到用户输入(如文本框、消息等)时,可以实现输入过滤机制,检测并移除或替换掉已知的可能引发崩溃的特殊字符串或 Unicode 字符序列。这通常涉及到对输入的字符串进行扫描,查找 ZWJ 以及与之相关的阿拉伯字符组合。
字符串预处理/标准化: 在将字符串传递给系统进行渲染之前,可以先对其进行标准化处理。这可能包括使用 Unicode 的规范化形式(如 NFC、NFD、NFKC、NFKD)来统一表示字符串,或者更具体地,移除 ZWJ 等控制字符,或者将可能引起问题的字符组合替换成安全的等价表示。
自定义渲染逻辑: 对于非常关键的应用,或者当系统补丁无法满足需求时,开发者可能需要考虑实现自己的文本渲染逻辑,或者使用更健壮的第三方渲染库,这些库可能已经考虑到了这类问题。
限制已知危险字符: 在某些情况下,如果目标用户群体不依赖阿拉伯语的复杂文本排版,可以考虑暂时禁用或限制 ZWJ 等字符的输入,以避免潜在风险。

对于开发者来说,当时最直接和推荐的解决方案是尽快将iOS和OS X系统更新到包含补丁的版本。 如果无法更新系统,则需要在应用程序层面实现输入过滤或预处理,以防止这些特殊字符串被传递到操作系统进行渲染。

为什么是2015年?

Unicode 标准在不断发展,文本渲染技术也在不断进步。在2015年,正是某些特定 Unicode 版本(可能与阿拉伯语字符集或 ZWJ 的新用法相关)的引入,与当时现有的文本布局引擎的实现方式相结合,才暴露了这一问题。随着后续 Unicode 标准的更新和渲染引擎的改进,这类bug发生的可能性会降低,或者被新的标准和实现所解决。然而,由于历史遗留代码和不同的实现方式,类似的文本处理问题可能在未来依然会以新的形式出现。

总而言之,这个bug是Unicode标准复杂性、文本渲染引擎的实现细节以及字符连接规则共同作用的结果。Apple通过系统更新解决了这个问题,而开发者也可以通过输入过滤和预处理来增强应用程序的健壮性。

网友意见

user avatar

先说结论吧:

@Bill Cheng

@麦子龙

@李铁柱

的答案都是片面的。

导致这个 bug 的元凶,在 iOS 6 上是 WebCore,在 OS X 10.8 上是 CoreText。

========

以下是反驳

@Bill Cheng

的观点:

这是那个越狱补丁的源码:

github.com/FilippoBiga/

很明显是给 WebCore 打的补丁。

代码的注释里提到了 WebCore 的 characterRangeCodePath() 函数会在处理 U+0600 ~ U+109F 的字符(其中 U+0600 ~ U+06FF、U+0750 ~ U+077F 是阿拉伯字母)时当成复杂类型,而它调用的 ComplexTextController::adjustGlyphsAndAdvances() 方法在处理这种类型的文字时却出错了。

以下代码来自 WebKit 源码(

github.com/WebKit/webki

):

       float Font::getGlyphsAndAdvancesForComplexText(const TextRun& run, int from, int to, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot forTextEmphasis) const {     float initialAdvance;      ComplexTextController controller(this, run, false, 0, forTextEmphasis);     controller.advance(from);     float beforeWidth = controller.runWidthSoFar();     controller.advance(to, &glyphBuffer);      if (glyphBuffer.isEmpty())         return 0;      float afterWidth = controller.runWidthSoFar();      if (run.rtl()) {         initialAdvance = controller.totalWidth() + controller.finalRoundingWidth() - afterWidth;         glyphBuffer.reverse(0, glyphBuffer.size());     } else         initialAdvance = beforeWidth;      return initialAdvance; }      

它构造了一个 ComplexTextController 类型的对象,这个对象在初始化时崩溃了。

这个类是 WebCore 框架的,根据

@麦子龙

和匿名用户的回答(

zhihu.com/question/2156

),这里并没有更深的调用栈(也不排除调用了内联函数),基本可以认定是 WebCore 的 bug。我并不同意

@麦子龙

说异常栈可能不完整的问题,因为汇编代码都显示出来了,在哪崩溃是很显而易见的。

补丁给出的解决办法是将类型设为自动,也就不会调用上述方法了。但这种解决办法也许会引来其他的 bug(主要是显示方面,对不用阿拉伯语的人应该没影响);只是我对阿拉伯语不了解,也就不妄做判断了。

更直接的依据是

@祝博韬

的回答:iOS 6 的 CoreText 是能正常显示这段文字的。

========

但上述结论也并不代表 CoreText 就没问题,我今天在测试时发现某些字符组合甚至可以让 OS X 10.8 的 Terminal 崩溃,而它是不太可能使用 WebCore 的。

此外,这个 bug 并不是在 iOS 7 和 OS X 10.9 上就不存在了,在某些情况下仍能造成 crash。例如用那串文字做文件名,那么 Finder 在显示这个文件时就会崩溃。这也让我觉得 CoreText 很可疑。

以 Mac Safari 的崩溃异常栈为例,它是在 CoreText 里崩溃的;虽然也用到了 WebCore,但崩溃的位置和 iOS 下不一样:

       Thread 0 Crashed:: Dispatch queue: com.apple.main-thread 0   libvDSP.dylib                  0x00007fff8efdbadb 0x7fff8efbf000 + 117467 1   com.apple.CoreText             0x00007fff8e6c1d5c TRun::TRun(TRun const&, CFRange, TRun::SubrangingStyle) + 850 2   com.apple.CoreText             0x00007fff8e6c19ee CTGlyphRun::CloneRange(CTRun const*, CFRange, TRun::SubrangingStyle) + 142 3   com.apple.CoreText             0x00007fff8e6d0764 TLine::SetLevelRange(CFRange, unsigned char, bool) + 162 4   com.apple.CoreText             0x00007fff8e6d1e2c TLine::SetTrailingWhitespaceLevel(unsigned char) + 70 5   com.apple.CoreText             0x00007fff8e6d1d58 TRunReorder::ReorderRuns(TBidiLevelsProvider const&, TLine&) + 122 6   com.apple.CoreText             0x00007fff8e6d1bfe TTypesetter::FinishLineFill(TLine&, double, double) const + 142 7   com.apple.CoreText             0x00007fff8e6c1775 CTTypesetterCreateLine + 131 8   com.apple.WebCore              0x00007fff8f7f1f1a WebCore::ComplexTextController::collectComplexTextRunsForCharactersCoreText(unsigned short const*, unsigned int, unsigned int, WebCore::SimpleFontData const*) + 1562 9   com.apple.WebCore              0x00007fff8f7f176a WebCore::ComplexTextController::collectComplexTextRuns() + 522      

这是 Mac QQ 崩溃的异常栈,受伤的也是 CoreText:

       Exception Type:  EXC_CRASH (SIGSEGV) Exception Codes: 0x0000000000000000, 0x0000000000000000  Application Specific Information: Performing @selector(paste:) from sender NSMenuItem 0x6b85170  Thread 0:: Dispatch queue: com.apple.main-thread 0   com.apple.CoreText             0x9568e48f TStorageRange::SetStorageSubRange(CFRange) + 307 1   com.apple.CoreText             0x9568cdf7 TRun::TRun(TRun const&, CFRange, TRun::SubrangingStyle) + 853 2   com.apple.CoreText             0x9568ca8e CTGlyphRun::CloneRange(CTRun const*, CFRange, TRun::SubrangingStyle) + 166 3   com.apple.CoreText             0x95699947 TLine::SetLevelRange(CFRange, unsigned char, bool) + 157 4   com.apple.CoreText             0x9569b000 TLine::SetTrailingWhitespaceLevel(unsigned char) + 86 5   com.apple.CoreText             0x9569aee7 TRunReorder::ReorderRuns(TBidiLevelsProvider const&, TLine&) + 107 6   com.apple.CoreText             0x9569ad8c TTypesetter::FinishLineFill(TLine&, double, double) const + 122 7   com.apple.CoreText             0x9568c870 TTypesetter::FillLine(TLine&, CFRange, double, double) const + 90 8   com.apple.CoreText             0x9568c7ff CTTypesetterCreateLine + 185 9   com.apple.AppKit               0x98085a9a -[NSATSLineFragment layoutForStartingGlyphAtIndex:characterIndex:minPosition:maxPosition:lineFragmentRect:] + 802 10  com.apple.AppKit               0x9808452c -[NSATSTypesetter _layoutLineFragmentStartingWithGlyphAtIndex:characterIndex:atPoint:renderingContext:] + 2254 11  com.apple.AppKit               0x980a91cb -[NSATSTypesetter layoutParagraphAtPoint:] + 147 12  com.apple.AppKit               0x98645f21 -[NSTypesetter _layoutGlyphsInLayoutManager:startingAtGlyphIndex:maxNumberOfLineFragments:maxCharacterIndex:nextGlyphIndex:nextCharacterIndex:] + 3403 13  com.apple.AppKit               0x980a813a -[NSTypesetter layoutCharactersInRange:forLayoutManager:maximumNumberOfLineFragments:] + 215 14  com.apple.AppKit               0x980a8011 -[NSATSTypesetter layoutCharactersInRange:forLayoutManager:maximumNumberOfLineFragments:] + 1185 15  com.apple.AppKit               0x980a69c5 -[NSLayoutManager(NSPrivate) _fillLayoutHoleForCharacterRange:desiredNumberOfLines:isSoft:] + 738 16  com.apple.AppKit               0x980d9f1e _NSFastFillAllLayoutHolesForGlyphRange + 227 17  com.apple.AppKit               0x980a4f95 -[NSLayoutManager textContainerForGlyphAtIndex:effectiveRange:] + 198 18  com.apple.AppKit               0x97ee4a80 -[NSTextView(NSPrivate) _ensureLayoutCompleteToEndOfCharacterRange:] + 125 19  com.apple.AppKit               0x97ee442b -[NSTextView(NSSharing) didChangeText] + 159 20  com.apple.AppKit               0x97ee1f71 -[NSTextView insertText:replacementRange:] + 2261 21  com.tencent.qq                 0x003ed850 -[TXTypingTextView insertText:replacementRange:] + 163 22  com.apple.AppKit               0x985b57f0 -[NSTextView insertText:] + 319 23  com.tencent.qq                 0x003cfd6c -[TXBaseTextView AddAttributedString:] + 69 24  com.tencent.qq                 0x003d2a0c -[TXBaseTextView ReadFromPasteborad:] + 392 25  com.tencent.qq                 0x003ee97f -[TXTypingTextView paste:] + 98 26  libobjc.A.dylib                0x96dbc5d3 -[NSObject performSelector:withObject:] + 70 27  com.apple.AppKit               0x98110ad2 -[NSApplication sendAction:to:from:] + 436 28  com.apple.AppKit               0x9824d2fc -[NSMenuItem _corePerformAction] + 529 29  com.apple.AppKit               0x9824cf8b -[NSCarbonMenuImpl performActionWithHighlightingForItemAtIndex:] + 163 30  com.apple.AppKit               0x9824c614 -[NSMenu _performActionWithHighlightingForItemAtIndex:sendAccessibilityNotification:] + 79 31  com.apple.AppKit               0x9824c5c0 -[NSMenu _performActionWithHighlightingForItemAtIndex:] + 48 32  com.apple.AppKit               0x9824bac5 -[NSMenu performKeyEquivalent:] + 306 33  com.tencent.qq                 0x0053233e -[TXMenu performKeyEquivalent:] + 503 34  com.apple.AppKit               0x9824ae7c -[NSApplication _handleKeyEquivalent:] + 915 35  com.apple.AppKit               0x98100de1 -[NSApplication sendEvent:] + 5512 36  com.apple.AppKit               0x9801a62c -[NSApplication run] + 951 37  com.apple.AppKit               0x97fbd5f6 NSApplicationMain + 1053 38  com.tencent.qq                 0x000028b5 start + 53      

这就说明在 OS X 10.8 上,CoreText 才是真凶。

因此 iOS 和 OS X 上各有不同的 bug,而不能想当然地认为凶手只有一个。

类似的话题

  • 回答
    你提到的“特殊阿拉伯字符串能造成iOS系统和OS X下应用崩溃”的bug,在2015年确实引起了广泛的关注,并且被俗称为 "Unicode9 Bug" 或 "ZeroWidth Joiner (ZWJ) Bug"。这是一个相当复杂的bug,涉及Unicode字符编码、字符串渲染以及操作系统处理文本的.............
  • 回答
    在《哈利波特与阿兹卡班的囚徒》的结尾,当哈利和赫敏利用时间转换器回到过去,去拯救小天狼星布莱克和鹰头马身有翼兽巴克比克时,邓布利多并没有选择和他们一起回到过去。这背后其实有多重原因,而且这些原因都紧密地围绕着时间转换器的使用规则以及邓布利多作为一位深思熟虑的巫师的责任感。首先,最核心的限制在于时间转.............
  • 回答
    与其他闪米特语族成员相比,阿拉伯语在某些方面的演变确实保留了一些古老的特征,使其在闪米特语族内部显得独树一帜。要深入理解这一点,我们需要从语音、语法和词汇几个层面来细致地剖析。首先,在语音层面,阿拉伯语的某些读音可以说是对原始闪米特语(ProtoSemitic)语音系统的一种“活化石”。 喉音的.............
  • 回答
    刚看完《王国 北方的阿信》,你好奇这是什么“特产”?这可不是什么吃的,也不是当地的纪念品,而是一部韩国拍摄的 电视剧!这么说吧,如果你喜欢那种有历史厚重感,讲述普通人在时代洪流中如何挣扎求生,又充满韧性和希望的故事,那么《王国 北方的阿信》绝对是你能找到的“特产”中的精品。它是什么?首先,要说清楚《.............
  • 回答
    外交部消息证实,中国、俄罗斯和巴基斯坦三国就阿富汗问题互派的特使,近期一同踏上了喀布尔的土地,并与阿富汗临时政府的要员们进行了会晤。这一动态,绝非简单的外交礼节性访问,它向外界释放出一系列值得深入解读的信号,尤其是在当前阿富汗局势复杂多变的背景下。首先,这标志着中国、俄罗斯、巴基斯坦三国在阿富汗问题.............
  • 回答
    最近隔壁卖特仑苏的阿姨又开始说教了,一口一个“本科有个屁用”,说现在大学毕业生的收入还不如她家那卖煎饼的儿子。听得我耳朵都快起茧子了。她那话怎么说呢,硬是把一本正经的人生规划给说得七零八落,让人听了心里也跟着打鼓:这挤破了头皮上大学,到底是为了啥?是真没用,还是我们自己没弄明白?首先,咱们得承认,阿.............
  • 回答
    太好了,你也中了这类游戏的“毒”!文明6那种宏大的战略体验,饥荒那种在恶劣环境中生存的刺激,以及以撒那种Roguelike的重复可玩性和随机性,这几种元素组合起来,确实能让人沉迷得不行。要说类似这些游戏的,我脑子里马上就蹦出几个方向,希望能帮你找到下一款让你废寝忘食的好游戏。1. 融合了“生存+建造.............
  • 回答
    绿谷制药的九期一(甘露特钠胶囊)在中国医保目录中的成功进入,无疑是阿尔茨海默病治疗领域的一个重要里程碑,其意义深远且复杂,可以从多个层面进行分析:一、 对患者及其家庭的直接意义: 减轻经济负担,提高药物可及性: 这是最直接、最显而易见的意义。九期一此前价格不菲,许多家庭难以负担。纳入医保意味着患.............
  • 回答
    这个问题确实触及了一些关于舆论引导、信息传播和平台审核的敏感点,要详细地讲清楚,需要从几个层面来分析。简单来说,不是平台“故意”偏袒哪个城市,而是信息内容本身所触及的议题敏感度和潜在的社会影响力的不同,以及它们在内容传播中的“引爆点”的差异。首先,我们需要明确“限流”是怎么回事。知乎作为一个知识问答.............
  • 回答
    您提出的这个问题非常重要且具有启发性。事实上,关于特殊儿童(在这里我们更倾向于使用“有特殊需求儿童”或“发展性障碍儿童”等更具尊重性的称谓,涵盖范围更广)在双高知家庭中出现的现象,确实是社会观察和研究中一个值得探讨的议题。但需要明确的是,这并不意味着双高知家庭“更容易生出”有特殊需求的孩子,也不是说.............
  • 回答
    人之所以会对某些特殊声音感到恐惧,这背后其实是一个相当复杂且多层面的原因,涉及到我们的生理构造、心理机制、过往经历,甚至我们祖先的进化遗迹。这可不是一句简单的“不喜欢”就能概括的。一、生物本能:危险信号的放大器最根本的,很多让我们感到恐惧的声音,其实是人类在漫长进化过程中形成的对危险的本能反应。想象.............
  • 回答
    这个问题很有意思,涉及到军事训练、装备适配以及实战需求等多个层面。简单来说,很多特殊兵种设身高上限是为了更好地适应装备和环境,而伞兵有身高下限则主要关乎安全和训练效率。为什么很多特殊兵种设身高上限?这里说的“特殊兵种”通常指的是那些对体能、灵活性、隐蔽性以及装备适应性要求极高的单位,比如: 特种.............
  • 回答
    .......
  • 回答
    要说清楚《汉书》为何废掉了《世家》这个特殊的传记体例,得从它所处的时代背景、司马迁《史记》的开创性影响,以及班固创作《汉书》的宗旨和时代局限性来一一梳理。首先,我们得理解《世家》在司马迁《史记》中的独特性。《史记》的“世家”:权臣与诸侯的春秋谱系司马迁写《史记》的时候,他面临的最大问题是如何在一个统.............
  • 回答
    说中国人对苏联抱有“特殊情感”,这说法其实挺微妙的,需要拆解来看。这种情感并非单一的,而是混杂着复杂、甚至有些矛盾的情绪,深深烙印在几代中国人的集体记忆中。要理解这份“特殊”,就得回到历史的脉络里。一、 革命的起点与“老大哥”的扶持:首先,咱们得承认,苏联在中国的近代史上扮演了一个极其重要的角色。想.............
  • 回答
    在日本的成人漫画,也就是我们常说的H同人本里,确实会注意到对一些“特殊部位”的处理,尤其是生殖器部分,有时会采用比较粗犷、概括的线条来表现。这背后其实有着多方面的原因,并不是单一的技术或美学选择,而是多种因素共同作用的结果。首先,最直接的一个原因是规避审查和法律风险。在日本,对于露骨的性器官描绘是有.............
  • 回答
    稀硫酸与浓硫酸:一字之差,云泥之别硫酸,这个化学世界中再熟悉不过的名字,它的一言一行,都牵动着无数化学实验的走向。然而,当我们在讨论硫酸的性质时,绝不能忽视一个关键的“度”——浓度。稀硫酸和浓硫酸,仅仅是“稀”与“浓”这两个字的差别,却仿佛是两个截然不同的世界,它们所展现出的性质,简直是天壤之别,让.............
  • 回答
    麻将这玩意儿,从古至今,从东半球玩到西半球,总得有点规矩,要不怎么叫“麻将”呢?你仔细琢磨琢磨,会发现,甭管哪地方的麻将,几乎都给你划拉出这么一条——“你得凑够个啥啥条件,才能胡牌!” 为什么?这可不是随便定的,里面门道多着呢!你想啊,要是真就那么简单,随便摸一张牌,就能“我胡了!”那还有什么意思?.............
  • 回答
    这种差异,与其说是现代编程语言对 `null` 的“深恶痛绝”,不如说是对不同类型错误的不同理解和应对策略。究其根本,是因为 `0` 和 `null` 在概念上、在程序运行过程中以及在开发者意图上,扮演着截然不同的角色。让我们从 `0` 开始聊。数字 `0`,在数学和逻辑上,是一个非常具体、有意义的.............
  • 回答
    中国人对俄罗斯的好感,并非一蹴而就,而是历史、地缘政治、文化、经济以及情感等多重因素长期交织作用的结果。 要想详细解释,我们可以从以下几个方面来剖析:一、 历史渊源与情感连接: “老大哥”的记忆与革命情谊: 新中国成立初期,中苏两国曾是紧密的盟友,苏联在经济、军事、技术等方面提供了巨大的援助,帮.............

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有