diff options
Diffstat (limited to 'meta/recipes-graphics/harfbuzz/harfbuzz/CVE-2023-25193.patch')
-rw-r--r-- | meta/recipes-graphics/harfbuzz/harfbuzz/CVE-2023-25193.patch | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/meta/recipes-graphics/harfbuzz/harfbuzz/CVE-2023-25193.patch b/meta/recipes-graphics/harfbuzz/harfbuzz/CVE-2023-25193.patch new file mode 100644 index 0000000000..e4ac13dbad --- /dev/null +++ b/meta/recipes-graphics/harfbuzz/harfbuzz/CVE-2023-25193.patch | |||
@@ -0,0 +1,179 @@ | |||
1 | From 9c8e972dbecda93546038d24444d8216397d75a3 Mon Sep 17 00:00:00 2001 | ||
2 | From: Behdad Esfahbod <behdad@behdad.org> | ||
3 | Date: Mon, 6 Feb 2023 14:51:25 -0700 | ||
4 | Subject: [PATCH] [GPOS] Avoid O(n^2) behavior in mark-attachment | ||
5 | |||
6 | Upstream-Status: Backport from [https://github.com/harfbuzz/harfbuzz/commit/8708b9e081192786c027bb7f5f23d76dbe5c19e8] | ||
7 | Comment1: The Original Patch [https://github.com/harfbuzz/harfbuzz/commit/85be877925ddbf34f74a1229f3ca1716bb6170dc] causes regression and was reverted. This Patch completes the fix. | ||
8 | Comment2: The Patch contained files MarkBasePosFormat1.hh and MarkLigPosFormat1.hh which were moved from hb-ot-layout-gpos-table.hh as per https://github.com/harfbuzz/harfbuzz/commit/197d9a5c994eb41c8c89b7b958b26b1eacfeeb00 | ||
9 | CVE: CVE-2023-25193 | ||
10 | Signed-off-by: Siddharth Doshi <sdoshi@mvista.com> | ||
11 | Signed-off-by: Dhairya Nagodra <dnagodra@cisco.com> | ||
12 | |||
13 | --- | ||
14 | src/hb-ot-layout-gpos-table.hh | 103 +++++++++++++++++++++++---------- | ||
15 | src/hb-ot-layout-gsubgpos.hh | 5 +- | ||
16 | 2 files changed, 78 insertions(+), 30 deletions(-) | ||
17 | |||
18 | diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh | ||
19 | index 024312d..db5f9ae 100644 | ||
20 | --- a/src/hb-ot-layout-gpos-table.hh | ||
21 | +++ b/src/hb-ot-layout-gpos-table.hh | ||
22 | @@ -1458,6 +1458,25 @@ struct MarkBasePosFormat1 | ||
23 | |||
24 | const Coverage &get_coverage () const { return this+markCoverage; } | ||
25 | |||
26 | + static inline bool accept (hb_buffer_t *buffer, unsigned idx) | ||
27 | + { | ||
28 | + /* We only want to attach to the first of a MultipleSubst sequence. | ||
29 | + * https://github.com/harfbuzz/harfbuzz/issues/740 | ||
30 | + * Reject others... | ||
31 | + * ...but stop if we find a mark in the MultipleSubst sequence: | ||
32 | + * https://github.com/harfbuzz/harfbuzz/issues/1020 */ | ||
33 | + return !_hb_glyph_info_multiplied (&buffer->info[idx]) || | ||
34 | + 0 == _hb_glyph_info_get_lig_comp (&buffer->info[idx]) || | ||
35 | + (idx == 0 || | ||
36 | + _hb_glyph_info_is_mark (&buffer->info[idx - 1]) || | ||
37 | + !_hb_glyph_info_multiplied (&buffer->info[idx - 1]) || | ||
38 | + _hb_glyph_info_get_lig_id (&buffer->info[idx]) != | ||
39 | + _hb_glyph_info_get_lig_id (&buffer->info[idx - 1]) || | ||
40 | + _hb_glyph_info_get_lig_comp (&buffer->info[idx]) != | ||
41 | + _hb_glyph_info_get_lig_comp (&buffer->info[idx - 1]) + 1 | ||
42 | + ); | ||
43 | + } | ||
44 | + | ||
45 | bool apply (hb_ot_apply_context_t *c) const | ||
46 | { | ||
47 | TRACE_APPLY (this); | ||
48 | @@ -1465,37 +1484,46 @@ struct MarkBasePosFormat1 | ||
49 | unsigned int mark_index = (this+markCoverage).get_coverage (buffer->cur().codepoint); | ||
50 | if (likely (mark_index == NOT_COVERED)) return_trace (false); | ||
51 | |||
52 | - /* Now we search backwards for a non-mark glyph */ | ||
53 | + /* Now we search backwards for a non-mark glyph. | ||
54 | + * We don't use skippy_iter.prev() to avoid O(n^2) behavior. */ | ||
55 | + | ||
56 | hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; | ||
57 | - skippy_iter.reset (buffer->idx, 1); | ||
58 | skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); | ||
59 | - do { | ||
60 | - if (!skippy_iter.prev ()) return_trace (false); | ||
61 | - /* We only want to attach to the first of a MultipleSubst sequence. | ||
62 | - * https://github.com/harfbuzz/harfbuzz/issues/740 | ||
63 | - * Reject others... | ||
64 | - * ...but stop if we find a mark in the MultipleSubst sequence: | ||
65 | - * https://github.com/harfbuzz/harfbuzz/issues/1020 */ | ||
66 | - if (!_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx]) || | ||
67 | - 0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) || | ||
68 | - (skippy_iter.idx == 0 || | ||
69 | - _hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx - 1]) || | ||
70 | - _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]) != | ||
71 | - _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx - 1]) || | ||
72 | - _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) != | ||
73 | - _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx - 1]) + 1 | ||
74 | - )) | ||
75 | - break; | ||
76 | - skippy_iter.reject (); | ||
77 | - } while (true); | ||
78 | + unsigned j; | ||
79 | + for (j = buffer->idx; j > c->last_base_until; j--) | ||
80 | + { | ||
81 | + auto match = skippy_iter.match (buffer->info[j - 1]); | ||
82 | + if (match == skippy_iter.MATCH) | ||
83 | + { | ||
84 | + if (!accept (buffer, j - 1)) | ||
85 | + match = skippy_iter.SKIP; | ||
86 | + } | ||
87 | + if (match == skippy_iter.MATCH) | ||
88 | + { | ||
89 | + c->last_base = (signed) j - 1; | ||
90 | + break; | ||
91 | + } | ||
92 | + } | ||
93 | + c->last_base_until = buffer->idx; | ||
94 | + if (c->last_base == -1) | ||
95 | + { | ||
96 | + buffer->unsafe_to_concat_from_outbuffer (0, buffer->idx + 1); | ||
97 | + return_trace (false); | ||
98 | + } | ||
99 | + | ||
100 | + unsigned idx = (unsigned) c->last_base; | ||
101 | |||
102 | /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */ | ||
103 | - //if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { return_trace (false); } | ||
104 | + //if (!_hb_glyph_info_is_base_glyph (&buffer->info[idx])) { return_trace (false); } | ||
105 | |||
106 | - unsigned int base_index = (this+baseCoverage).get_coverage (buffer->info[skippy_iter.idx].codepoint); | ||
107 | - if (base_index == NOT_COVERED) return_trace (false); | ||
108 | + unsigned int base_index = (this+baseCoverage).get_coverage (buffer->info[idx].codepoint); | ||
109 | + if (base_index == NOT_COVERED) | ||
110 | + { | ||
111 | + buffer->unsafe_to_concat_from_outbuffer (idx, buffer->idx + 1); | ||
112 | + return_trace (false); | ||
113 | + } | ||
114 | |||
115 | - return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx)); | ||
116 | + return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, idx)); | ||
117 | } | ||
118 | |||
119 | bool subset (hb_subset_context_t *c) const | ||
120 | @@ -1587,15 +1615,32 @@ struct MarkLigPosFormat1 | ||
121 | if (likely (mark_index == NOT_COVERED)) return_trace (false); | ||
122 | |||
123 | /* Now we search backwards for a non-mark glyph */ | ||
124 | + | ||
125 | hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; | ||
126 | - skippy_iter.reset (buffer->idx, 1); | ||
127 | skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); | ||
128 | - if (!skippy_iter.prev ()) return_trace (false); | ||
129 | + | ||
130 | + unsigned j; | ||
131 | + for (j = buffer->idx; j > c->last_base_until; j--) | ||
132 | + { | ||
133 | + auto match = skippy_iter.match (buffer->info[j - 1]); | ||
134 | + if (match == skippy_iter.MATCH) | ||
135 | + { | ||
136 | + c->last_base = (signed) j - 1; | ||
137 | + break; | ||
138 | + } | ||
139 | + } | ||
140 | + c->last_base_until = buffer->idx; | ||
141 | + if (c->last_base == -1) | ||
142 | + { | ||
143 | + buffer->unsafe_to_concat_from_outbuffer (0, buffer->idx + 1); | ||
144 | + return_trace (false); | ||
145 | + } | ||
146 | + | ||
147 | + j = (unsigned) c->last_base; | ||
148 | |||
149 | /* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */ | ||
150 | - //if (!_hb_glyph_info_is_ligature (&buffer->info[skippy_iter.idx])) { return_trace (false); } | ||
151 | + //if (!_hb_glyph_info_is_ligature (&buffer->info[idx])) { return_trace (false); } | ||
152 | |||
153 | - unsigned int j = skippy_iter.idx; | ||
154 | unsigned int lig_index = (this+ligatureCoverage).get_coverage (buffer->info[j].codepoint); | ||
155 | if (lig_index == NOT_COVERED) return_trace (false); | ||
156 | |||
157 | diff --git a/src/hb-ot-layout-gsubgpos.hh b/src/hb-ot-layout-gsubgpos.hh | ||
158 | index 5a7e564..437123c 100644 | ||
159 | --- a/src/hb-ot-layout-gsubgpos.hh | ||
160 | +++ b/src/hb-ot-layout-gsubgpos.hh | ||
161 | @@ -503,6 +503,9 @@ struct hb_ot_apply_context_t : | ||
162 | uint32_t random_state; | ||
163 | |||
164 | |||
165 | + signed last_base = -1; // GPOS uses | ||
166 | + unsigned last_base_until = 0; // GPOS uses | ||
167 | + | ||
168 | hb_ot_apply_context_t (unsigned int table_index_, | ||
169 | hb_font_t *font_, | ||
170 | hb_buffer_t *buffer_) : | ||
171 | @@ -536,7 +539,7 @@ struct hb_ot_apply_context_t : | ||
172 | iter_context.init (this, true); | ||
173 | } | ||
174 | |||
175 | - void set_lookup_mask (hb_mask_t mask) { lookup_mask = mask; init_iters (); } | ||
176 | + void set_lookup_mask (hb_mask_t mask) { lookup_mask = mask; last_base = -1; last_base_until = 0; init_iters (); } | ||
177 | void set_auto_zwj (bool auto_zwj_) { auto_zwj = auto_zwj_; init_iters (); } | ||
178 | void set_auto_zwnj (bool auto_zwnj_) { auto_zwnj = auto_zwnj_; init_iters (); } | ||
179 | void set_random (bool random_) { random = random_; } | ||