Commit | Line | Data |
---|---|---|
e940f6d8 LLB |
1 | From 7b64eb285dd937b34df71c95188301be50dd1409 Mon Sep 17 00:00:00 2001 |
2 | From: Michael Catanzaro <mcatanzaro@gnome.org> | |
3 | Date: Wed, 12 Aug 2020 13:54:15 -0500 | |
4 | Subject: [PATCH] libcroco: Limit recursion in block and any productions | |
5 | (CVE-2020-12825) | |
6 | ||
7 | If we don't have any limits, we can recurse forever and overflow the | |
8 | stack. | |
9 | ||
10 | This is per https://gitlab.gnome.org/Archive/libcroco/-/issues/8 | |
11 | ||
12 | https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1404 | |
13 | --- | |
14 | src/st/croco/cr-parser.c | 44 ++++++++++++++++++++++++++-------------- | |
15 | 1 file changed, 29 insertions(+), 15 deletions(-) | |
16 | ||
17 | diff --git a/src/st/croco/cr-parser.c b/src/st/croco/cr-parser.c | |
18 | index 07f4ed9e8b..8304b75614 100644 | |
19 | --- a/src/cr-parser.c | |
20 | +++ b/src/cr-parser.c | |
21 | @@ -136,6 +136,8 @@ struct _CRParserPriv { | |
22 | ||
23 | #define CHARS_TAB_SIZE 12 | |
24 | ||
25 | +#define RECURSIVE_CALLERS_LIMIT 100 | |
26 | + | |
27 | /** | |
28 | * IS_NUM: | |
29 | *@a_char: the char to test. | |
30 | @@ -343,9 +345,11 @@ static enum CRStatus cr_parser_parse_selector_core (CRParser * a_this); | |
31 | ||
32 | static enum CRStatus cr_parser_parse_declaration_core (CRParser * a_this); | |
33 | ||
34 | -static enum CRStatus cr_parser_parse_any_core (CRParser * a_this); | |
35 | +static enum CRStatus cr_parser_parse_any_core (CRParser * a_this, | |
36 | + guint n_calls); | |
37 | ||
38 | -static enum CRStatus cr_parser_parse_block_core (CRParser * a_this); | |
39 | +static enum CRStatus cr_parser_parse_block_core (CRParser * a_this, | |
40 | + guint n_calls); | |
41 | ||
42 | static enum CRStatus cr_parser_parse_value_core (CRParser * a_this); | |
43 | ||
44 | @@ -783,7 +787,7 @@ cr_parser_parse_atrule_core (CRParser * a_this) | |
45 | cr_parser_try_to_skip_spaces_and_comments (a_this); | |
46 | ||
47 | do { | |
48 | - status = cr_parser_parse_any_core (a_this); | |
49 | + status = cr_parser_parse_any_core (a_this, 0); | |
50 | } while (status == CR_OK); | |
51 | ||
52 | status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, | |
53 | @@ -794,7 +798,7 @@ cr_parser_parse_atrule_core (CRParser * a_this) | |
54 | cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, | |
55 | token); | |
56 | token = NULL; | |
57 | - status = cr_parser_parse_block_core (a_this); | |
58 | + status = cr_parser_parse_block_core (a_this, 0); | |
59 | CHECK_PARSING_STATUS (status, | |
60 | FALSE); | |
61 | goto done; | |
62 | @@ -929,11 +933,11 @@ cr_parser_parse_selector_core (CRParser * a_this) | |
63 | ||
64 | RECORD_INITIAL_POS (a_this, &init_pos); | |
65 | ||
66 | - status = cr_parser_parse_any_core (a_this); | |
67 | + status = cr_parser_parse_any_core (a_this, 0); | |
68 | CHECK_PARSING_STATUS (status, FALSE); | |
69 | ||
70 | do { | |
71 | - status = cr_parser_parse_any_core (a_this); | |
72 | + status = cr_parser_parse_any_core (a_this, 0); | |
73 | ||
74 | } while (status == CR_OK); | |
75 | ||
76 | @@ -955,10 +959,12 @@ cr_parser_parse_selector_core (CRParser * a_this) | |
77 | *in chapter 4.1 of the css2 spec. | |
78 | *block ::= '{' S* [ any | block | ATKEYWORD S* | ';' ]* '}' S*; | |
79 | *@param a_this the current instance of #CRParser. | |
80 | + *@param n_calls used to limit recursion depth | |
81 | *FIXME: code this function. | |
82 | */ | |
83 | static enum CRStatus | |
84 | -cr_parser_parse_block_core (CRParser * a_this) | |
85 | +cr_parser_parse_block_core (CRParser * a_this, | |
86 | + guint n_calls) | |
87 | { | |
88 | CRToken *token = NULL; | |
89 | CRInputPos init_pos; | |
90 | @@ -966,6 +972,9 @@ cr_parser_parse_block_core (CRParser * a_this) | |
91 | ||
92 | g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR); | |
93 | ||
94 | + if (n_calls > RECURSIVE_CALLERS_LIMIT) | |
95 | + return CR_ERROR; | |
96 | + | |
97 | RECORD_INITIAL_POS (a_this, &init_pos); | |
98 | ||
99 | status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token); | |
100 | @@ -995,13 +1004,13 @@ cr_parser_parse_block_core (CRParser * a_this) | |
101 | } else if (token->type == CBO_TK) { | |
102 | cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token); | |
103 | token = NULL; | |
104 | - status = cr_parser_parse_block_core (a_this); | |
105 | + status = cr_parser_parse_block_core (a_this, n_calls + 1); | |
106 | CHECK_PARSING_STATUS (status, FALSE); | |
107 | goto parse_block_content; | |
108 | } else { | |
109 | cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token); | |
110 | token = NULL; | |
111 | - status = cr_parser_parse_any_core (a_this); | |
112 | + status = cr_parser_parse_any_core (a_this, n_calls + 1); | |
113 | CHECK_PARSING_STATUS (status, FALSE); | |
114 | goto parse_block_content; | |
115 | } | |
116 | @@ -1108,7 +1117,7 @@ cr_parser_parse_value_core (CRParser * a_this) | |
117 | status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, | |
118 | token); | |
119 | token = NULL; | |
120 | - status = cr_parser_parse_block_core (a_this); | |
121 | + status = cr_parser_parse_block_core (a_this, 0); | |
122 | CHECK_PARSING_STATUS (status, FALSE); | |
123 | ref++; | |
124 | goto continue_parsing; | |
125 | @@ -1122,7 +1131,7 @@ cr_parser_parse_value_core (CRParser * a_this) | |
126 | status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, | |
127 | token); | |
128 | token = NULL; | |
129 | - status = cr_parser_parse_any_core (a_this); | |
130 | + status = cr_parser_parse_any_core (a_this, 0); | |
131 | if (status == CR_OK) { | |
132 | ref++; | |
133 | goto continue_parsing; | |
134 | @@ -1161,10 +1170,12 @@ cr_parser_parse_value_core (CRParser * a_this) | |
135 | * | FUNCTION | DASHMATCH | '(' any* ')' | '[' any* ']' ] S*; | |
136 | * | |
137 | *@param a_this the current instance of #CRParser. | |
138 | + *@param n_calls used to limit recursion depth | |
139 | *@return CR_OK upon successfull completion, an error code otherwise. | |
140 | */ | |
141 | static enum CRStatus | |
142 | -cr_parser_parse_any_core (CRParser * a_this) | |
143 | +cr_parser_parse_any_core (CRParser * a_this, | |
144 | + guint n_calls) | |
145 | { | |
146 | CRToken *token1 = NULL, | |
147 | *token2 = NULL; | |
148 | @@ -1173,6 +1184,9 @@ cr_parser_parse_any_core (CRParser * a_this) | |
149 | ||
150 | g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR); | |
151 | ||
152 | + if (n_calls > RECURSIVE_CALLERS_LIMIT) | |
153 | + return CR_ERROR; | |
154 | + | |
155 | RECORD_INITIAL_POS (a_this, &init_pos); | |
156 | ||
157 | status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token1); | |
158 | @@ -1211,7 +1225,7 @@ cr_parser_parse_any_core (CRParser * a_this) | |
159 | *We consider parameter as being an "any*" production. | |
160 | */ | |
161 | do { | |
162 | - status = cr_parser_parse_any_core (a_this); | |
163 | + status = cr_parser_parse_any_core (a_this, n_calls + 1); | |
164 | } while (status == CR_OK); | |
165 | ||
166 | ENSURE_PARSING_COND (status == CR_PARSING_ERROR); | |
167 | @@ -1236,7 +1250,7 @@ cr_parser_parse_any_core (CRParser * a_this) | |
168 | } | |
169 | ||
170 | do { | |
171 | - status = cr_parser_parse_any_core (a_this); | |
172 | + status = cr_parser_parse_any_core (a_this, n_calls + 1); | |
173 | } while (status == CR_OK); | |
174 | ||
175 | ENSURE_PARSING_COND (status == CR_PARSING_ERROR); | |
176 | @@ -1264,7 +1278,7 @@ cr_parser_parse_any_core (CRParser * a_this) | |
177 | } | |
178 | ||
179 | do { | |
180 | - status = cr_parser_parse_any_core (a_this); | |
181 | + status = cr_parser_parse_any_core (a_this, n_calls + 1); | |
182 | } while (status == CR_OK); | |
183 | ||
184 | ENSURE_PARSING_COND (status == CR_PARSING_ERROR); | |
185 | -- | |
186 | GitLab | |
187 |