diff --git a/src/hb-cff-interp-common.hh b/src/hb-cff-interp-common.hh index f2ccc2bdd..9b595ff1a 100644 --- a/src/hb-cff-interp-common.hh +++ b/src/hb-cff-interp-common.hh @@ -391,8 +391,22 @@ struct SubByteStr inline operator ByteStr (void) const { return ByteStr (str, offset, str.len - offset); } - inline bool avail (unsigned int count=1) const { return str.check_limit (offset, count); } - inline void inc (unsigned int count=1) { offset += count; assert (count <= str.len); } + inline bool avail (unsigned int count=1) const + { + return (!in_error () && str.check_limit (offset, count)); + } + inline void inc (unsigned int count=1) + { + if (likely (!in_error () && (offset <= str.len) && (offset + count <= str.len))) + { + offset += count; + } + else + { + offset = str.len; + set_error (); + } + } inline void set_error (void) { error = true; } inline bool in_error (void) const { return error; } diff --git a/src/hb-cff-interp-dict-common.hh b/src/hb-cff-interp-dict-common.hh index 2822af40c..63ca685ec 100644 --- a/src/hb-cff-interp-dict-common.hh +++ b/src/hb-cff-interp-dict-common.hh @@ -28,6 +28,7 @@ #include "hb-cff-interp-common.hh" #include +#include namespace CFF { @@ -105,20 +106,21 @@ struct DictOpSet : OpSet static inline double parse_bcd (SubByteStr& substr) { - double v = 0.0; - bool neg = false; double int_part = 0; - long frac_part = 0; - unsigned int frac_count = 0; + uint64_t frac_part = 0; + uint32_t frac_count = 0; bool exp_neg = false; - unsigned int exp_part = 0; + uint32_t exp_part = 0; + bool exp_overflow = false; enum Part { INT_PART=0, FRAC_PART, EXP_PART } part = INT_PART; enum Nibble { DECIMAL=10, EXP_POS, EXP_NEG, RESERVED, NEG, END }; + const uint64_t MAX_FRACT = 0xFFFFFFFFFFFFFllu; /* 1^52-1 */ + const uint32_t MAX_EXP = 0x7FFu; /* 1^11-1 */ double value = 0.0; unsigned char byte = 0; - for (unsigned int i = 0;; i++) + for (uint32_t i = 0;; i++) { char d; if ((i & 1) == 0) @@ -139,12 +141,25 @@ struct DictOpSet : OpSet { case RESERVED: substr.set_error (); - return v; + return value; case END: value = (double)(neg? -int_part: int_part); if (frac_count > 0) - value += (frac_part / pow (10.0, (double)frac_count)); + { + double frac = (frac_part / pow (10.0, (double)frac_count)); + if (neg) frac = -frac; + value += frac; + } + if (unlikely (exp_overflow)) + { + if (value == 0.0) + return value; + if (exp_neg) + return neg? -DBL_MIN: DBL_MIN; + else + return neg? -DBL_MAX: DBL_MAX; + } if (exp_part != 0) { if (exp_neg) @@ -167,7 +182,7 @@ struct DictOpSet : OpSet if (part != INT_PART) { substr.set_error (); - return v; + return value; } part = FRAC_PART; break; @@ -180,7 +195,7 @@ struct DictOpSet : OpSet if (part == EXP_PART) { substr.set_error (); - return v; + return value; } part = EXP_PART; break; @@ -193,18 +208,26 @@ struct DictOpSet : OpSet break; case FRAC_PART: - frac_part = (frac_part * 10) + d; - frac_count++; + if (likely ((frac_part <= MAX_FRACT / 10))) + { + frac_part = (frac_part * 10) + (unsigned)d; + frac_count++; + } break; case EXP_PART: - exp_part = (exp_part * 10) + d; + if (likely (exp_part * 10) + d <= MAX_EXP) + { + exp_part = (exp_part * 10) + d; + } + else + exp_overflow = true; break; } } } - return v; + return value; } static inline bool is_hint_op (OpCode op) diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5672006905757696 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5672006905757696 new file mode 100644 index 000000000..cb5fb83da Binary files /dev/null and b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5672006905757696 differ diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5768186323009536 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5768186323009536 new file mode 100644 index 000000000..858604d71 Binary files /dev/null and b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5768186323009536 differ