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