QtBase  v6.3.1
hb-sanitize.hh
Go to the documentation of this file.
1 /*
2  * Copyright © 2007,2008,2009,2010 Red Hat, Inc.
3  * Copyright © 2012,2018 Google, Inc.
4  *
5  * This is part of HarfBuzz, a text shaping library.
6  *
7  * Permission is hereby granted, without written agreement and without
8  * license or royalty fees, to use, copy, modify, and distribute this
9  * software and its documentation for any purpose, provided that the
10  * above copyright notice and the following two paragraphs appear in
11  * all copies of this software.
12  *
13  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17  * DAMAGE.
18  *
19  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
22  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24  *
25  * Red Hat Author(s): Behdad Esfahbod
26  * Google Author(s): Behdad Esfahbod
27  */
28 
29 #ifndef HB_SANITIZE_HH
30 #define HB_SANITIZE_HH
31 
32 #include "hb.hh"
33 #include "hb-blob.hh"
34 #include "hb-dispatch.hh"
35 
36 
37 /*
38  * Sanitize
39  *
40  *
41  * === Introduction ===
42  *
43  * The sanitize machinery is at the core of our zero-cost font loading. We
44  * mmap() font file into memory and create a blob out of it. Font subtables
45  * are returned as a readonly sub-blob of the main font blob. These table
46  * blobs are then sanitized before use, to ensure invalid memory access does
47  * not happen. The toplevel sanitize API use is like, eg. to load the 'head'
48  * table:
49  *
50  * hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table<OT::head> (face);
51  *
52  * The blob then can be converted to a head table struct with:
53  *
54  * const head *head_table = head_blob->as<head> ();
55  *
56  * What the reference_table does is, to call hb_face_reference_table() to load
57  * the table blob, sanitize it and return either the sanitized blob, or empty
58  * blob if sanitization failed. The blob->as() function returns the null
59  * object of its template type argument if the blob is empty. Otherwise, it
60  * just casts the blob contents to the desired type.
61  *
62  * Sanitizing a blob of data with a type T works as follows (with minor
63  * simplification):
64  *
65  * - Cast blob content to T*, call sanitize() method of it,
66  * - If sanitize succeeded, return blob.
67  * - Otherwise, if blob is not writable, try making it writable,
68  * or copy if cannot be made writable in-place,
69  * - Call sanitize() again. Return blob if sanitize succeeded.
70  * - Return empty blob otherwise.
71  *
72  *
73  * === The sanitize() contract ===
74  *
75  * The sanitize() method of each object type shall return true if it's safe to
76  * call other methods of the object, and %false otherwise.
77  *
78  * Note that what sanitize() checks for might align with what the specification
79  * describes as valid table data, but does not have to be. In particular, we
80  * do NOT want to be pedantic and concern ourselves with validity checks that
81  * are irrelevant to our use of the table. On the contrary, we want to be
82  * lenient with error handling and accept invalid data to the extent that it
83  * does not impose extra burden on us.
84  *
85  * Based on the sanitize contract, one can see that what we check for depends
86  * on how we use the data in other table methods. Ie. if other table methods
87  * assume that offsets do NOT point out of the table data block, then that's
88  * something sanitize() must check for (GSUB/GPOS/GDEF/etc work this way). On
89  * the other hand, if other methods do such checks themselves, then sanitize()
90  * does not have to bother with them (glyf/local work this way). The choice
91  * depends on the table structure and sanitize() performance. For example, to
92  * check glyf/loca offsets in sanitize() would cost O(num-glyphs). We try hard
93  * to avoid such costs during font loading. By postponing such checks to the
94  * actual glyph loading, we reduce the sanitize cost to O(1) and total runtime
95  * cost to O(used-glyphs). As such, this is preferred.
96  *
97  * The same argument can be made re GSUB/GPOS/GDEF, but there, the table
98  * structure is so complicated that by checking all offsets at sanitize() time,
99  * we make the code much simpler in other methods, as offsets and referenced
100  * objects do not need to be validated at each use site.
101  */
102 
103 /* This limits sanitizing time on really broken fonts. */
104 #ifndef HB_SANITIZE_MAX_EDITS
105 #define HB_SANITIZE_MAX_EDITS 32
106 #endif
107 #ifndef HB_SANITIZE_MAX_OPS_FACTOR
108 #define HB_SANITIZE_MAX_OPS_FACTOR 64
109 #endif
110 #ifndef HB_SANITIZE_MAX_OPS_MIN
111 #define HB_SANITIZE_MAX_OPS_MIN 16384
112 #endif
113 #ifndef HB_SANITIZE_MAX_OPS_MAX
114 #define HB_SANITIZE_MAX_OPS_MAX 0x3FFFFFFF
115 #endif
116 #ifndef HB_SANITIZE_MAX_SUBTABLES
117 #define HB_SANITIZE_MAX_SUBTABLES 0x4000
118 #endif
119 
121  hb_dispatch_context_t<hb_sanitize_context_t, bool, HB_DEBUG_SANITIZE>
122 {
124  start (nullptr), end (nullptr),
125  max_ops (0), max_subtables (0),
126  recursion_depth (0),
127  writable (false), edit_count (0),
128  blob (nullptr),
129  num_glyphs (65536),
130  num_glyphs_set (false) {}
131 
132  const char *get_name () { return "SANITIZE"; }
133  template <typename T, typename F>
134  bool may_dispatch (const T *obj HB_UNUSED, const F *format)
135  { return format->sanitize (this); }
136  static return_t default_return_value () { return true; }
137  static return_t no_dispatch_return_value () { return false; }
138  bool stop_sublookup_iteration (const return_t r) const { return !r; }
139 
140  bool visit_subtables (unsigned count)
141  {
142  max_subtables += count;
143  return max_subtables < HB_SANITIZE_MAX_SUBTABLES;
144  }
145 
146  private:
147  template <typename T, typename ...Ts> auto
148  _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN
149  ( obj.sanitize (this, std::forward<Ts> (ds)...) )
150  template <typename T, typename ...Ts> auto
151  _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN
152  ( obj.dispatch (this, std::forward<Ts> (ds)...) )
153  public:
154  template <typename T, typename ...Ts> auto
155  dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN
156  ( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) )
157 
158 
159  void init (hb_blob_t *b)
160  {
161  this->blob = hb_blob_reference (b);
162  this->writable = false;
163  }
164 
165  void set_num_glyphs (unsigned int num_glyphs_)
166  {
167  num_glyphs = num_glyphs_;
168  num_glyphs_set = true;
169  }
170  unsigned int get_num_glyphs () { return num_glyphs; }
171 
172  void set_max_ops (int max_ops_) { max_ops = max_ops_; }
173 
174  template <typename T>
175  void set_object (const T *obj)
176  {
177  reset_object ();
178 
179  if (!obj) return;
180 
181  const char *obj_start = (const char *) obj;
182  if (unlikely (obj_start < this->start || this->end <= obj_start))
183  this->start = this->end = nullptr;
184  else
185  {
186  this->start = obj_start;
187  this->end = obj_start + hb_min (size_t (this->end - obj_start), obj->get_size ());
188  }
189  }
190 
191  void reset_object ()
192  {
193  this->start = this->blob->data;
194  this->end = this->start + this->blob->length;
195  assert (this->start <= this->end); /* Must not overflow. */
196  }
197 
198  void start_processing ()
199  {
200  reset_object ();
201  if (unlikely (hb_unsigned_mul_overflows (this->end - this->start, HB_SANITIZE_MAX_OPS_FACTOR)))
202  this->max_ops = HB_SANITIZE_MAX_OPS_MAX;
203  else
204  this->max_ops = hb_clamp ((unsigned) (this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR,
205  (unsigned) HB_SANITIZE_MAX_OPS_MIN,
206  (unsigned) HB_SANITIZE_MAX_OPS_MAX);
207  this->edit_count = 0;
208  this->debug_depth = 0;
209  this->recursion_depth = 0;
210 
211  DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1,
212  "start [%p..%p] (%lu bytes)",
213  this->start, this->end,
214  (unsigned long) (this->end - this->start));
215  }
216 
217  void end_processing ()
218  {
219  DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1,
220  "end [%p..%p] %u edit requests",
221  this->start, this->end, this->edit_count);
222 
223  hb_blob_destroy (this->blob);
224  this->blob = nullptr;
225  this->start = this->end = nullptr;
226  }
227 
228  unsigned get_edit_count () { return edit_count; }
229 
230  bool check_range (const void *base,
231  unsigned int len) const
232  {
233  const char *p = (const char *) base;
234  bool ok = !len ||
235  (this->start <= p &&
236  p <= this->end &&
237  (unsigned int) (this->end - p) >= len &&
238  (this->max_ops -= len) > 0);
239 
240  DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
241  "check_range [%p..%p]"
242  " (%d bytes) in [%p..%p] -> %s",
243  p, p + len, len,
244  this->start, this->end,
245  ok ? "OK" : "OUT-OF-RANGE");
246 
247  return likely (ok);
248  }
249 
250  template <typename T>
251  bool check_range (const T *base,
252  unsigned int a,
253  unsigned int b) const
254  {
255  return !hb_unsigned_mul_overflows (a, b) &&
256  this->check_range (base, a * b);
257  }
258 
259  template <typename T>
260  bool check_range (const T *base,
261  unsigned int a,
262  unsigned int b,
263  unsigned int c) const
264  {
265  return !hb_unsigned_mul_overflows (a, b) &&
266  this->check_range (base, a * b, c);
267  }
268 
269  template <typename T>
270  bool check_array (const T *base, unsigned int len) const
271  {
272  return this->check_range (base, len, hb_static_size (T));
273  }
274 
275  template <typename T>
276  bool check_array (const T *base,
277  unsigned int a,
278  unsigned int b) const
279  {
280  return this->check_range (base, a, b, hb_static_size (T));
281  }
282 
283  bool check_start_recursion (int max_depth)
284  {
285  if (unlikely (recursion_depth >= max_depth)) return false;
286  return ++recursion_depth;
287  }
288 
289  bool end_recursion (bool result)
290  {
291  recursion_depth--;
292  return result;
293  }
294 
295  template <typename Type>
296  bool check_struct (const Type *obj) const
297  { return likely (this->check_range (obj, obj->min_size)); }
298 
299  bool may_edit (const void *base, unsigned int len)
300  {
301  if (this->edit_count >= HB_SANITIZE_MAX_EDITS)
302  return false;
303 
304  const char *p = (const char *) base;
305  this->edit_count++;
306 
307  DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
308  "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s",
309  this->edit_count,
310  p, p + len, len,
311  this->start, this->end,
312  this->writable ? "GRANTED" : "DENIED");
313 
314  return this->writable;
315  }
316 
317  template <typename Type, typename ValueType>
318  bool try_set (const Type *obj, const ValueType &v)
319  {
320  if (this->may_edit (obj, hb_static_size (Type)))
321  {
322  * const_cast<Type *> (obj) = v;
323  return true;
324  }
325  return false;
326  }
327 
328  template <typename Type>
329  hb_blob_t *sanitize_blob (hb_blob_t *blob)
330  {
331  bool sane;
332 
333  init (blob);
334 
335  retry:
336  DEBUG_MSG_FUNC (SANITIZE, start, "start");
337 
338  start_processing ();
339 
340  if (unlikely (!start))
341  {
342  end_processing ();
343  return blob;
344  }
345 
346  Type *t = reinterpret_cast<Type *> (const_cast<char *> (start));
347 
348  sane = t->sanitize (this);
349  if (sane)
350  {
351  if (edit_count)
352  {
353  DEBUG_MSG_FUNC (SANITIZE, start, "passed first round with %d edits; going for second round", edit_count);
354 
355  /* sanitize again to ensure no toe-stepping */
356  edit_count = 0;
357  sane = t->sanitize (this);
358  if (edit_count) {
359  DEBUG_MSG_FUNC (SANITIZE, start, "requested %d edits in second round; FAILLING", edit_count);
360  sane = false;
361  }
362  }
363  }
364  else
365  {
366  if (edit_count && !writable) {
367  start = hb_blob_get_data_writable (blob, nullptr);
368  end = start + blob->length;
369 
370  if (start)
371  {
372  writable = true;
373  /* ok, we made it writable by relocating. try again */
374  DEBUG_MSG_FUNC (SANITIZE, start, "retry");
375  goto retry;
376  }
377  }
378  }
379 
380  end_processing ();
381 
382  DEBUG_MSG_FUNC (SANITIZE, start, sane ? "PASSED" : "FAILED");
383  if (sane)
384  {
385  hb_blob_make_immutable (blob);
386  return blob;
387  }
388  else
389  {
390  hb_blob_destroy (blob);
391  return hb_blob_get_empty ();
392  }
393  }
394 
395  template <typename Type>
396  hb_blob_t *reference_table (const hb_face_t *face, hb_tag_t tableTag = Type::tableTag)
397  {
398  if (!num_glyphs_set)
399  set_num_glyphs (hb_face_get_glyph_count (face));
400  return sanitize_blob<Type> (hb_face_reference_table (face, tableTag));
401  }
402 
403  const char *start, *end;
404  mutable int max_ops, max_subtables;
405  private:
406  int recursion_depth;
407  bool writable;
408  unsigned int edit_count;
409  hb_blob_t *blob;
410  unsigned int num_glyphs;
411  bool num_glyphs_set;
412 };
413 
415 {
416  template <typename T>
418  { c->set_object (obj); }
420  { c->reset_object (); }
421 
422  private:
424 };
425 
426 
427 #endif /* HB_SANITIZE_HH */
Definition: base.h:37
#define T(x)
Definition: main.cpp:42
hb_blob_t * hb_blob_get_empty()
Definition: hb-blob.cc:226
void hb_blob_make_immutable(hb_blob_t *blob)
Definition: hb-blob.cc:324
hb_blob_t * hb_blob_reference(hb_blob_t *blob)
Definition: hb-blob.cc:244
void hb_blob_destroy(hb_blob_t *blob)
Definition: hb-blob.cc:262
char * hb_blob_get_data_writable(hb_blob_t *blob, unsigned int *length)
Definition: hb-blob.cc:402
#define DEBUG_MSG_FUNC(WHAT, OBJ,...)
#define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, LEVEL_DIR,...)
void const void *obj HB_UNUSED
Definition: hb-debug.hh:180
unsigned int hb_face_get_glyph_count(const hb_face_t *face)
Definition: hb-face.cc:531
hb_blob_t * hb_face_reference_table(const hb_face_t *face, hb_tag_t tag)
Definition: hb-face.cc:398
#define hb_prioritize
Definition: hb-meta.hh:81
#define HB_AUTO_RETURN(E)
Definition: hb-meta.hh:76
#define HB_SANITIZE_MAX_OPS_MIN
Definition: hb-sanitize.hh:111
#define HB_SANITIZE_MAX_OPS_MAX
Definition: hb-sanitize.hh:114
#define HB_SANITIZE_MAX_EDITS
Definition: hb-sanitize.hh:105
#define HB_SANITIZE_MAX_OPS_FACTOR
Definition: hb-sanitize.hh:108
#define HB_SANITIZE_MAX_SUBTABLES
Definition: hb-sanitize.hh:117
#define likely(expr)
Definition: hb.hh:250
#define unlikely(expr)
Definition: hb.hh:251
set set set set set set set macro pixldst1 abits if abits op else op endif endm macro pixldst2 abits if abits op else op endif endm macro pixldst4 abits if abits op else op endif endm macro pixldst0 abits op endm macro pixldst3 mem_operand op endm macro pixldst30 mem_operand op endm macro pixldst abits if abits elseif abits elseif abits elseif abits elseif abits pixldst0 abits else pixldst0 abits pixldst0 abits pixldst0 abits pixldst0 abits endif elseif abits else pixldst0 abits pixldst0 abits endif elseif abits else error unsupported bpp *numpix else pixst endif endm macro vuzp8 reg2 vuzp d d &reg2 endm macro vzip8 reg2 vzip d d &reg2 endm macro pixdeinterleave basereg basereg basereg basereg basereg endif endm macro pixinterleave basereg basereg basereg basereg basereg endif endm macro PF boost_increment endif if endif PF tst PF addne PF subne PF cmp ORIG_W if endif if endif if endif PF subge ORIG_W PF subges if endif if endif if endif endif endm macro cache_preload_simple endif if dst_r_bpp pld[DST_R, #(PREFETCH_DISTANCE_SIMPLE *dst_r_bpp/8)] endif if mask_bpp pld init[MASK, #(PREFETCH_DISTANCE_SIMPLE *mask_bpp/8)] endif endif endm macro ensure_destination_ptr_alignment process_pixblock_tail_head if beq irp skip1 beq endif SRC MASK if dst_r_bpp DST_R else add endif PF add sub src_basereg pixdeinterleave mask_basereg pixdeinterleave dst_r_basereg process_pixblock_head pixblock_size cache_preload_simple process_pixblock_tail pixinterleave dst_w_basereg irp beq endif process_pixblock_tail_head tst beq irp if pixblock_size chunk_size tst beq pixld SRC pixld MASK if DST_R else pixld DST_R endif if src_basereg pixdeinterleave mask_basereg pixdeinterleave dst_r_basereg process_pixblock_head if pixblock_size cache_preload_simple endif process_pixblock_tail pixinterleave dst_w_basereg irp if pixblock_size chunk_size tst beq if DST_W else pixst DST_W else mov ORIG_W endif add lsl if lsl endif if lsl endif lsl endif lsl endif lsl endif subs mov DST_W if regs_shortage str endif bge start_of_loop_label endm macro generate_composite_function
#define assert
Definition: qcborcommon_p.h:63
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLboolean r
[2]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint GLuint end
GLenum GLenum GLsizei count
GLenum face
GLuint start
GLint GLsizei GLsizei GLenum format
GLhandleARB obj
[2]
Definition: qopenglext.h:4164
const GLubyte * c
Definition: qopenglext.h:12701
GLenum GLsizei len
Definition: qopenglext.h:3292
GLdouble GLdouble t
[9]
Definition: qopenglext.h:243
GLuint64EXT * result
[6]
Definition: qopenglext.h:10932
GLfloat GLfloat p
[1]
Definition: qopenglext.h:12698
uint32_t hb_tag_t
Definition: hb-common.h:157
QObject::connect nullptr
Definition: main.cpp:38
Definition: moc.h:48
unsigned int length
Definition: hb-blob.hh:65
const char * data
Definition: hb-blob.hh:64
bool stop_sublookup_iteration(const return_t r) const
Definition: hb-sanitize.hh:138
const char * get_name()
Definition: hb-sanitize.hh:132
static return_t no_dispatch_return_value()
Definition: hb-sanitize.hh:137
bool may_dispatch(const T *obj HB_UNUSED, const F *format)
Definition: hb-sanitize.hh:134
static return_t default_return_value()
Definition: hb-sanitize.hh:136
bool visit_subtables(unsigned count)
Definition: hb-sanitize.hh:140
hb_sanitize_with_object_t(hb_sanitize_context_t *c, const T &obj)
Definition: hb-sanitize.hh:417