tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

gfxGraphiteShaper.cpp (18605B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "gfxGraphiteShaper.h"
      7 #include "nsString.h"
      8 #include "gfxContext.h"
      9 #include "gfxFontConstants.h"
     10 #include "gfxTextRun.h"
     11 
     12 #include "graphite2/Font.h"
     13 #include "graphite2/GraphiteExtra.h"
     14 #include "graphite2/Segment.h"
     15 
     16 #include "harfbuzz/hb.h"
     17 
     18 #include "mozilla/ScopeExit.h"
     19 
     20 #include "ThebesRLBox.h"
     21 
     22 #define FloatToFixed(f) (65536 * (f))
     23 #define FixedToFloat(f) ((f) * (1.0 / 65536.0))
     24 // Right shifts of negative (signed) integers are undefined, as are overflows
     25 // when converting unsigned to negative signed integers.
     26 // (If speed were an issue we could make some 2's complement assumptions.)
     27 #define FixedToIntRound(f) \
     28  ((f) > 0 ? ((32768 + (f)) >> 16) : -((32767 - (f)) >> 16))
     29 
     30 #define CopyAndVerifyOrFail(t, cond, failed) \
     31  (t).copy_and_verify([&](auto val) {        \
     32    if (!(cond)) {                           \
     33      *(failed) = true;                      \
     34    }                                        \
     35    return val;                              \
     36  })
     37 
     38 using namespace mozilla;  // for AutoSwap_* types
     39 
     40 /*
     41 * Creation and destruction; on deletion, release any font tables we're holding
     42 */
     43 
     44 gfxGraphiteShaper::gfxGraphiteShaper(gfxFont* aFont)
     45    : gfxFontShaper(aFont),
     46      mGrFace(mFont->GetFontEntry()->GetGrFace()),
     47      mSandbox(mFont->GetFontEntry()->GetGrSandbox()),
     48      mCallback(mFont->GetFontEntry()->GetGrSandboxAdvanceCallbackHandle()),
     49      mFallbackToSmallCaps(false) {
     50  mCallbackData.mFont = aFont;
     51 }
     52 
     53 gfxGraphiteShaper::~gfxGraphiteShaper() {
     54  auto t_mGrFont = rlbox::from_opaque(mGrFont);
     55  if (t_mGrFont) {
     56    sandbox_invoke(*mSandbox, gr_font_destroy, t_mGrFont);
     57  }
     58  mFont->GetFontEntry()->ReleaseGrFace(mGrFace);
     59 }
     60 
     61 /*static*/
     62 thread_local gfxGraphiteShaper::CallbackData*
     63    gfxGraphiteShaper::tl_GrGetAdvanceData = nullptr;
     64 
     65 /*static*/
     66 tainted_opaque_gr<float> gfxGraphiteShaper::GrGetAdvance(
     67    rlbox_sandbox_gr& sandbox,
     68    tainted_opaque_gr<const void*> /* appFontHandle */,
     69    tainted_opaque_gr<uint16_t> t_glyphid) {
     70  CallbackData* cb = tl_GrGetAdvanceData;
     71  if (!cb) {
     72    // GrGetAdvance callback called unexpectedly. Just return safe value.
     73    tainted_gr<float> ret = 0;
     74    return ret.to_opaque();
     75  }
     76  auto glyphid = rlbox::from_opaque(t_glyphid).unverified_safe_because(
     77      "Here the only use of a glyphid is for lookup to get a width. "
     78      "Implementations of GetGlyphWidth in this code base use a hashtable "
     79      "which is robust to unknown keys. So no validation is required.");
     80  tainted_gr<float> ret = FixedToFloat(cb->mFont->GetGlyphWidth(glyphid));
     81  return ret.to_opaque();
     82 }
     83 
     84 static inline uint32_t MakeGraphiteLangTag(uint32_t aTag) {
     85  uint32_t grLangTag = aTag;
     86  // replace trailing space-padding with NULs for graphite
     87  uint32_t mask = 0x000000FF;
     88  while ((grLangTag & mask) == ' ') {
     89    grLangTag &= ~mask;
     90    mask <<= 8;
     91  }
     92  return grLangTag;
     93 }
     94 
     95 struct GrFontFeatures {
     96  tainted_gr<gr_face*> mFace;
     97  tainted_gr<gr_feature_val*> mFeatures;
     98  rlbox_sandbox_gr* mSandbox;
     99 };
    100 
    101 static void AddFeature(uint32_t aTag, uint32_t aValue, void* aUserArg) {
    102  GrFontFeatures* f = static_cast<GrFontFeatures*>(aUserArg);
    103 
    104  tainted_gr<const gr_feature_ref*> fref =
    105      sandbox_invoke(*(f->mSandbox), gr_face_find_fref, f->mFace, aTag);
    106  if (fref) {
    107    sandbox_invoke(*(f->mSandbox), gr_fref_set_feature_value, fref, aValue,
    108                   f->mFeatures);
    109  }
    110 }
    111 
    112 // Count the number of Unicode characters in a UTF-16 string (i.e. surrogate
    113 // pairs are counted as 1, although they are 2 code units).
    114 // (Any isolated surrogates will count 1 each, because in decoding they would
    115 // be replaced by individual U+FFFD REPLACEMENT CHARACTERs.)
    116 static inline size_t CountUnicodes(const char16_t* aText, uint32_t aLength) {
    117  size_t total = 0;
    118  const char16_t* end = aText + aLength;
    119  while (aText < end) {
    120    if (NS_IS_HIGH_SURROGATE(*aText) && aText + 1 < end &&
    121        NS_IS_LOW_SURROGATE(*(aText + 1))) {
    122      aText += 2;
    123    } else {
    124      aText++;
    125    }
    126    total++;
    127  }
    128  return total;
    129 }
    130 
    131 bool gfxGraphiteShaper::ShapeText(DrawTarget* aDrawTarget,
    132                                  const char16_t* aText, uint32_t aOffset,
    133                                  uint32_t aLength, Script aScript,
    134                                  nsAtom* aLanguage, bool aVertical,
    135                                  RoundingFlags aRounding,
    136                                  gfxShapedText* aShapedText) {
    137  const gfxFontStyle* style = mFont->GetStyle();
    138  auto t_mGrFace = rlbox::from_opaque(mGrFace);
    139  auto t_mGrFont = rlbox::from_opaque(mGrFont);
    140 
    141  if (!t_mGrFont) {
    142    if (!t_mGrFace) {
    143      return false;
    144    }
    145 
    146    if (mFont->ProvidesGlyphWidths()) {
    147      auto p_ops = mSandbox->malloc_in_sandbox<gr_font_ops>();
    148      if (!p_ops) {
    149        return false;
    150      }
    151      auto clean_ops = MakeScopeExit([&] { mSandbox->free_in_sandbox(p_ops); });
    152      p_ops->size = sizeof(*p_ops);
    153      p_ops->glyph_advance_x = *mCallback;
    154      p_ops->glyph_advance_y = nullptr;  // vertical text not yet implemented
    155      t_mGrFont = sandbox_invoke(
    156          *mSandbox, gr_make_font_with_ops, mFont->GetAdjustedSize(),
    157          // For security, we do not pass the callback data to this arg, and use
    158          // a TLS var instead. However, gr_make_font_with_ops expects this to
    159          // be a non null ptr, and changes its behavior if it isn't. Therefore,
    160          // we should pass some dummy non null pointer which will be passed to
    161          // the GrGetAdvance callback, but never used. Let's just pass p_ops
    162          // again, as this is a non-null tainted pointer.
    163          p_ops /* mCallbackData */, p_ops, t_mGrFace);
    164    } else {
    165      t_mGrFont = sandbox_invoke(*mSandbox, gr_make_font,
    166                                 mFont->GetAdjustedSize(), t_mGrFace);
    167    }
    168    mGrFont = t_mGrFont.to_opaque();
    169 
    170    if (!t_mGrFont) {
    171      return false;
    172    }
    173 
    174    // determine whether petite-caps falls back to small-caps
    175    if (style->variantCaps != NS_FONT_VARIANT_CAPS_NORMAL) {
    176      switch (style->variantCaps) {
    177        case NS_FONT_VARIANT_CAPS_ALLPETITE:
    178        case NS_FONT_VARIANT_CAPS_PETITECAPS:
    179          bool synLower, synUpper;
    180          mFont->SupportsVariantCaps(aScript, style->variantCaps,
    181                                     mFallbackToSmallCaps, synLower, synUpper);
    182          break;
    183        default:
    184          break;
    185      }
    186    }
    187  }
    188 
    189  gfxFontEntry* entry = mFont->GetFontEntry();
    190  uint32_t grLang = 0;
    191  if (style->languageOverride._0) {
    192    grLang = MakeGraphiteLangTag(style->languageOverride._0);
    193  } else if (entry->mLanguageOverride) {
    194    grLang = MakeGraphiteLangTag(entry->mLanguageOverride);
    195  } else if (aLanguage) {
    196    nsAutoCString langString;
    197    aLanguage->ToUTF8String(langString);
    198    grLang = GetGraphiteTagForLang(langString);
    199  }
    200  tainted_gr<gr_feature_val*> grFeatures =
    201      sandbox_invoke(*mSandbox, gr_face_featureval_for_lang, t_mGrFace, grLang);
    202 
    203  // insert any merged features into Graphite feature list
    204  GrFontFeatures f = {t_mGrFace, grFeatures, mSandbox};
    205  MergeFontFeatures(style, mFont->GetFontEntry()->mFeatureSettings,
    206                    aShapedText->DisableLigatures(),
    207                    mFont->GetFontEntry()->FamilyName(), mFallbackToSmallCaps,
    208                    AddFeature, &f);
    209 
    210  // Graphite shaping doesn't map U+00a0 (nbsp) to space if it is missing
    211  // from the font, so check for that possibility. (Most fonts double-map
    212  // the space glyph to both 0x20 and 0xA0, so this won't often be needed;
    213  // so we don't copy the text until we know it's required.)
    214  nsAutoString transformed;
    215  const char16_t NO_BREAK_SPACE = 0x00a0;
    216  if (!entry->HasCharacter(NO_BREAK_SPACE)) {
    217    nsDependentSubstring src(aText, aLength);
    218    if (src.FindChar(NO_BREAK_SPACE) != kNotFound) {
    219      transformed = src;
    220      transformed.ReplaceChar(NO_BREAK_SPACE, ' ');
    221      aText = transformed.BeginReading();
    222    }
    223  }
    224 
    225  size_t numChars = CountUnicodes(aText, aLength);
    226  gr_bidirtl grBidi = gr_bidirtl(
    227      aShapedText->IsRightToLeft() ? (gr_rtl | gr_nobidi) : gr_nobidi);
    228 
    229  tainted_gr<char16_t*> t_aText =
    230      mSandbox->malloc_in_sandbox<char16_t>(aLength);
    231  if (!t_aText) {
    232    return false;
    233  }
    234  auto clean_txt = MakeScopeExit([&] { mSandbox->free_in_sandbox(t_aText); });
    235 
    236  rlbox::memcpy(*mSandbox, t_aText, aText, aLength * sizeof(char16_t));
    237 
    238  tl_GrGetAdvanceData = &mCallbackData;
    239  auto clean_adv_data = MakeScopeExit([&] { tl_GrGetAdvanceData = nullptr; });
    240 
    241  tainted_gr<gr_segment*> seg =
    242      sandbox_invoke(*mSandbox, gr_make_seg, mGrFont, t_mGrFace, 0, grFeatures,
    243                     gr_utf16, t_aText, numChars, grBidi);
    244 
    245  sandbox_invoke(*mSandbox, gr_featureval_destroy, grFeatures);
    246 
    247  if (!seg) {
    248    return false;
    249  }
    250 
    251  nsresult rv =
    252      SetGlyphsFromSegment(aShapedText, aOffset, aLength, aText,
    253                           t_aText.to_opaque(), seg.to_opaque(), aRounding);
    254 
    255  sandbox_invoke(*mSandbox, gr_seg_destroy, seg);
    256 
    257  return NS_SUCCEEDED(rv);
    258 }
    259 
    260 nsresult gfxGraphiteShaper::SetGlyphsFromSegment(
    261    gfxShapedText* aShapedText, uint32_t aOffset, uint32_t aLength,
    262    const char16_t* aText, tainted_opaque_gr<char16_t*> t_aText,
    263    tainted_opaque_gr<gr_segment*> aSegment, RoundingFlags aRounding) {
    264  typedef gfxShapedText::CompressedGlyph CompressedGlyph;
    265 
    266  int32_t dev2appUnits = aShapedText->GetAppUnitsPerDevUnit();
    267  bool rtl = aShapedText->IsRightToLeft();
    268 
    269  // identify clusters; graphite may have reordered/expanded/ligated glyphs.
    270  tainted_gr<gr_glyph_to_char_association*> data =
    271      sandbox_invoke(*mSandbox, gr_get_glyph_to_char_association, aSegment,
    272                     aLength, rlbox::from_opaque(t_aText));
    273 
    274  if (!data) {
    275    return NS_ERROR_FAILURE;
    276  }
    277 
    278  tainted_gr<gr_glyph_to_char_cluster*> clusters = data->clusters;
    279  tainted_gr<uint16_t*> gids = data->gids;
    280  tainted_gr<float*> xLocs = data->xLocs;
    281  tainted_gr<float*> yLocs = data->yLocs;
    282 
    283  CompressedGlyph* charGlyphs = aShapedText->GetCharacterGlyphs() + aOffset;
    284 
    285  bool roundX = bool(aRounding & RoundingFlags::kRoundX);
    286  bool roundY = bool(aRounding & RoundingFlags::kRoundY);
    287 
    288  bool failedVerify = false;
    289 
    290  // cIndex is primarily used to index into the clusters array which has size
    291  // aLength below. As cIndex is not changing anymore, let's just verify it
    292  // and remove the tainted wrapper.
    293  uint32_t cIndex =
    294      CopyAndVerifyOrFail(data->cIndex, val < aLength, &failedVerify);
    295  if (failedVerify) {
    296    return NS_ERROR_ILLEGAL_VALUE;
    297  }
    298  // now put glyphs into the textrun, one cluster at a time
    299  for (uint32_t i = 0; i <= cIndex; ++i) {
    300    // We makes a local copy of "clusters[i]" which is of type
    301    // tainted_gr<gr_glyph_to_char_cluster> below. We do this intentionally
    302    // rather than taking a reference. Taking a reference with the code
    303    //
    304    // tainted_volatile_gr<gr_glyph_to_char_cluster>& c = clusters[i];
    305    //
    306    // produces a tainted_volatile which means the value can change at any
    307    // moment allowing for possible time-of-check-time-of-use vuln. We thus
    308    // make a local copy to simplify the verification.
    309    tainted_gr<gr_glyph_to_char_cluster> c = clusters[i];
    310 
    311    tainted_gr<float> t_adv;  // total advance of the cluster
    312    if (rtl) {
    313      if (i == 0) {
    314        t_adv = sandbox_invoke(*mSandbox, gr_seg_advance_X, aSegment) -
    315                xLocs[c.baseGlyph];
    316      } else {
    317        t_adv = xLocs[clusters[i - 1].baseGlyph] - xLocs[c.baseGlyph];
    318      }
    319    } else {
    320      if (i == cIndex) {
    321        t_adv = sandbox_invoke(*mSandbox, gr_seg_advance_X, aSegment) -
    322                xLocs[c.baseGlyph];
    323      } else {
    324        t_adv = xLocs[clusters[i + 1].baseGlyph] - xLocs[c.baseGlyph];
    325      }
    326    }
    327 
    328    float adv = t_adv.unverified_safe_because(
    329        "Per Bug 1569464 - this is the advance width of a glyph or cluster of "
    330        "glyphs. There are no a-priori limits on what that might be. Incorrect "
    331        "values will tend to result in bad layout or missing text, or bad "
    332        "nscoord values. But, these will not result in safety issues.");
    333 
    334    // check unexpected offset - offs used to index into aText
    335    uint32_t offs =
    336        CopyAndVerifyOrFail(c.baseChar, val < aLength, &failedVerify);
    337    if (failedVerify) {
    338      return NS_ERROR_ILLEGAL_VALUE;
    339    }
    340 
    341    // Check for default-ignorable char that didn't get filtered, combined,
    342    // etc by the shaping process, and skip it.
    343    auto one_glyph = c.nGlyphs == static_cast<uint32_t>(1);
    344    auto one_char = c.nChars == static_cast<uint32_t>(1);
    345 
    346    if ((one_glyph && one_char)
    347            .unverified_safe_because(
    348                "using this boolean check to decide whether to ignore a "
    349                "character or not. The worst that can happen is a bad "
    350                "rendering.")) {
    351      if (aShapedText->FilterIfIgnorable(aOffset + offs, aText[offs])) {
    352        continue;
    353      }
    354    }
    355 
    356    uint32_t appAdvance = roundX ? NSToIntRound(adv) * dev2appUnits
    357                                 : NSToIntRound(adv * dev2appUnits);
    358 
    359    const char gid_simple_value[] =
    360        "Per Bug 1569464 - these are glyph IDs that can range from 0 to the "
    361        "maximum glyph ID supported by the font. However, out-of-range values "
    362        "here should not lead to safety issues; they would simply result in "
    363        "blank rendering, although this depends on the platform back-end.";
    364 
    365    // gids[c.baseGlyph] is checked and used below. Since this is a
    366    // tainted_volatile, which can change at any moment, we make a local copy
    367    // first to prevent a time-of-check-time-of-use vuln.
    368    uint16_t gid_of_base_glyph =
    369        gids[c.baseGlyph].unverified_safe_because(gid_simple_value);
    370 
    371    const char fast_path[] =
    372        "Even if the number of glyphs set is an incorrect value, the else "
    373        "branch is a more general purpose algorithm which can handle other "
    374        "values of nGlyphs";
    375 
    376    if (one_glyph.unverified_safe_because(fast_path) &&
    377        CompressedGlyph::IsSimpleGlyphID(gid_of_base_glyph) &&
    378        CompressedGlyph::IsSimpleAdvance(appAdvance) &&
    379        charGlyphs[offs].IsClusterStart() &&
    380        (yLocs[c.baseGlyph] == 0).unverified_safe_because(fast_path)) {
    381      charGlyphs[offs].SetSimpleGlyph(appAdvance, gid_of_base_glyph);
    382 
    383    } else {
    384      // not a one-to-one mapping with simple metrics: use DetailedGlyph
    385      AutoTArray<gfxShapedText::DetailedGlyph, 8> details;
    386      float clusterLoc;
    387 
    388      uint32_t glyph_end =
    389          (c.baseGlyph + c.nGlyphs)
    390              .unverified_safe_because(
    391                  "This only controls the total number of glyphs set for this "
    392                  "particular text. Worst that can happen is a bad rendering");
    393 
    394      // check overflow - ensure loop start is before the end
    395      uint32_t glyph_start =
    396          CopyAndVerifyOrFail(c.baseGlyph, val <= glyph_end, &failedVerify);
    397      if (failedVerify) {
    398        return NS_ERROR_ILLEGAL_VALUE;
    399      }
    400 
    401      for (uint32_t j = glyph_start; j < glyph_end; ++j) {
    402        gfxShapedText::DetailedGlyph* d = details.AppendElement();
    403        d->mGlyphID = gids[j].unverified_safe_because(gid_simple_value);
    404 
    405        const char safe_coordinates[] =
    406            "There are no limits on coordinates. Worst case, bad values would "
    407            "force rendering off-screen, but there are no memory safety "
    408            "issues.";
    409 
    410        float yLocs_j = yLocs[j].unverified_safe_because(safe_coordinates);
    411        float xLocs_j = xLocs[j].unverified_safe_because(safe_coordinates);
    412 
    413        d->mOffset.y = roundY ? NSToIntRound(-yLocs_j) * dev2appUnits
    414                              : -yLocs_j * dev2appUnits;
    415        if (j == glyph_start) {
    416          d->mAdvance = appAdvance;
    417          clusterLoc = xLocs_j;
    418        } else {
    419          float dx =
    420              rtl ? (xLocs_j - clusterLoc) : (xLocs_j - clusterLoc - adv);
    421          d->mOffset.x =
    422              roundX ? NSToIntRound(dx) * dev2appUnits : dx * dev2appUnits;
    423          d->mAdvance = 0;
    424        }
    425      }
    426      aShapedText->SetDetailedGlyphs(aOffset + offs, details.Length(),
    427                                     details.Elements());
    428    }
    429 
    430    // check unexpected offset
    431    uint32_t char_end = CopyAndVerifyOrFail(c.baseChar + c.nChars,
    432                                            val <= aLength, &failedVerify);
    433    // check overflow - ensure loop start is before the end
    434    uint32_t char_start =
    435        CopyAndVerifyOrFail(c.baseChar + 1, val <= char_end, &failedVerify);
    436    if (failedVerify) {
    437      return NS_ERROR_ILLEGAL_VALUE;
    438    }
    439 
    440    for (uint32_t j = char_start; j < char_end; ++j) {
    441      CompressedGlyph& g = charGlyphs[j];
    442      NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
    443      g.SetComplex(g.IsClusterStart(), false);
    444    }
    445  }
    446 
    447  sandbox_invoke(*mSandbox, gr_free_char_association, data);
    448  return NS_OK;
    449 }
    450 
    451 // for language tag validation - include list of tags from the IANA registry
    452 #include "gfxLanguageTagList.cpp"
    453 
    454 nsTHashSet<uint32_t>* gfxGraphiteShaper::sLanguageTags;
    455 
    456 /*static*/
    457 uint32_t gfxGraphiteShaper::GetGraphiteTagForLang(const nsCString& aLang) {
    458  int len = aLang.Length();
    459  if (len < 2) {
    460    return 0;
    461  }
    462 
    463  // convert primary language subtag to a left-packed, NUL-padded integer
    464  // for the Graphite API
    465  uint32_t grLang = 0;
    466  for (int i = 0; i < 4; ++i) {
    467    grLang <<= 8;
    468    if (i < len) {
    469      uint8_t ch = aLang[i];
    470      if (ch == '-') {
    471        // found end of primary language subtag, truncate here
    472        len = i;
    473        continue;
    474      }
    475      if (ch < 'a' || ch > 'z') {
    476        // invalid character in tag, so ignore it completely
    477        return 0;
    478      }
    479      grLang += ch;
    480    }
    481  }
    482 
    483  // valid tags must have length = 2 or 3
    484  if (len < 2 || len > 3) {
    485    return 0;
    486  }
    487 
    488  if (!sLanguageTags) {
    489    // store the registered IANA tags in a hash for convenient validation
    490    sLanguageTags = new nsTHashSet<uint32_t>(std::size(sLanguageTagList));
    491    for (const uint32_t* tag = sLanguageTagList; *tag != 0; ++tag) {
    492      sLanguageTags->Insert(*tag);
    493    }
    494  }
    495 
    496  // only accept tags known in the IANA registry
    497  if (sLanguageTags->Contains(grLang)) {
    498    return grLang;
    499  }
    500 
    501  return 0;
    502 }
    503 
    504 /*static*/
    505 void gfxGraphiteShaper::Shutdown() {
    506 #ifdef NS_FREE_PERMANENT_DATA
    507  if (sLanguageTags) {
    508    sLanguageTags->Clear();
    509    delete sLanguageTags;
    510    sLanguageTags = nullptr;
    511  }
    512 #endif
    513 }