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 
gfxGraphiteShaper(gfxFont * aFont)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 
~gfxGraphiteShaper()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*/
GrGetAdvance(rlbox_sandbox_gr & sandbox,tainted_opaque_gr<const void * >,tainted_opaque_gr<uint16_t> t_glyphid)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 
MakeGraphiteLangTag(uint32_t aTag)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 
AddFeature(const uint32_t & aTag,uint32_t & aValue,void * aUserArg)101 static void AddFeature(const 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.)
CountUnicodes(const char16_t * aText,uint32_t aLength)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 
ShapeText(DrawTarget * aDrawTarget,const char16_t * aText,uint32_t aOffset,uint32_t aLength,Script aScript,nsAtom * aLanguage,bool aVertical,RoundingFlags aRounding,gfxShapedText * aShapedText)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) {
192     grLang = MakeGraphiteLangTag(style->languageOverride);
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 
SetGlyphsFromSegment(gfxShapedText * aShapedText,uint32_t aOffset,uint32_t aLength,const char16_t * aText,tainted_opaque_gr<char16_t * > t_aText,tainted_opaque_gr<gr_segment * > aSegment,RoundingFlags aRounding)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*/
GetGraphiteTagForLang(const nsCString & aLang)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>(ArrayLength(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*/
Shutdown()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 }
514