2 * Copyright © 2007,2008,2009,2010 Red Hat, Inc.
3 * Copyright © 2012,2018 Google, Inc.
5 * This is part of HarfBuzz, a text shaping library.
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.
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
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.
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
29 #ifndef HB_SANITIZE_HH
30 #define HB_SANITIZE_HH
34 #include "hb-dispatch.hh"
41 * === Introduction ===
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'
50 * hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table<OT::head> (face);
52 * The blob then can be converted to a head table struct with:
54 * const head *head_table = head_blob->as<head> ();
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.
62 * Sanitizing a blob of data with a type T works as follows (with minor
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.
73 * === The sanitize() contract ===
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.
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.
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.
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.
103 /* This limits sanitizing time on really broken fonts. */
104 #ifndef HB_SANITIZE_MAX_EDITS
105 #define HB_SANITIZE_MAX_EDITS 32
107 #ifndef HB_SANITIZE_MAX_OPS_FACTOR
108 #define HB_SANITIZE_MAX_OPS_FACTOR 64
110 #ifndef HB_SANITIZE_MAX_OPS_MIN
111 #define HB_SANITIZE_MAX_OPS_MIN 16384
113 #ifndef HB_SANITIZE_MAX_OPS_MAX
114 #define HB_SANITIZE_MAX_OPS_MAX 0x3FFFFFFF
116 #ifndef HB_SANITIZE_MAX_SUBTABLES
117 #define HB_SANITIZE_MAX_SUBTABLES 0x4000
120 struct hb_sanitize_context_t :
121 hb_dispatch_context_t<hb_sanitize_context_t, bool, HB_DEBUG_SANITIZE>
123 hb_sanitize_context_t () :
124 start (nullptr), end (nullptr),
125 max_ops (0), max_subtables (0),
127 writable (false), edit_count (0),
130 num_glyphs_set (false) {}
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; }
140 bool visit_subtables (unsigned count)
142 max_subtables += count;
143 return max_subtables < HB_SANITIZE_MAX_SUBTABLES;
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)...) )
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)...) )
159 void init (hb_blob_t *b)
161 this->blob = hb_blob_reference (b);
162 this->writable = false;
165 void set_num_glyphs (unsigned int num_glyphs_)
167 num_glyphs = num_glyphs_;
168 num_glyphs_set = true;
170 unsigned int get_num_glyphs () { return num_glyphs; }
172 void set_max_ops (int max_ops_) { max_ops = max_ops_; }
174 template <typename T>
175 void set_object (const T *obj)
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;
186 this->start = obj_start;
187 this->end = obj_start + hb_min (size_t (this->end - obj_start), obj->get_size ());
193 this->start = this->blob->data;
194 this->end = this->start + this->blob->length;
195 assert (this->start <= this->end); /* Must not overflow. */
198 void start_processing ()
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;
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;
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));
217 void end_processing ()
219 DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1,
220 "end [%p..%p] %u edit requests",
221 this->start, this->end, this->edit_count);
223 hb_blob_destroy (this->blob);
224 this->blob = nullptr;
225 this->start = this->end = nullptr;
228 unsigned get_edit_count () { return edit_count; }
230 bool check_range (const void *base,
231 unsigned int len) const
233 const char *p = (const char *) base;
237 (unsigned int) (this->end - p) >= len &&
238 (this->max_ops -= len) > 0);
240 DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
241 "check_range [%p..%p]"
242 " (%d bytes) in [%p..%p] -> %s",
244 this->start, this->end,
245 ok ? "OK" : "OUT-OF-RANGE");
250 template <typename T>
251 bool check_range (const T *base,
253 unsigned int b) const
255 return !hb_unsigned_mul_overflows (a, b) &&
256 this->check_range (base, a * b);
259 template <typename T>
260 bool check_range (const T *base,
263 unsigned int c) const
265 return !hb_unsigned_mul_overflows (a, b) &&
266 this->check_range (base, a * b, c);
269 template <typename T>
270 bool check_array (const T *base, unsigned int len) const
272 return this->check_range (base, len, hb_static_size (T));
275 template <typename T>
276 bool check_array (const T *base,
278 unsigned int b) const
280 return this->check_range (base, a, b, hb_static_size (T));
283 bool check_start_recursion (int max_depth)
285 if (unlikely (recursion_depth >= max_depth)) return false;
286 return ++recursion_depth;
289 bool end_recursion (bool result)
295 template <typename Type>
296 bool check_struct (const Type *obj) const
297 { return likely (this->check_range (obj, obj->min_size)); }
299 bool may_edit (const void *base, unsigned int len)
301 if (this->edit_count >= HB_SANITIZE_MAX_EDITS)
304 const char *p = (const char *) base;
307 DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
308 "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s",
311 this->start, this->end,
312 this->writable ? "GRANTED" : "DENIED");
314 return this->writable;
317 template <typename Type, typename ValueType>
318 bool try_set (const Type *obj, const ValueType &v)
320 if (this->may_edit (obj, hb_static_size (Type)))
322 * const_cast<Type *> (obj) = v;
328 template <typename Type>
329 hb_blob_t *sanitize_blob (hb_blob_t *blob)
336 DEBUG_MSG_FUNC (SANITIZE, start, "start");
340 if (unlikely (!start))
346 Type *t = reinterpret_cast<Type *> (const_cast<char *> (start));
348 sane = t->sanitize (this);
353 DEBUG_MSG_FUNC (SANITIZE, start, "passed first round with %d edits; going for second round", edit_count);
355 /* sanitize again to ensure no toe-stepping */
357 sane = t->sanitize (this);
359 DEBUG_MSG_FUNC (SANITIZE, start, "requested %d edits in second round; FAILLING", edit_count);
366 if (edit_count && !writable) {
367 start = hb_blob_get_data_writable (blob, nullptr);
368 end = start + blob->length;
373 /* ok, we made it writable by relocating. try again */
374 DEBUG_MSG_FUNC (SANITIZE, start, "retry");
382 DEBUG_MSG_FUNC (SANITIZE, start, sane ? "PASSED" : "FAILED");
385 hb_blob_make_immutable (blob);
390 hb_blob_destroy (blob);
391 return hb_blob_get_empty ();
395 template <typename Type>
396 hb_blob_t *reference_table (const hb_face_t *face, hb_tag_t tableTag = Type::tableTag)
399 set_num_glyphs (hb_face_get_glyph_count (face));
400 return sanitize_blob<Type> (hb_face_reference_table (face, tableTag));
403 const char *start, *end;
404 mutable int max_ops, max_subtables;
408 unsigned int edit_count;
410 unsigned int num_glyphs;
414 struct hb_sanitize_with_object_t
416 template <typename T>
417 hb_sanitize_with_object_t (hb_sanitize_context_t *c, const T& obj) : c (c)
418 { c->set_object (obj); }
419 ~hb_sanitize_with_object_t ()
420 { c->reset_object (); }
423 hb_sanitize_context_t *c;
427 #endif /* HB_SANITIZE_HH */