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 }