xref: /illumos-gate/usr/src/tools/smatch/src/smatch_struct_assignment.c (revision 3b436d06bb95fd180ef7416b2b1b9972e2f2a513)
1 /*
2  * Copyright (C) 2014 Oracle.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
16  */
17 
18 /*
19  * This file started out by saying that if you have:
20  *
21  * 	struct foo one, two;
22  * 	...
23  * 	one = two;
24  *
25  * That's equivalent to saying:
26  *
27  * 	one.x = two.x;
28  * 	one.y = two.y;
29  *
30  * Turning an assignment like that into a bunch of small fake assignments is
31  * really useful.
32  *
33  * The call to memcpy(&one, &two, sizeof(foo)); is the same as "one = two;" so
34  * we can re-use the code.  And we may as well use it for memset() too.
35  * Assigning pointers is almost the same:
36  *
37  * 	p1 = p2;
38  *
39  * Is the same as:
40  *
41  * 	p1->x = p2->x;
42  * 	p1->y = p2->y;
43  *
44  * The problem is that you can go a bit crazy with pointers to pointers.
45  *
46  * 	p1->x->y->z->one->two->three = p2->x->y->z->one->two->three;
47  *
48  * I don't have a proper solution for this problem right now.  I just copy one
49  * level and don't nest.  It should handle limitted nesting but intelligently.
50  *
51  * The other thing is that you end up with a lot of garbage assignments where
52  * we record "x could be anything. x->y could be anything. x->y->z->a->b->c
53  * could *also* be anything!".  There should be a better way to filter this
54  * useless information.
55  *
56  */
57 
58 #include "scope.h"
59 #include "smatch.h"
60 #include "smatch_slist.h"
61 #include "smatch_extra.h"
62 
63 enum {
64 	COPY_NORMAL,
65 	COPY_MEMCPY,
66 	COPY_MEMSET,
67 };
68 
69 static struct symbol *get_struct_type(struct expression *expr)
70 {
71 	struct symbol *type;
72 
73 	type = get_type(expr);
74 	if (!type)
75 		return NULL;
76 	if (type->type == SYM_PTR) {
77 		type = get_real_base_type(type);
78 		if (!type)
79 			return NULL;
80 	}
81 	if (type->type == SYM_STRUCT)
82 		return type;
83 	if (type->type == SYM_UNION)
84 		return type;
85 	return NULL;
86 }
87 
88 static struct expression *get_right_base_expr(struct symbol *left_type, struct expression *right)
89 {
90 	struct symbol *struct_type;
91 
92 	if (!right)
93 		return NULL;
94 
95 	struct_type = get_struct_type(right);
96 	if (!struct_type)
97 		return NULL;
98 	if (struct_type != left_type)
99 		return NULL;
100 
101 	if (right->type == EXPR_PREOP && right->op == '&')
102 		right = strip_expr(right->unop);
103 
104 	if (right->type == EXPR_CALL)
105 		return NULL;
106 
107 	if (is_pointer(right))
108 		right = deref_expression(right);
109 
110 	return right;
111 }
112 
113 static struct expression *remove_addr(struct expression *expr)
114 {
115 	struct symbol *type;
116 
117 	expr = strip_expr(expr);
118 	if (!expr)
119 		return NULL;
120 
121 	if (expr->type == EXPR_PREOP && expr->op == '&')
122 		return strip_expr(expr->unop);
123 	type = get_type(expr);
124 	if (!type)
125 		return expr;
126 	if (type->type != SYM_PTR && type->type != SYM_ARRAY)
127 		return expr;
128 
129 	return deref_expression(expr);
130 }
131 
132 static struct expression *faked_expression;
133 struct expression *get_faked_expression(void)
134 {
135 	if (!__in_fake_assign)
136 		return NULL;
137 	return faked_expression;
138 }
139 
140 static void split_fake_expr(struct expression *expr)
141 {
142 	__in_fake_assign++;
143 	__in_fake_struct_assign++;
144 	__split_expr(expr);
145 	__in_fake_struct_assign--;
146 	__in_fake_assign--;
147 }
148 
149 static void handle_non_struct_assignments(struct expression *left, struct expression *right)
150 {
151 	struct symbol *type;
152 	struct expression *assign;
153 
154 	type = get_type(left);
155 	if (!type)
156 		return;
157 	if (type->type == SYM_PTR) {
158 		left = deref_expression(left);
159 		if (right)
160 			right = deref_expression(right);
161 		else
162 			right = unknown_value_expression(left);
163 		assign = assign_expression(left, '=', right);
164 		split_fake_expr(assign);
165 		return;
166 	}
167 	if (type->type != SYM_BASETYPE)
168 		return;
169 	right = strip_expr(right);
170 	type = get_type(right);
171 	if (!right || !type || type->type == SYM_ARRAY)
172 		right = unknown_value_expression(left);
173 	assign = assign_expression(left, '=', right);
174 	split_fake_expr(assign);
175 }
176 
177 static void set_inner_struct_members(int mode, struct expression *faked, struct expression *left, struct expression *right, struct symbol *member)
178 {
179 	struct expression *left_member;
180 	struct expression *right_member = NULL;  /* silence GCC */
181 	struct expression *assign;
182 	struct symbol *base = get_real_base_type(member);
183 	struct symbol *tmp;
184 
185 	if (member->ident) {
186 		left = member_expression(left, '.', member->ident);
187 		if (mode != COPY_MEMSET && right)
188 			right = member_expression(right, '.', member->ident);
189 	}
190 
191 	FOR_EACH_PTR(base->symbol_list, tmp) {
192 		struct symbol *type;
193 
194 		type = get_real_base_type(tmp);
195 		if (!type)
196 			continue;
197 
198 		if (type->type == SYM_ARRAY)
199 			continue;
200 		if (type->type == SYM_UNION || type->type == SYM_STRUCT) {
201 			set_inner_struct_members(mode, faked, left, right, tmp);
202 			continue;
203 		}
204 		if (!tmp->ident)
205 			continue;
206 
207 		left_member = member_expression(left, '.', tmp->ident);
208 
209 		switch (mode) {
210 		case COPY_NORMAL:
211 		case COPY_MEMCPY:
212 			if (right)
213 				right_member = member_expression(right, '.', tmp->ident);
214 			else
215 				right_member = unknown_value_expression(left_member);
216 			break;
217 		case COPY_MEMSET:
218 			right_member = right;
219 			break;
220 		}
221 
222 		assign = assign_expression(left_member, '=', right_member);
223 		split_fake_expr(assign);
224 	} END_FOR_EACH_PTR(tmp);
225 }
226 
227 static void __struct_members_copy(int mode, struct expression *faked,
228 				  struct expression *left,
229 				  struct expression *right)
230 {
231 	struct symbol *struct_type, *tmp, *type;
232 	struct expression *left_member;
233 	struct expression *right_member;
234 	struct expression *assign;
235 	int op = '.';
236 
237 
238 	if (__in_fake_assign)
239 		return;
240 	faked_expression = faked;
241 
242 	left = strip_expr(left);
243 	right = strip_expr(right);
244 
245 	struct_type = get_struct_type(left);
246 	if (!struct_type) {
247 		/*
248 		 * This is not a struct assignment obviously.  But this is where
249 		 * memcpy() is handled so it feels like a good place to add this
250 		 * code.
251 		 */
252 		handle_non_struct_assignments(left, right);
253 		goto done;
254 	}
255 
256 	if (is_pointer(left)) {
257 		left = deref_expression(left);
258 		op = '*';
259 	}
260 	if (mode != COPY_MEMSET)
261 		right = get_right_base_expr(struct_type, right);
262 
263 	FOR_EACH_PTR(struct_type->symbol_list, tmp) {
264 		type = get_real_base_type(tmp);
265 		if (!type)
266 			continue;
267 		if (type->type == SYM_ARRAY)
268 			continue;
269 
270 		if (type->type == SYM_UNION || type->type == SYM_STRUCT) {
271 			set_inner_struct_members(mode, faked, left, right, tmp);
272 			continue;
273 		}
274 
275 		if (!tmp->ident)
276 			continue;
277 
278 		left_member = member_expression(left, op, tmp->ident);
279 		right_member = NULL;
280 
281 		switch (mode) {
282 		case COPY_NORMAL:
283 		case COPY_MEMCPY:
284 			if (right)
285 				right_member = member_expression(right, op, tmp->ident);
286 			else
287 				right_member = unknown_value_expression(left_member);
288 			break;
289 		case COPY_MEMSET:
290 			right_member = right;
291 			break;
292 		}
293 		if (!right_member) {
294 			sm_perror("No right member");
295 			continue;
296 		}
297 		assign = assign_expression(left_member, '=', right_member);
298 		split_fake_expr(assign);
299 	} END_FOR_EACH_PTR(tmp);
300 
301 done:
302 	faked_expression = NULL;
303 }
304 
305 static int returns_zeroed_mem(struct expression *expr)
306 {
307 	char *fn;
308 
309 	if (expr->type != EXPR_CALL || expr->fn->type != EXPR_SYMBOL)
310 		return 0;
311 	fn = expr_to_var(expr->fn);
312 	if (!fn)
313 		return 0;
314 	if (strcmp(fn, "kcalloc") == 0)
315 		return 1;
316 	if (option_project == PROJ_KERNEL && strstr(fn, "zalloc"))
317 		return 1;
318 	return 0;
319 }
320 
321 static int copy_containter_states(struct expression *left, struct expression *right, int offset)
322 {
323 	char *left_name = NULL, *right_name = NULL;
324 	struct symbol *left_sym, *right_sym;
325 	struct sm_state *sm, *new_sm;
326 	int ret = 0;
327 	int len;
328 	char buf[64];
329 	char new_name[128];
330 
331 	right_name = expr_to_var_sym(right, &right_sym);
332 	if (!right_name || !right_sym)
333 		goto free;
334 	left_name = expr_to_var_sym(left, &left_sym);
335 	if (!left_name || !left_sym)
336 		goto free;
337 
338 	len = snprintf(buf, sizeof(buf), "%s(-%d)", right_name, offset);
339 	if (len >= sizeof(buf))
340 		goto free;
341 
342 	FOR_EACH_SM(__get_cur_stree(), sm) {
343 		if (sm->sym != right_sym)
344 			continue;
345 		if (strncmp(sm->name, buf, len) != 0)
346 			continue;
347 		snprintf(new_name, sizeof(new_name), "%s%s", left_name, sm->name + len);
348 		new_sm = clone_sm(sm);
349 		new_sm->name = alloc_sname(new_name);
350 		new_sm->sym = left_sym;
351 		__set_sm(new_sm);
352 		ret = 1;
353 	} END_FOR_EACH_SM(sm);
354 free:
355 	free_string(left_name);
356 	free_string(right_name);
357 	return ret;
358 }
359 
360 static int handle_param_offsets(struct expression *expr)
361 {
362 	struct expression *right;
363 	sval_t sval;
364 
365 	right = strip_expr(expr->right);
366 
367 	if (right->type != EXPR_BINOP || right->op != '-')
368 		return 0;
369 
370 	if (!get_value(right->right, &sval))
371 		return 0;
372 
373 	right = get_assigned_expr(right->left);
374 	if (!right)
375 		return 0;
376 	return copy_containter_states(expr->left, right, sval.value);
377 }
378 
379 static void returns_container_of(struct expression *expr, int param, char *key, char *value)
380 {
381 	struct expression *call, *arg;
382 	int offset;
383 
384 	if (expr->type != EXPR_ASSIGNMENT || expr->op != '=')
385 		return;
386 	call = strip_expr(expr->right);
387 	if (call->type != EXPR_CALL)
388 		return;
389 	if (param != -1)
390 		return;
391 	param = atoi(key);
392 	offset = atoi(value);
393 
394 	arg = get_argument_from_call_expr(call->args, param);
395 	if (!arg)
396 		return;
397 
398 	copy_containter_states(expr->left, arg, -offset);
399 }
400 
401 void __fake_struct_member_assignments(struct expression *expr)
402 {
403 	struct symbol *left_type;
404 
405 	if (expr->op != '=')
406 		return;
407 
408 	if (expr_is_zero(expr->right))
409 		return;
410 
411 	left_type = get_type(expr->left);
412 	if (!left_type ||
413 	    (left_type->type != SYM_PTR &&
414 	     left_type->type != SYM_STRUCT))
415 		return;
416 
417 	if (handle_param_offsets(expr))
418 		return;
419 
420 	if (returns_zeroed_mem(expr->right))
421 		__struct_members_copy(COPY_MEMSET, expr, expr->left, zero_expr());
422 	else
423 		__struct_members_copy(COPY_NORMAL, expr, expr->left, expr->right);
424 }
425 
426 static void match_memset(const char *fn, struct expression *expr, void *_size_arg)
427 {
428 	struct expression *buf;
429 	struct expression *val;
430 
431 	buf = get_argument_from_call_expr(expr->args, 0);
432 	val = get_argument_from_call_expr(expr->args, 1);
433 
434 	buf = strip_expr(buf);
435 	__struct_members_copy(COPY_MEMSET, expr, remove_addr(buf), val);
436 }
437 
438 static void match_memcpy(const char *fn, struct expression *expr, void *_arg)
439 {
440 	struct expression *dest;
441 	struct expression *src;
442 
443 	dest = get_argument_from_call_expr(expr->args, 0);
444 	src = get_argument_from_call_expr(expr->args, 1);
445 
446 	__struct_members_copy(COPY_MEMCPY, expr, remove_addr(dest), remove_addr(src));
447 }
448 
449 static void match_memdup(const char *fn, struct expression *call_expr,
450 			struct expression *expr, void *_unused)
451 {
452 	struct expression *left, *right, *arg;
453 
454 	if (!expr || expr->type != EXPR_ASSIGNMENT)
455 		return;
456 
457 	left = strip_expr(expr->left);
458 	right = strip_expr(expr->right);
459 
460 	if (right->type != EXPR_CALL)
461 		return;
462 	arg = get_argument_from_call_expr(right->args, 0);
463 	__struct_members_copy(COPY_MEMCPY, expr, left, arg);
464 }
465 
466 static void match_memcpy_unknown(const char *fn, struct expression *expr, void *_arg)
467 {
468 	struct expression *dest;
469 
470 	dest = get_argument_from_call_expr(expr->args, 0);
471 	__struct_members_copy(COPY_MEMCPY, expr, remove_addr(dest), NULL);
472 }
473 
474 static void match_sscanf(const char *fn, struct expression *expr, void *unused)
475 {
476 	struct expression *arg;
477 	int i;
478 
479 	i = -1;
480 	FOR_EACH_PTR(expr->args, arg) {
481 		if (++i < 2)
482 			continue;
483 		__struct_members_copy(COPY_MEMCPY, expr, remove_addr(arg), NULL);
484 	} END_FOR_EACH_PTR(arg);
485 }
486 
487 static void unop_expr(struct expression *expr)
488 {
489 	if (expr->op != SPECIAL_INCREMENT &&
490 	    expr->op != SPECIAL_DECREMENT)
491 		return;
492 
493 	if (!is_pointer(expr))
494 		return;
495 	faked_expression = expr;
496 	__struct_members_copy(COPY_MEMCPY, expr, expr->unop, NULL);
497 	faked_expression = NULL;
498 }
499 
500 static void register_clears_param(void)
501 {
502 	struct token *token;
503 	char name[256];
504 	const char *function;
505 	int param;
506 
507 	if (option_project == PROJ_NONE)
508 		return;
509 
510 	snprintf(name, 256, "%s.clears_argument", option_project_str);
511 
512 	token = get_tokens_file(name);
513 	if (!token)
514 		return;
515 	if (token_type(token) != TOKEN_STREAMBEGIN)
516 		return;
517 	token = token->next;
518 	while (token_type(token) != TOKEN_STREAMEND) {
519 		if (token_type(token) != TOKEN_IDENT)
520 			return;
521 		function = show_ident(token->ident);
522 		token = token->next;
523 		if (token_type(token) != TOKEN_NUMBER)
524 			return;
525 		param = atoi(token->number);
526 		add_function_hook(function, &match_memcpy_unknown, INT_PTR(param));
527 		token = token->next;
528 	}
529 	clear_token_alloc();
530 }
531 
532 static void db_param_cleared(struct expression *expr, int param, char *key, char *value)
533 {
534 	struct expression *arg;
535 
536 	while (expr->type == EXPR_ASSIGNMENT)
537 		expr = strip_expr(expr->right);
538 	if (expr->type != EXPR_CALL)
539 		return;
540 
541 	/*
542 	 * FIXME:  __struct_members_copy() requires an expression but
543 	 * get_variable_from_key() returns a name/sym pair so that doesn't
544 	 * work here.
545 	 */
546 	if (strcmp(key, "$") != 0)
547 		return;
548 
549 	arg = get_argument_from_call_expr(expr->args, param);
550 	if (!arg)
551 		return;
552 
553 	if (strcmp(value, "0") == 0)
554 		__struct_members_copy(COPY_MEMSET, expr, remove_addr(arg), zero_expr());
555 	else
556 		__struct_members_copy(COPY_MEMCPY, expr, remove_addr(arg), NULL);
557 }
558 
559 void register_struct_assignment(int id)
560 {
561 	add_function_hook("memset", &match_memset, NULL);
562 	add_function_hook("__memset", &match_memset, NULL);
563 
564 	add_function_hook("memcpy", &match_memcpy, INT_PTR(0));
565 	add_function_hook("memmove", &match_memcpy, INT_PTR(0));
566 	add_function_hook("__memcpy", &match_memcpy, INT_PTR(0));
567 	add_function_hook("__memmove", &match_memcpy, INT_PTR(0));
568 
569 	if (option_project == PROJ_KERNEL)
570 		return_implies_state_sval("kmemdup", valid_ptr_min_sval, valid_ptr_max_sval, &match_memdup, NULL);
571 
572 	add_function_hook("sscanf", &match_sscanf, NULL);
573 
574 	add_hook(&unop_expr, OP_HOOK);
575 	register_clears_param();
576 	select_return_states_hook(PARAM_CLEARED, &db_param_cleared);
577 
578 	select_return_states_hook(CONTAINER, &returns_container_of);
579 }
580