Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* GRAPHITE2 LICENSING
3 Copyright 2010, SIL International
4 All rights reserved.
6 This library is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should also have received a copy of the GNU Lesser General Public
17 License along with this library in the file named "LICENSE".
18 If not, write to the Free Software Foundation, 51 Franklin Street,
19 Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
20 internet at http://www.fsf.org/licenses/lgpl.html.
22 Alternatively, the contents of this file may be used under the terms of the
23 Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
24 License, as published by the Free Software Foundation, either version 2
25 of the License or (at your option) any later version.
26 */
27 // This class represents loaded graphite stack machine code. It performs
28 // basic sanity checks, on the incoming code to prevent more obvious problems
29 // from crashing graphite.
30 // Author: Tim Eves
32 #include <cassert>
33 #include <cstddef>
34 #include <cstdlib>
35 #include <cstring>
36 #include "graphite2/Segment.h"
37 #include "inc/Code.h"
38 #include "inc/Face.h"
39 #include "inc/GlyphFace.h"
40 #include "inc/GlyphCache.h"
41 #include "inc/Machine.h"
42 #include "inc/Rule.h"
43 #include "inc/Silf.h"
45 #include <stdio.h>
47 #ifdef NDEBUG
48 #ifdef __GNUC__
49 #pragma GCC diagnostic ignored "-Wunused-parameter"
50 #endif
51 #endif
54 using namespace graphite2;
55 using namespace vm;
57 namespace {
59 inline bool is_return(const instr i) {
60 const opcode_t * opmap = Machine::getOpcodeTable();
61 const instr pop_ret = *opmap[POP_RET].impl,
62 ret_zero = *opmap[RET_ZERO].impl,
63 ret_true = *opmap[RET_TRUE].impl;
64 return i == pop_ret || i == ret_zero || i == ret_true;
65 }
67 struct context
68 {
69 context(uint8 ref=0) : codeRef(ref) {flags.changed=false; flags.referenced=false; flags.inserted=false;}
70 struct {
71 uint8 changed:1,
72 referenced:1,
73 inserted:1;
74 } flags;
75 uint8 codeRef;
76 };
78 } // end namespace
81 class Machine::Code::decoder
82 {
83 public:
84 struct limits;
85 struct analysis
86 {
87 uint8 slotref;
88 context contexts[256];
89 byte max_ref;
91 analysis() : slotref(0), max_ref(0) {};
92 void set_ref(int index) throw();
93 void set_changed(int index) throw();
95 };
97 decoder(const limits & lims, Code &code) throw();
99 bool load(const byte * bc_begin, const byte * bc_end);
100 void apply_analysis(instr * const code, instr * code_end);
101 byte max_ref() { return _analysis.max_ref; }
102 int pre_context() const { return _pre_context; }
104 private:
105 opcode fetch_opcode(const byte * bc);
106 void analyse_opcode(const opcode, const int8 * const dp) throw();
107 bool emit_opcode(opcode opc, const byte * & bc);
108 bool validate_opcode(const opcode opc, const byte * const bc);
109 bool valid_upto(const uint16 limit, const uint16 x) const throw();
110 void failure(const status_t s) const throw() { _code.failure(s); }
112 Code & _code;
113 int _pre_context;
114 uint16 _rule_length;
115 instr * _instr;
116 byte * _data;
117 const limits & _max;
118 analysis _analysis;
119 };
122 struct Machine::Code::decoder::limits
123 {
124 const byte * const bytecode;
125 const uint8 pre_context;
126 const uint16 rule_length,
127 classes,
128 glyf_attrs,
129 features;
130 const byte attrid[gr_slatMax];
131 };
133 inline Machine::Code::decoder::decoder(const limits & lims, Code &code) throw()
134 : _code(code),
135 _pre_context(code._constraint ? 0 : lims.pre_context),
136 _rule_length(code._constraint ? 1 : lims.rule_length),
137 _instr(code._code), _data(code._data), _max(lims)
138 { }
142 Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end,
143 uint8 pre_context, uint16 rule_length, const Silf & silf, const Face & face)
144 : _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0), _status(loaded),
145 _constraint(is_constraint), _modify(false), _delete(false), _own(true)
146 {
147 #ifdef GRAPHITE2_TELEMETRY
148 telemetry::category _code_cat(face.tele.code);
149 #endif
150 assert(bytecode_begin != 0);
151 if (bytecode_begin == bytecode_end)
152 {
153 ::new (this) Code();
154 return;
155 }
156 assert(bytecode_end > bytecode_begin);
157 const opcode_t * op_to_fn = Machine::getOpcodeTable();
159 // Allocate code and dat target buffers, these sizes are a worst case
160 // estimate. Once we know their real sizes the we'll shrink them.
161 _code = static_cast<instr *>(malloc((bytecode_end - bytecode_begin)
162 * sizeof(instr)));
163 _data = static_cast<byte *>(malloc((bytecode_end - bytecode_begin)
164 * sizeof(byte)));
166 if (!_code || !_data) {
167 failure(alloc_failed);
168 return;
169 }
171 const decoder::limits lims = {
172 bytecode_end,
173 pre_context,
174 rule_length,
175 silf.numClasses(),
176 face.glyphs().numAttrs(),
177 face.numFeatures(),
178 {1,1,1,1,1,1,1,1,
179 1,1,1,1,1,1,1,255,
180 1,1,1,1,1,1,1,1,
181 1,1,1,1,1,1,0,0,
182 0,0,0,0,0,0,0,0,
183 0,0,0,0,0,0,0,0,
184 0,0,0,0,0,0,0, silf.numUser()}
185 };
187 decoder dec(lims, *this);
188 if(!dec.load(bytecode_begin, bytecode_end))
189 return;
191 // Is this an empty program?
192 if (_instr_count == 0)
193 {
194 release_buffers();
195 ::new (this) Code();
196 return;
197 }
199 // When we reach the end check we've terminated it correctly
200 if (!is_return(_code[_instr_count-1])) {
201 failure(missing_return);
202 return;
203 }
205 assert((_constraint && immutable()) || !_constraint);
206 dec.apply_analysis(_code, _code + _instr_count);
207 _max_ref = dec.max_ref();
209 // Now we know exactly how much code and data the program really needs
210 // realloc the buffers to exactly the right size so we don't waste any
211 // memory.
212 assert((bytecode_end - bytecode_begin) >= std::ptrdiff_t(_instr_count));
213 assert((bytecode_end - bytecode_begin) >= std::ptrdiff_t(_data_size));
214 _code = static_cast<instr *>(realloc(_code, (_instr_count+1)*sizeof(instr)));
215 _data = static_cast<byte *>(realloc(_data, _data_size*sizeof(byte)));
217 if (!_code)
218 {
219 failure(alloc_failed);
220 return;
221 }
223 // Make this RET_ZERO, we should never reach this but just in case ...
224 _code[_instr_count] = op_to_fn[RET_ZERO].impl[_constraint];
226 #ifdef GRAPHITE2_TELEMETRY
227 telemetry::count_bytes(_data_size + (_instr_count+1)*sizeof(instr));
228 #endif
229 }
231 Machine::Code::~Code() throw ()
232 {
233 if (_own)
234 release_buffers();
235 }
238 bool Machine::Code::decoder::load(const byte * bc, const byte * bc_end)
239 {
240 while (bc < bc_end)
241 {
242 const opcode opc = fetch_opcode(bc++);
243 if (opc == vm::MAX_OPCODE)
244 return false;
246 analyse_opcode(opc, reinterpret_cast<const int8 *>(bc));
248 if (!emit_opcode(opc, bc))
249 return false;
250 }
252 return bool(_code);
253 }
255 // Validation check and fixups.
256 //
258 opcode Machine::Code::decoder::fetch_opcode(const byte * bc)
259 {
260 const opcode opc = opcode(*bc++);
262 // Do some basic sanity checks based on what we know about the opcode
263 if (!validate_opcode(opc, bc)) return MAX_OPCODE;
265 // And check it's arguments as far as possible
266 switch (opc)
267 {
268 case NOP :
269 case PUSH_BYTE :
270 case PUSH_BYTEU :
271 case PUSH_SHORT :
272 case PUSH_SHORTU :
273 case PUSH_LONG :
274 case ADD :
275 case SUB :
276 case MUL :
277 case DIV :
278 case MIN_ :
279 case MAX_ :
280 case NEG :
281 case TRUNC8 :
282 case TRUNC16 :
283 case COND :
284 case AND :
285 case OR :
286 case NOT :
287 case EQUAL :
288 case NOT_EQ :
289 case LESS :
290 case GTR :
291 case LESS_EQ :
292 case GTR_EQ :
293 break;
294 case NEXT :
295 case NEXT_N : // runtime checked
296 case COPY_NEXT :
297 ++_pre_context;
298 break;
299 case PUT_GLYPH_8BIT_OBS :
300 valid_upto(_max.classes, bc[0]);
301 break;
302 case PUT_SUBS_8BIT_OBS :
303 valid_upto(_rule_length, _pre_context + int8(bc[0]));
304 valid_upto(_max.classes, bc[1]);
305 valid_upto(_max.classes, bc[2]);
306 break;
307 case PUT_COPY :
308 valid_upto(_rule_length, _pre_context + int8(bc[0]));
309 break;
310 case INSERT :
311 --_pre_context;
312 break;
313 case DELETE :
314 break;
315 case ASSOC :
316 for (uint8 num = bc[0]; num; --num)
317 valid_upto(_rule_length, _pre_context + int8(bc[num]));
318 break;
319 case CNTXT_ITEM :
320 valid_upto(_max.rule_length, _max.pre_context + int8(bc[0]));
321 if (bc + 2 + bc[1] >= _max.bytecode) failure(jump_past_end);
322 if (_pre_context != 0) failure(nested_context_item);
323 break;
324 case ATTR_SET :
325 case ATTR_ADD :
326 case ATTR_SUB :
327 case ATTR_SET_SLOT :
328 valid_upto(gr_slatMax, bc[0]);
329 break;
330 case IATTR_SET_SLOT :
331 if (valid_upto(gr_slatMax, bc[0]))
332 valid_upto(_max.attrid[bc[0]], bc[1]);
333 break;
334 case PUSH_SLOT_ATTR :
335 valid_upto(gr_slatMax, bc[0]);
336 valid_upto(_rule_length, _pre_context + int8(bc[1]));
337 break;
338 case PUSH_GLYPH_ATTR_OBS :
339 valid_upto(_max.glyf_attrs, bc[0]);
340 valid_upto(_rule_length, _pre_context + int8(bc[1]));
341 break;
342 case PUSH_GLYPH_METRIC :
343 valid_upto(kgmetDescent, bc[0]);
344 valid_upto(_rule_length, _pre_context + int8(bc[1]));
345 // level: dp[2] no check necessary
346 break;
347 case PUSH_FEAT :
348 valid_upto(_max.features, bc[0]);
349 valid_upto(_rule_length, _pre_context + int8(bc[1]));
350 break;
351 case PUSH_ATT_TO_GATTR_OBS :
352 valid_upto(_max.glyf_attrs, bc[0]);
353 valid_upto(_rule_length, _pre_context + int8(bc[1]));
354 break;
355 case PUSH_ATT_TO_GLYPH_METRIC :
356 valid_upto(kgmetDescent, bc[0]);
357 valid_upto(_rule_length, _pre_context + int8(bc[1]));
358 // level: dp[2] no check necessary
359 break;
360 case PUSH_ISLOT_ATTR :
361 if (valid_upto(gr_slatMax, bc[0]))
362 {
363 valid_upto(_rule_length, _pre_context + int8(bc[1]));
364 valid_upto(_max.attrid[bc[0]], bc[2]);
365 }
366 break;
367 case PUSH_IGLYPH_ATTR :// not implemented
368 case POP_RET :
369 case RET_ZERO :
370 case RET_TRUE :
371 break;
372 case IATTR_SET :
373 case IATTR_ADD :
374 case IATTR_SUB :
375 if (valid_upto(gr_slatMax, bc[0]))
376 valid_upto(_max.attrid[bc[0]], bc[1]);
377 break;
378 case PUSH_PROC_STATE : // dummy: dp[0] no check necessary
379 case PUSH_VERSION :
380 break;
381 case PUT_SUBS :
382 valid_upto(_rule_length, _pre_context + int8(bc[0]));
383 valid_upto(_max.classes, uint16(bc[1]<< 8) | bc[2]);
384 valid_upto(_max.classes, uint16(bc[3]<< 8) | bc[4]);
385 break;
386 case PUT_SUBS2 : // not implemented
387 case PUT_SUBS3 : // not implemented
388 break;
389 case PUT_GLYPH :
390 valid_upto(_max.classes, uint16(bc[0]<< 8) | bc[1]);
391 break;
392 case PUSH_GLYPH_ATTR :
393 case PUSH_ATT_TO_GLYPH_ATTR :
394 valid_upto(_max.glyf_attrs, uint16(bc[0]<< 8) | bc[1]);
395 valid_upto(_rule_length, _pre_context + int8(bc[2]));
396 break;
397 default:
398 failure(invalid_opcode);
399 break;
400 }
402 return bool(_code) ? opc : MAX_OPCODE;
403 }
406 void Machine::Code::decoder::analyse_opcode(const opcode opc, const int8 * arg) throw()
407 {
408 if (_code._constraint) return;
410 switch (opc)
411 {
412 case DELETE :
413 _code._delete = true;
414 break;
415 case PUT_GLYPH_8BIT_OBS :
416 case PUT_GLYPH :
417 _code._modify = true;
418 _analysis.set_changed(_analysis.slotref);
419 break;
420 case NEXT :
421 case COPY_NEXT :
422 if (!_analysis.contexts[_analysis.slotref].flags.inserted)
423 ++_analysis.slotref;
424 _analysis.contexts[_analysis.slotref] = context(_code._instr_count+1);
425 if (_analysis.slotref > _analysis.max_ref) _analysis.max_ref = _analysis.slotref;
426 break;
427 case INSERT :
428 _analysis.contexts[_analysis.slotref].flags.inserted = true;
429 _code._modify = true;
430 break;
431 case PUT_SUBS_8BIT_OBS : // slotref on 1st parameter
432 case PUT_SUBS :
433 _code._modify = true;
434 _analysis.set_changed(_analysis.slotref);
435 // no break
436 case PUT_COPY :
437 {
438 if (arg[0] != 0) { _analysis.set_changed(_analysis.slotref); _code._modify = true; }
439 if (arg[0] <= 0 && -arg[0] <= _analysis.slotref - _analysis.contexts[_analysis.slotref].flags.inserted)
440 _analysis.set_ref(_analysis.slotref + arg[0] - _analysis.contexts[_analysis.slotref].flags.inserted);
441 else if (_analysis.slotref + arg[0] > _analysis.max_ref) _analysis.max_ref = _analysis.slotref + arg[0];
442 break;
443 }
444 case PUSH_ATT_TO_GATTR_OBS : // slotref on 2nd parameter
445 if (_code._constraint) return;
446 // no break
447 case PUSH_GLYPH_ATTR_OBS :
448 case PUSH_SLOT_ATTR :
449 case PUSH_GLYPH_METRIC :
450 case PUSH_ATT_TO_GLYPH_METRIC :
451 case PUSH_ISLOT_ATTR :
452 case PUSH_FEAT :
453 if (arg[1] <= 0 && -arg[1] <= _analysis.slotref - _analysis.contexts[_analysis.slotref].flags.inserted)
454 _analysis.set_ref(_analysis.slotref + arg[1] - _analysis.contexts[_analysis.slotref].flags.inserted);
455 else if (_analysis.slotref + arg[1] > _analysis.max_ref) _analysis.max_ref = _analysis.slotref + arg[1];
456 break;
457 case PUSH_ATT_TO_GLYPH_ATTR :
458 if (_code._constraint) return;
459 // no break
460 case PUSH_GLYPH_ATTR :
461 if (arg[2] <= 0 && -arg[2] <= _analysis.slotref - _analysis.contexts[_analysis.slotref].flags.inserted)
462 _analysis.set_ref(_analysis.slotref + arg[2] - _analysis.contexts[_analysis.slotref].flags.inserted);
463 else if (_analysis.slotref + arg[2] > _analysis.max_ref) _analysis.max_ref = _analysis.slotref + arg[2];
464 break;
465 case ASSOC : // slotrefs in varargs
466 break;
467 default:
468 break;
469 }
470 }
473 bool Machine::Code::decoder::emit_opcode(opcode opc, const byte * & bc)
474 {
475 const opcode_t * op_to_fn = Machine::getOpcodeTable();
476 const opcode_t & op = op_to_fn[opc];
477 if (op.impl[_code._constraint] == 0)
478 {
479 failure(unimplemented_opcode_used);
480 return false;
481 }
483 const size_t param_sz = op.param_sz == VARARGS ? bc[0] + 1 : op.param_sz;
485 // Add this instruction
486 *_instr++ = op.impl[_code._constraint];
487 ++_code._instr_count;
489 // Grab the parameters
490 if (param_sz) {
491 memcpy(_data, bc, param_sz * sizeof(byte));
492 bc += param_sz;
493 _data += param_sz;
494 _code._data_size += param_sz;
495 }
497 // recursively decode a context item so we can split the skip into
498 // instruction and data portions.
499 if (opc == CNTXT_ITEM)
500 {
501 assert(_pre_context == 0);
502 _pre_context = _max.pre_context + int8(_data[-2]);
503 _rule_length = _max.rule_length;
505 const size_t ctxt_start = _code._instr_count;
506 byte & instr_skip = _data[-1];
507 byte & data_skip = *_data++;
508 ++_code._data_size;
510 if (load(bc, bc + instr_skip))
511 {
512 bc += instr_skip;
513 data_skip = instr_skip - (_code._instr_count - ctxt_start);
514 instr_skip = _code._instr_count - ctxt_start;
516 _rule_length = 1;
517 _pre_context = 0;
518 }
519 }
521 return bool(_code);
522 }
525 void Machine::Code::decoder::apply_analysis(instr * const code, instr * code_end)
526 {
527 // insert TEMP_COPY commands for slots that need them (that change and are referenced later)
528 int tempcount = 0;
529 if (_code._constraint) return;
531 const instr temp_copy = Machine::getOpcodeTable()[TEMP_COPY].impl[0];
532 for (const context * c = _analysis.contexts, * const ce = c + _analysis.slotref; c != ce; ++c)
533 {
534 if (!c->flags.referenced || !c->flags.changed) continue;
536 instr * const tip = code + c->codeRef + tempcount;
537 memmove(tip+1, tip, (code_end - tip) * sizeof(instr));
538 *tip = temp_copy;
539 ++code_end;
540 ++tempcount;
541 }
543 _code._instr_count = code_end - code;
544 }
547 inline
548 bool Machine::Code::decoder::validate_opcode(const opcode opc, const byte * const bc)
549 {
550 if (opc >= MAX_OPCODE)
551 {
552 failure(invalid_opcode);
553 return false;
554 }
555 const opcode_t & op = Machine::getOpcodeTable()[opc];
556 const size_t param_sz = op.param_sz == VARARGS ? bc[0] + 1 : op.param_sz;
557 if (bc + param_sz > _max.bytecode)
558 {
559 failure(arguments_exhausted);
560 return false;
561 }
562 return true;
563 }
566 bool Machine::Code::decoder::valid_upto(const uint16 limit, const uint16 x) const throw()
567 {
568 const bool t = x < limit;
569 if (!t) failure(out_of_range_data);
570 return t;
571 }
574 inline
575 void Machine::Code::failure(const status_t s) throw() {
576 release_buffers();
577 _status = s;
578 }
581 inline
582 void Machine::Code::decoder::analysis::set_ref(const int index) throw() {
583 contexts[index].flags.referenced = true;
584 if (index > max_ref) max_ref = index;
585 }
588 inline
589 void Machine::Code::decoder::analysis::set_changed(const int index) throw() {
590 contexts[index].flags.changed = true;
591 if (index > max_ref) max_ref = index;
592 }
595 void Machine::Code::release_buffers() throw()
596 {
597 free(_code);
598 free(_data);
599 _code = 0;
600 _data = 0;
601 _own = false;
602 }
605 int32 Machine::Code::run(Machine & m, slotref * & map) const
606 {
607 assert(_own);
608 assert(*this); // Check we are actually runnable
610 if (m.slotMap().size() <= size_t(_max_ref + m.slotMap().context()))
611 {
612 m._status = Machine::slot_offset_out_bounds;
613 // return (m.slotMap().end() - map);
614 return 1;
615 }
617 return m.run(_code, _data, map);
618 }