1*1f5207b7SJohn Levon /*
2*1f5207b7SJohn Levon  * Copyright (C) 2010 Dan Carpenter.
3*1f5207b7SJohn Levon  *
4*1f5207b7SJohn Levon  * This program is free software; you can redistribute it and/or
5*1f5207b7SJohn Levon  * modify it under the terms of the GNU General Public License
6*1f5207b7SJohn Levon  * as published by the Free Software Foundation; either version 2
7*1f5207b7SJohn Levon  * of the License, or (at your option) any later version.
8*1f5207b7SJohn Levon  *
9*1f5207b7SJohn Levon  * This program is distributed in the hope that it will be useful,
10*1f5207b7SJohn Levon  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11*1f5207b7SJohn Levon  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12*1f5207b7SJohn Levon  * GNU General Public License for more details.
13*1f5207b7SJohn Levon  *
14*1f5207b7SJohn Levon  * You should have received a copy of the GNU General Public License
15*1f5207b7SJohn Levon  * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
16*1f5207b7SJohn Levon  */
17*1f5207b7SJohn Levon 
18*1f5207b7SJohn Levon #include <stdlib.h>
19*1f5207b7SJohn Levon #include "parse.h"
20*1f5207b7SJohn Levon #include "smatch.h"
21*1f5207b7SJohn Levon #include "smatch_slist.h"
22*1f5207b7SJohn Levon #include "smatch_extra.h"
23*1f5207b7SJohn Levon 
24*1f5207b7SJohn Levon struct limiter {
25*1f5207b7SJohn Levon 	int buf_arg;
26*1f5207b7SJohn Levon 	int limit_arg;
27*1f5207b7SJohn Levon };
28*1f5207b7SJohn Levon static struct limiter b0_l2 = {0, 2};
29*1f5207b7SJohn Levon static struct limiter b1_l2 = {1, 2};
30*1f5207b7SJohn Levon 
31*1f5207b7SJohn Levon struct string_list *ignored_structs;
32*1f5207b7SJohn Levon 
get_the_max(struct expression * expr,sval_t * sval)33*1f5207b7SJohn Levon static int get_the_max(struct expression *expr, sval_t *sval)
34*1f5207b7SJohn Levon {
35*1f5207b7SJohn Levon 	struct range_list *rl;
36*1f5207b7SJohn Levon 
37*1f5207b7SJohn Levon 	if (get_hard_max(expr, sval))
38*1f5207b7SJohn Levon 		return 1;
39*1f5207b7SJohn Levon 	if (!option_spammy)
40*1f5207b7SJohn Levon 		return 0;
41*1f5207b7SJohn Levon 	if (get_fuzzy_max(expr, sval))
42*1f5207b7SJohn Levon 		return 1;
43*1f5207b7SJohn Levon 	if (!get_user_rl(expr, &rl))
44*1f5207b7SJohn Levon 		return 0;
45*1f5207b7SJohn Levon 	*sval = rl_max(rl);
46*1f5207b7SJohn Levon 	return 1;
47*1f5207b7SJohn Levon }
48*1f5207b7SJohn Levon 
bytes_to_end_of_struct(struct expression * expr)49*1f5207b7SJohn Levon static int bytes_to_end_of_struct(struct expression *expr)
50*1f5207b7SJohn Levon {
51*1f5207b7SJohn Levon 	struct expression *deref;
52*1f5207b7SJohn Levon 	struct symbol *type;
53*1f5207b7SJohn Levon 	int struct_bytes;
54*1f5207b7SJohn Levon 	int offset;
55*1f5207b7SJohn Levon 
56*1f5207b7SJohn Levon 	if (expr->type == EXPR_PREOP && expr->op == '&')
57*1f5207b7SJohn Levon 		expr = strip_parens(expr->unop);
58*1f5207b7SJohn Levon 	else {
59*1f5207b7SJohn Levon 		type = get_type(expr);
60*1f5207b7SJohn Levon 		if (!type || type->type != SYM_ARRAY)
61*1f5207b7SJohn Levon 			return 0;
62*1f5207b7SJohn Levon 	}
63*1f5207b7SJohn Levon 	if (expr->type != EXPR_DEREF || !expr->member)
64*1f5207b7SJohn Levon 		return 0;
65*1f5207b7SJohn Levon 	deref = expr->deref;
66*1f5207b7SJohn Levon 	if (deref->type == EXPR_PREOP && deref->op == '*')
67*1f5207b7SJohn Levon 		deref = deref->unop;
68*1f5207b7SJohn Levon 	struct_bytes = get_array_size_bytes_max(deref);
69*1f5207b7SJohn Levon 	if (struct_bytes <= 0) {
70*1f5207b7SJohn Levon 		type = get_type(expr->deref);
71*1f5207b7SJohn Levon 		struct_bytes = type_bytes(type);
72*1f5207b7SJohn Levon 	}
73*1f5207b7SJohn Levon 	offset = get_member_offset_from_deref(expr);
74*1f5207b7SJohn Levon 	if (offset <= 0)
75*1f5207b7SJohn Levon 		return 0;
76*1f5207b7SJohn Levon 	return struct_bytes - expr->member_offset;
77*1f5207b7SJohn Levon }
78*1f5207b7SJohn Levon 
size_of_union(struct expression * expr)79*1f5207b7SJohn Levon static int size_of_union(struct expression *expr)
80*1f5207b7SJohn Levon {
81*1f5207b7SJohn Levon 	struct symbol *type;
82*1f5207b7SJohn Levon 
83*1f5207b7SJohn Levon 	if (expr->type != EXPR_PREOP || expr->op != '&')
84*1f5207b7SJohn Levon 		return 0;
85*1f5207b7SJohn Levon 	expr = strip_parens(expr->unop);
86*1f5207b7SJohn Levon 	if (expr->type != EXPR_DEREF || !expr->member)
87*1f5207b7SJohn Levon 		return 0;
88*1f5207b7SJohn Levon 	expr = expr->unop;
89*1f5207b7SJohn Levon 	type = get_type(expr);
90*1f5207b7SJohn Levon 	if (!type || type->type != SYM_UNION)
91*1f5207b7SJohn Levon 		return 0;
92*1f5207b7SJohn Levon 	return type_bytes(type);
93*1f5207b7SJohn Levon }
94*1f5207b7SJohn Levon 
is_likely_multiple(int has,int needed,struct expression * limit)95*1f5207b7SJohn Levon static int is_likely_multiple(int has, int needed, struct expression *limit)
96*1f5207b7SJohn Levon {
97*1f5207b7SJohn Levon 	sval_t mult;
98*1f5207b7SJohn Levon 
99*1f5207b7SJohn Levon 	limit = strip_parens(limit);
100*1f5207b7SJohn Levon 	if (limit->type != EXPR_BINOP || limit->op != '*')
101*1f5207b7SJohn Levon 		return 0;
102*1f5207b7SJohn Levon 	if (!get_value(limit->left, &mult))
103*1f5207b7SJohn Levon 		return 0;
104*1f5207b7SJohn Levon 	if (has * mult.value == needed)
105*1f5207b7SJohn Levon 		return 1;
106*1f5207b7SJohn Levon 	if (!get_value(limit->right, &mult))
107*1f5207b7SJohn Levon 		return 0;
108*1f5207b7SJohn Levon 	if (has * mult.value == needed)
109*1f5207b7SJohn Levon 		return 1;
110*1f5207b7SJohn Levon 
111*1f5207b7SJohn Levon 	return 0;
112*1f5207b7SJohn Levon }
113*1f5207b7SJohn Levon 
name_in_union(struct symbol * type,const char * name)114*1f5207b7SJohn Levon static int name_in_union(struct symbol *type, const char *name)
115*1f5207b7SJohn Levon {
116*1f5207b7SJohn Levon 	struct symbol *tmp;
117*1f5207b7SJohn Levon 
118*1f5207b7SJohn Levon 	if (type->type == SYM_NODE)
119*1f5207b7SJohn Levon 		type = get_real_base_type(type);
120*1f5207b7SJohn Levon 	if (!type || type->type != SYM_UNION)
121*1f5207b7SJohn Levon 		return 0;
122*1f5207b7SJohn Levon 
123*1f5207b7SJohn Levon 	FOR_EACH_PTR(type->symbol_list, tmp) {
124*1f5207b7SJohn Levon 		if (tmp->ident &&
125*1f5207b7SJohn Levon 		    strcmp(name, tmp->ident->name) == 0)
126*1f5207b7SJohn Levon 			return 1;
127*1f5207b7SJohn Levon 	} END_FOR_EACH_PTR(tmp);
128*1f5207b7SJohn Levon 
129*1f5207b7SJohn Levon 	return 0;
130*1f5207b7SJohn Levon }
131*1f5207b7SJohn Levon 
ends_on_struct_member_boundary(struct expression * expr,int needed)132*1f5207b7SJohn Levon static int ends_on_struct_member_boundary(struct expression *expr, int needed)
133*1f5207b7SJohn Levon {
134*1f5207b7SJohn Levon 	struct symbol *type, *tmp;
135*1f5207b7SJohn Levon 	int offset;
136*1f5207b7SJohn Levon 	int size;
137*1f5207b7SJohn Levon 	int found = 0;
138*1f5207b7SJohn Levon 
139*1f5207b7SJohn Levon 	expr = strip_expr(expr);
140*1f5207b7SJohn Levon 	if (expr->type == EXPR_PREOP && expr->op == '&') {
141*1f5207b7SJohn Levon 		expr = strip_parens(expr->unop);
142*1f5207b7SJohn Levon 	} else {
143*1f5207b7SJohn Levon 		type = get_type(expr);
144*1f5207b7SJohn Levon 		if (!type || type->type != SYM_ARRAY)
145*1f5207b7SJohn Levon 			return 0;
146*1f5207b7SJohn Levon 	}
147*1f5207b7SJohn Levon 	if (expr->type != EXPR_DEREF || !expr->member)
148*1f5207b7SJohn Levon 		return 0;
149*1f5207b7SJohn Levon 
150*1f5207b7SJohn Levon 	type = get_type(expr->unop);
151*1f5207b7SJohn Levon 	if (!type)
152*1f5207b7SJohn Levon 		return 0;
153*1f5207b7SJohn Levon 	if (type->type == SYM_UNION) {
154*1f5207b7SJohn Levon 		struct expression *unop = strip_expr(expr->unop);
155*1f5207b7SJohn Levon 
156*1f5207b7SJohn Levon 		if (unop->type != EXPR_DEREF)
157*1f5207b7SJohn Levon 			return 0;
158*1f5207b7SJohn Levon 		type = get_type(unop->unop);
159*1f5207b7SJohn Levon 		if (!type)
160*1f5207b7SJohn Levon 			return 0;
161*1f5207b7SJohn Levon 	}
162*1f5207b7SJohn Levon 	if (type->type != SYM_STRUCT)
163*1f5207b7SJohn Levon 		return 0;
164*1f5207b7SJohn Levon 
165*1f5207b7SJohn Levon 	offset = 0;
166*1f5207b7SJohn Levon 	FOR_EACH_PTR(type->symbol_list, tmp) {
167*1f5207b7SJohn Levon 		if (!found) {
168*1f5207b7SJohn Levon 			if ((tmp->ident &&
169*1f5207b7SJohn Levon 			     strcmp(expr->member->name, tmp->ident->name) == 0) ||
170*1f5207b7SJohn Levon 			    name_in_union(tmp, expr->member->name))
171*1f5207b7SJohn Levon 				found = 1;
172*1f5207b7SJohn Levon 
173*1f5207b7SJohn Levon 			offset = ALIGN(offset, tmp->ctype.alignment);
174*1f5207b7SJohn Levon 
175*1f5207b7SJohn Levon 			offset += type_bytes(tmp);
176*1f5207b7SJohn Levon 			size = type_bytes(tmp);
177*1f5207b7SJohn Levon 			continue;
178*1f5207b7SJohn Levon 		}
179*1f5207b7SJohn Levon 
180*1f5207b7SJohn Levon 		/* if there is a hole then fail. */
181*1f5207b7SJohn Levon 		if (offset != ALIGN(offset, tmp->ctype.alignment))
182*1f5207b7SJohn Levon 			return 0;
183*1f5207b7SJohn Levon 		offset += type_bytes(tmp);
184*1f5207b7SJohn Levon 		size += type_bytes(tmp);
185*1f5207b7SJohn Levon 
186*1f5207b7SJohn Levon 		if (size == needed)
187*1f5207b7SJohn Levon 			return 1;
188*1f5207b7SJohn Levon 		if (size > needed)
189*1f5207b7SJohn Levon 			return 0;
190*1f5207b7SJohn Levon 	} END_FOR_EACH_PTR(tmp);
191*1f5207b7SJohn Levon 	return 0;
192*1f5207b7SJohn Levon }
193*1f5207b7SJohn Levon 
is_one_element_array(struct expression * expr)194*1f5207b7SJohn Levon static int is_one_element_array(struct expression *expr)
195*1f5207b7SJohn Levon {
196*1f5207b7SJohn Levon 	struct symbol *type;
197*1f5207b7SJohn Levon 	sval_t sval;
198*1f5207b7SJohn Levon 
199*1f5207b7SJohn Levon 	if (expr->type == EXPR_PREOP && expr->op == '&')
200*1f5207b7SJohn Levon 		expr = expr->unop;
201*1f5207b7SJohn Levon 	if (expr->type == EXPR_BINOP) /* array elements foo[5] */
202*1f5207b7SJohn Levon 		return 0;
203*1f5207b7SJohn Levon 
204*1f5207b7SJohn Levon 	type = get_type(expr);
205*1f5207b7SJohn Levon 	if (!type)
206*1f5207b7SJohn Levon 		return 0;
207*1f5207b7SJohn Levon 	if (!type || type->type != SYM_ARRAY)
208*1f5207b7SJohn Levon 		return 0;
209*1f5207b7SJohn Levon 
210*1f5207b7SJohn Levon 	if (!get_implied_value(type->array_size, &sval))
211*1f5207b7SJohn Levon 		return 0;
212*1f5207b7SJohn Levon 
213*1f5207b7SJohn Levon 	if (sval.value == 1)
214*1f5207b7SJohn Levon 		return 1;
215*1f5207b7SJohn Levon 	return 0;
216*1f5207b7SJohn Levon }
217*1f5207b7SJohn Levon 
is_ignored_struct(struct expression * expr)218*1f5207b7SJohn Levon static int is_ignored_struct(struct expression *expr)
219*1f5207b7SJohn Levon {
220*1f5207b7SJohn Levon 	struct symbol *type;
221*1f5207b7SJohn Levon 
222*1f5207b7SJohn Levon 	type = get_type(expr);
223*1f5207b7SJohn Levon 	if (!type)
224*1f5207b7SJohn Levon 		return 0;
225*1f5207b7SJohn Levon 	if (type->type == SYM_PTR)
226*1f5207b7SJohn Levon 		type = get_real_base_type(type);
227*1f5207b7SJohn Levon 	if (type->type != SYM_STRUCT)
228*1f5207b7SJohn Levon 		return 0;
229*1f5207b7SJohn Levon 	if (!type->ident)
230*1f5207b7SJohn Levon 		return 0;
231*1f5207b7SJohn Levon 	if (list_has_string(ignored_structs, type->ident->name))
232*1f5207b7SJohn Levon 		return 1;
233*1f5207b7SJohn Levon 	return 0;
234*1f5207b7SJohn Levon }
235*1f5207b7SJohn Levon 
match_limited(const char * fn,struct expression * expr,void * _limiter)236*1f5207b7SJohn Levon static void match_limited(const char *fn, struct expression *expr, void *_limiter)
237*1f5207b7SJohn Levon {
238*1f5207b7SJohn Levon 	struct limiter *limiter = (struct limiter *)_limiter;
239*1f5207b7SJohn Levon 	struct expression *dest;
240*1f5207b7SJohn Levon 	struct expression *limit;
241*1f5207b7SJohn Levon 	char *dest_name = NULL;
242*1f5207b7SJohn Levon 	sval_t needed;
243*1f5207b7SJohn Levon 	int has;
244*1f5207b7SJohn Levon 
245*1f5207b7SJohn Levon 	dest = get_argument_from_call_expr(expr->args, limiter->buf_arg);
246*1f5207b7SJohn Levon 	limit = get_argument_from_call_expr(expr->args, limiter->limit_arg);
247*1f5207b7SJohn Levon 	if (!get_the_max(limit, &needed))
248*1f5207b7SJohn Levon 		return;
249*1f5207b7SJohn Levon 	has = get_array_size_bytes_max(dest);
250*1f5207b7SJohn Levon 	if (!has)
251*1f5207b7SJohn Levon 		return;
252*1f5207b7SJohn Levon 	if (has >= needed.value)
253*1f5207b7SJohn Levon 		return;
254*1f5207b7SJohn Levon 
255*1f5207b7SJohn Levon 	if (needed.value == bytes_to_end_of_struct(dest))
256*1f5207b7SJohn Levon 		return;
257*1f5207b7SJohn Levon 
258*1f5207b7SJohn Levon 	if (needed.value <= size_of_union(dest))
259*1f5207b7SJohn Levon 		return;
260*1f5207b7SJohn Levon 
261*1f5207b7SJohn Levon 	if (is_likely_multiple(has, needed.value, limit))
262*1f5207b7SJohn Levon 		return;
263*1f5207b7SJohn Levon 
264*1f5207b7SJohn Levon 	if (ends_on_struct_member_boundary(dest, needed.value))
265*1f5207b7SJohn Levon 		return;
266*1f5207b7SJohn Levon 
267*1f5207b7SJohn Levon 	if (is_one_element_array(dest))
268*1f5207b7SJohn Levon 		return;
269*1f5207b7SJohn Levon 
270*1f5207b7SJohn Levon 	if (is_ignored_struct(dest))
271*1f5207b7SJohn Levon 		return;
272*1f5207b7SJohn Levon 
273*1f5207b7SJohn Levon 	dest_name = expr_to_str(dest);
274*1f5207b7SJohn Levon 	sm_error("%s() '%s' too small (%d vs %s)", fn, dest_name, has, sval_to_str(needed));
275*1f5207b7SJohn Levon 	free_string(dest_name);
276*1f5207b7SJohn Levon }
277*1f5207b7SJohn Levon 
register_funcs_from_file(void)278*1f5207b7SJohn Levon static void register_funcs_from_file(void)
279*1f5207b7SJohn Levon {
280*1f5207b7SJohn Levon 	char name[256];
281*1f5207b7SJohn Levon 	struct token *token;
282*1f5207b7SJohn Levon 	const char *func;
283*1f5207b7SJohn Levon 	int size, buf;
284*1f5207b7SJohn Levon 	struct limiter *limiter;
285*1f5207b7SJohn Levon 
286*1f5207b7SJohn Levon 	snprintf(name, 256, "%s.sizeof_param", option_project_str);
287*1f5207b7SJohn Levon 	name[255] = '\0';
288*1f5207b7SJohn Levon 	token = get_tokens_file(name);
289*1f5207b7SJohn Levon 	if (!token)
290*1f5207b7SJohn Levon 		return;
291*1f5207b7SJohn Levon 	if (token_type(token) != TOKEN_STREAMBEGIN)
292*1f5207b7SJohn Levon 		return;
293*1f5207b7SJohn Levon 	token = token->next;
294*1f5207b7SJohn Levon 	while (token_type(token) != TOKEN_STREAMEND) {
295*1f5207b7SJohn Levon 		if (token_type(token) != TOKEN_IDENT)
296*1f5207b7SJohn Levon 			break;
297*1f5207b7SJohn Levon 		func = show_ident(token->ident);
298*1f5207b7SJohn Levon 
299*1f5207b7SJohn Levon 		token = token->next;
300*1f5207b7SJohn Levon 		if (token_type(token) != TOKEN_NUMBER)
301*1f5207b7SJohn Levon 			break;
302*1f5207b7SJohn Levon 		size = atoi(token->number);
303*1f5207b7SJohn Levon 
304*1f5207b7SJohn Levon 		token = token->next;
305*1f5207b7SJohn Levon 		if (token_type(token) == TOKEN_SPECIAL) {
306*1f5207b7SJohn Levon 			if (token->special != '-')
307*1f5207b7SJohn Levon 				break;
308*1f5207b7SJohn Levon 			token = token->next;
309*1f5207b7SJohn Levon 			if (token_type(token) != TOKEN_NUMBER)
310*1f5207b7SJohn Levon 				break;
311*1f5207b7SJohn Levon 			token = token->next;
312*1f5207b7SJohn Levon 			continue;
313*1f5207b7SJohn Levon 
314*1f5207b7SJohn Levon 		}
315*1f5207b7SJohn Levon 		if (token_type(token) != TOKEN_NUMBER)
316*1f5207b7SJohn Levon 			break;
317*1f5207b7SJohn Levon 		buf = atoi(token->number);
318*1f5207b7SJohn Levon 
319*1f5207b7SJohn Levon 		limiter = malloc(sizeof(*limiter));
320*1f5207b7SJohn Levon 		limiter->limit_arg = size;
321*1f5207b7SJohn Levon 		limiter->buf_arg = buf;
322*1f5207b7SJohn Levon 
323*1f5207b7SJohn Levon 		add_function_hook(func, &match_limited, limiter);
324*1f5207b7SJohn Levon 
325*1f5207b7SJohn Levon 		token = token->next;
326*1f5207b7SJohn Levon 	}
327*1f5207b7SJohn Levon 	if (token_type(token) != TOKEN_STREAMEND)
328*1f5207b7SJohn Levon 		sm_perror("parsing '%s'", name);
329*1f5207b7SJohn Levon 	clear_token_alloc();
330*1f5207b7SJohn Levon }
331*1f5207b7SJohn Levon 
register_ignored_structs_from_file(void)332*1f5207b7SJohn Levon static void register_ignored_structs_from_file(void)
333*1f5207b7SJohn Levon {
334*1f5207b7SJohn Levon 	char name[256];
335*1f5207b7SJohn Levon 	struct token *token;
336*1f5207b7SJohn Levon 	const char *struct_type;
337*1f5207b7SJohn Levon 
338*1f5207b7SJohn Levon 	snprintf(name, 256, "%s.ignore_memcpy_struct_overflows", option_project_str);
339*1f5207b7SJohn Levon 	name[255] = '\0';
340*1f5207b7SJohn Levon 	token = get_tokens_file(name);
341*1f5207b7SJohn Levon 	if (!token)
342*1f5207b7SJohn Levon 		return;
343*1f5207b7SJohn Levon 	if (token_type(token) != TOKEN_STREAMBEGIN)
344*1f5207b7SJohn Levon 		return;
345*1f5207b7SJohn Levon 	token = token->next;
346*1f5207b7SJohn Levon 	while (token_type(token) != TOKEN_STREAMEND) {
347*1f5207b7SJohn Levon 		if (token_type(token) != TOKEN_IDENT)
348*1f5207b7SJohn Levon 			return;
349*1f5207b7SJohn Levon 
350*1f5207b7SJohn Levon 		struct_type = show_ident(token->ident);
351*1f5207b7SJohn Levon 		insert_string(&ignored_structs, alloc_string(struct_type));
352*1f5207b7SJohn Levon 
353*1f5207b7SJohn Levon 		token = token->next;
354*1f5207b7SJohn Levon 	}
355*1f5207b7SJohn Levon 	clear_token_alloc();
356*1f5207b7SJohn Levon }
357*1f5207b7SJohn Levon 
check_memcpy_overflow(int id)358*1f5207b7SJohn Levon void check_memcpy_overflow(int id)
359*1f5207b7SJohn Levon {
360*1f5207b7SJohn Levon 	register_funcs_from_file();
361*1f5207b7SJohn Levon 	register_ignored_structs_from_file();
362*1f5207b7SJohn Levon 	add_function_hook("memcmp", &match_limited, &b0_l2);
363*1f5207b7SJohn Levon 	add_function_hook("memcmp", &match_limited, &b1_l2);
364*1f5207b7SJohn Levon 	if (option_project == PROJ_KERNEL) {
365*1f5207b7SJohn Levon 		add_function_hook("copy_to_user", &match_limited, &b1_l2);
366*1f5207b7SJohn Levon 		add_function_hook("_copy_to_user", &match_limited, &b1_l2);
367*1f5207b7SJohn Levon 		add_function_hook("__copy_to_user", &match_limited, &b1_l2);
368*1f5207b7SJohn Levon 		add_function_hook("copy_from_user", &match_limited, &b0_l2);
369*1f5207b7SJohn Levon 		add_function_hook("_copy_from_user", &match_limited, &b0_l2);
370*1f5207b7SJohn Levon 		add_function_hook("__copy_from_user", &match_limited, &b0_l2);
371*1f5207b7SJohn Levon 	}
372*1f5207b7SJohn Levon }
373