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  * Sanitize
38  *
39  *
40  * === Introduction ===
41  *
42  * The sanitize machinery is at the core of our zero-cost font loading.  We
43  * mmap() font file into memory and create a blob out of it.  Font subtables
44  * are returned as a readonly sub-blob of the main font blob.  These table
45  * blobs are then sanitized before use, to ensure invalid memory access does
46  * not happen.  The toplevel sanitize API use is like, eg. to load the 'head'
47  * table:
48  *
49  *   hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table<OT::head> (face);
50  *
51  * The blob then can be converted to a head table struct with:
52  *
53  *   const head *head_table = head_blob->as<head> ();
54  *
55  * What the reference_table does is, to call hb_face_reference_table() to load
56  * the table blob, sanitize it and return either the sanitized blob, or empty
57  * blob if sanitization failed.  The blob->as() function returns the null
58  * object of its template type argument if the blob is empty.  Otherwise, it
59  * just casts the blob contents to the desired type.
60  *
61  * Sanitizing a blob of data with a type T works as follows (with minor
62  * simplification):
63  *
64  *   - Cast blob content to T*, call sanitize() method of it,
65  *   - If sanitize succeeded, return blob.
66  *   - Otherwise, if blob is not writable, try making it writable,
67  *     or copy if cannot be made writable in-place,
68  *   - Call sanitize() again.  Return blob if sanitize succeeded.
69  *   - Return empty blob otherwise.
70  *
71  *
72  * === The sanitize() contract ===
73  *
74  * The sanitize() method of each object type shall return true if it's safe to
75  * call other methods of the object, and false otherwise.
76  *
77  * Note that what sanitize() checks for might align with what the specification
78  * describes as valid table data, but does not have to be.  In particular, we
79  * do NOT want to be pedantic and concern ourselves with validity checks that
80  * are irrelevant to our use of the table.  On the contrary, we want to be
81  * lenient with error handling and accept invalid data to the extent that it
82  * does not impose extra burden on us.
83  *
84  * Based on the sanitize contract, one can see that what we check for depends
85  * on how we use the data in other table methods.  Ie. if other table methods
86  * assume that offsets do NOT point out of the table data block, then that's
87  * something sanitize() must check for (GSUB/GPOS/GDEF/etc work this way).  On
88  * the other hand, if other methods do such checks themselves, then sanitize()
89  * does not have to bother with them (glyf/local work this way).  The choice
90  * depends on the table structure and sanitize() performance.  For example, to
91  * check glyf/loca offsets in sanitize() would cost O(num-glyphs).  We try hard
92  * to avoid such costs during font loading.  By postponing such checks to the
93  * actual glyph loading, we reduce the sanitize cost to O(1) and total runtime
94  * cost to O(used-glyphs).  As such, this is preferred.
95  *
96  * The same argument can be made re GSUB/GPOS/GDEF, but there, the table
97  * structure is so complicated that by checking all offsets at sanitize() time,
98  * we make the code much simpler in other methods, as offsets and referenced
99  * objects do not need to be validated at each use site.
100  */
101 
102 /* This limits sanitizing time on really broken fonts. */
103 #ifndef HB_SANITIZE_MAX_EDITS
104 #define HB_SANITIZE_MAX_EDITS 32
105 #endif
106 #ifndef HB_SANITIZE_MAX_OPS_FACTOR
107 #define HB_SANITIZE_MAX_OPS_FACTOR 8
108 #endif
109 #ifndef HB_SANITIZE_MAX_OPS_MIN
110 #define HB_SANITIZE_MAX_OPS_MIN 16384
111 #endif
112 #ifndef HB_SANITIZE_MAX_OPS_MAX
113 #define HB_SANITIZE_MAX_OPS_MAX 0x3FFFFFFF
114 #endif
115 #ifndef HB_SANITIZE_MAX_SUTABLES
116 #define HB_SANITIZE_MAX_SUTABLES 0x4000
117 #endif
118 
119 struct hb_sanitize_context_t : hb_dispatch_context_t<hb_sanitize_context_t, bool, HB_DEBUG_SANITIZE>
120 {
hb_sanitize_context_thb_sanitize_context_t121     hb_sanitize_context_t()
122         : start(nullptr)
123         , end(nullptr)
124         , max_ops(0)
125         , max_subtables(0)
126         , writable(false)
127         , edit_count(0)
128         , blob(nullptr)
129         , num_glyphs(65536)
130         , num_glyphs_set(false)
131     {
132     }
133 
get_namehb_sanitize_context_t134     const char *get_name()
135     {
136         return "SANITIZE";
137     }
may_dispatchhb_sanitize_context_t138     template <typename T, typename F> bool may_dispatch(const T *obj HB_UNUSED, const F *format)
139     {
140         return format->sanitize(this);
141     }
default_return_valuehb_sanitize_context_t142     static return_t default_return_value()
143     {
144         return true;
145     }
no_dispatch_return_valuehb_sanitize_context_t146     static return_t no_dispatch_return_value()
147     {
148         return false;
149     }
stop_sublookup_iterationhb_sanitize_context_t150     bool stop_sublookup_iteration(const return_t r) const
151     {
152         return !r;
153     }
154 
visit_subtableshb_sanitize_context_t155     bool visit_subtables(unsigned count)
156     {
157         max_subtables += count;
158         return max_subtables < HB_SANITIZE_MAX_SUTABLES;
159     }
160 
161 private:
162     template <typename T, typename... Ts>
163     auto _dispatch(const T &obj, hb_priority<1>, Ts &&... ds)
164         HB_AUTO_RETURN(obj.sanitize(this, hb_forward<Ts>(ds)...)) template <typename T, typename... Ts>
165         auto _dispatch(const T &obj, hb_priority<0>, Ts &&... ds)
166             HB_AUTO_RETURN(obj.dispatch(this, hb_forward<Ts>(ds)...)) public
167         : template <typename T, typename... Ts>
dispatchhb_sanitize_context_t168           auto dispatch(const T &obj, Ts &&... ds) HB_AUTO_RETURN(_dispatch(obj, hb_prioritize, hb_forward<Ts>(ds)...))
169 
170               void init(hb_blob_t *b)
171     {
172         this->blob = hb_blob_reference(b);
173         this->writable = false;
174     }
175 
set_num_glyphshb_sanitize_context_t176     void set_num_glyphs(unsigned int num_glyphs_)
177     {
178         num_glyphs = num_glyphs_;
179         num_glyphs_set = true;
180     }
get_num_glyphshb_sanitize_context_t181     unsigned int get_num_glyphs()
182     {
183         return num_glyphs;
184     }
185 
set_max_opshb_sanitize_context_t186     void set_max_ops(int max_ops_)
187     {
188         max_ops = max_ops_;
189     }
190 
set_objecthb_sanitize_context_t191     template <typename T> void set_object(const T *obj)
192     {
193         reset_object();
194 
195         if (!obj)
196             return;
197 
198         const char *obj_start = (const char *)obj;
199         if (unlikely(obj_start < this->start || this->end <= obj_start))
200             this->start = this->end = nullptr;
201         else {
202             this->start = obj_start;
203             this->end = obj_start + hb_min(size_t(this->end - obj_start), obj->get_size());
204         }
205     }
206 
reset_objecthb_sanitize_context_t207     void reset_object()
208     {
209         this->start = this->blob->data;
210         this->end = this->start + this->blob->length;
211         assert(this->start <= this->end); /* Must not overflow. */
212     }
213 
start_processinghb_sanitize_context_t214     void start_processing()
215     {
216         reset_object();
217         if (unlikely(hb_unsigned_mul_overflows(this->end - this->start, HB_SANITIZE_MAX_OPS_FACTOR)))
218             this->max_ops = HB_SANITIZE_MAX_OPS_MAX;
219         else
220             this->max_ops = hb_clamp((unsigned)(this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR,
221                                      (unsigned)HB_SANITIZE_MAX_OPS_MIN,
222                                      (unsigned)HB_SANITIZE_MAX_OPS_MAX);
223         this->edit_count = 0;
224         this->debug_depth = 0;
225 
226         DEBUG_MSG_LEVEL(SANITIZE,
227                         start,
228                         0,
229                         +1,
230                         "start [%p..%p] (%lu bytes)",
231                         this->start,
232                         this->end,
233                         (unsigned long)(this->end - this->start));
234     }
235 
end_processinghb_sanitize_context_t236     void end_processing()
237     {
238         DEBUG_MSG_LEVEL(
239             SANITIZE, this->start, 0, -1, "end [%p..%p] %u edit requests", this->start, this->end, this->edit_count);
240 
241         hb_blob_destroy(this->blob);
242         this->blob = nullptr;
243         this->start = this->end = nullptr;
244     }
245 
get_edit_counthb_sanitize_context_t246     unsigned get_edit_count()
247     {
248         return edit_count;
249     }
250 
check_rangehb_sanitize_context_t251     bool check_range(const void *base, unsigned int len) const
252     {
253         const char *p = (const char *)base;
254         bool ok =
255             !len || (this->start <= p && p <= this->end && (unsigned int)(this->end - p) >= len && this->max_ops-- > 0);
256 
257         DEBUG_MSG_LEVEL(SANITIZE,
258                         p,
259                         this->debug_depth + 1,
260                         0,
261                         "check_range [%p..%p]"
262                         " (%d bytes) in [%p..%p] -> %s",
263                         p,
264                         p + len,
265                         len,
266                         this->start,
267                         this->end,
268                         ok ? "OK" : "OUT-OF-RANGE");
269 
270         return likely(ok);
271     }
272 
check_rangehb_sanitize_context_t273     template <typename T> bool check_range(const T *base, unsigned int a, unsigned int b) const
274     {
275         return !hb_unsigned_mul_overflows(a, b) && this->check_range(base, a * b);
276     }
277 
check_rangehb_sanitize_context_t278     template <typename T> bool check_range(const T *base, unsigned int a, unsigned int b, unsigned int c) const
279     {
280         return !hb_unsigned_mul_overflows(a, b) && this->check_range(base, a * b, c);
281     }
282 
check_arrayhb_sanitize_context_t283     template <typename T> bool check_array(const T *base, unsigned int len) const
284     {
285         return this->check_range(base, len, hb_static_size(T));
286     }
287 
check_arrayhb_sanitize_context_t288     template <typename T> bool check_array(const T *base, unsigned int a, unsigned int b) const
289     {
290         return this->check_range(base, a, b, hb_static_size(T));
291     }
292 
check_structhb_sanitize_context_t293     template <typename Type> bool check_struct(const Type *obj) const
294     {
295         return likely(this->check_range(obj, obj->min_size));
296     }
297 
may_edithb_sanitize_context_t298     bool may_edit(const void *base, unsigned int len)
299     {
300         if (this->edit_count >= HB_SANITIZE_MAX_EDITS)
301             return false;
302 
303         const char *p = (const char *)base;
304         this->edit_count++;
305 
306         DEBUG_MSG_LEVEL(SANITIZE,
307                         p,
308                         this->debug_depth + 1,
309                         0,
310                         "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s",
311                         this->edit_count,
312                         p,
313                         p + len,
314                         len,
315                         this->start,
316                         this->end,
317                         this->writable ? "GRANTED" : "DENIED");
318 
319         return this->writable;
320     }
321 
try_sethb_sanitize_context_t322     template <typename Type, typename ValueType> bool try_set(const Type *obj, const ValueType &v)
323     {
324         if (this->may_edit(obj, hb_static_size(Type))) {
325             *const_cast<Type *>(obj) = v;
326             return true;
327         }
328         return false;
329     }
330 
sanitize_blobhb_sanitize_context_t331     template <typename Type> hb_blob_t *sanitize_blob(hb_blob_t *blob)
332     {
333         bool sane;
334 
335         init(blob);
336 
337         DEBUG_MSG_FUNC(SANITIZE, start, "start");
338 
339         start_processing();
340 
341         if (unlikely(!start)) {
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             if (edit_count) {
351                 DEBUG_MSG_FUNC(SANITIZE, start, "passed first round with %d edits; going for second round", edit_count);
352 
353                 /* sanitize again to ensure no toe-stepping */
354                 edit_count = 0;
355                 sane = t->sanitize(this);
356                 if (edit_count) {
357                     DEBUG_MSG_FUNC(SANITIZE, start, "requested %d edits in second round; FAILLING", edit_count);
358                     sane = false;
359                 }
360             }
361         }
362 
363         end_processing();
364 
365         DEBUG_MSG_FUNC(SANITIZE, start, sane ? "PASSED" : "FAILED");
366         if (sane) {
367             hb_blob_make_immutable(blob);
368             return blob;
369         } else {
370             hb_blob_destroy(blob);
371             return hb_blob_get_empty();
372         }
373     }
374 
reference_tablehb_sanitize_context_t375     template <typename Type> hb_blob_t *reference_table(const hb_face_t *face, hb_tag_t tableTag = Type::tableTag)
376     {
377         if (!num_glyphs_set)
378             set_num_glyphs(hb_face_get_glyph_count(face));
379         return sanitize_blob<Type>(hb_face_reference_table(face, tableTag));
380     }
381 
382     const char *start, *end;
383     mutable int max_ops, max_subtables;
384 
385 private:
386     bool writable;
387     unsigned int edit_count;
388     hb_blob_t *blob;
389     unsigned int num_glyphs;
390     bool num_glyphs_set;
391 };
392 
393 struct hb_sanitize_with_object_t
394 {
395     template <typename T>
hb_sanitize_with_object_thb_sanitize_with_object_t396     hb_sanitize_with_object_t(hb_sanitize_context_t *c, const T &obj)
397         : c(c)
398     {
399         c->set_object(obj);
400     }
~hb_sanitize_with_object_thb_sanitize_with_object_t401     ~hb_sanitize_with_object_t()
402     {
403         c->reset_object();
404     }
405 
406 private:
407     hb_sanitize_context_t *c;
408 };
409 
410 #endif /* HB_SANITIZE_HH */
411