implied: remove some dead code
[smatch.git] / smatch_units.c
blobb3cfdf3a0c7c2042208b84e33f6c733239f551c8
1 /*
2 * Copyright (C) 2018 Oracle.
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.
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.
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
18 #include "smatch.h"
19 #include "smatch_slist.h"
21 static int my_id;
23 GLOBAL_STATE(unit_bit);
24 GLOBAL_STATE(unit_byte);
25 GLOBAL_STATE(unit_array_size);
26 GLOBAL_STATE(unit_long);
27 GLOBAL_STATE(unit_page);
28 GLOBAL_STATE(unit_msec);
29 GLOBAL_STATE(unit_ns);
30 GLOBAL_STATE(unit_jiffy);
31 GLOBAL_STATE(unit_pixel);
33 struct type_info {
34 const char *name;
35 int type;
36 int param;
37 const char *key;
38 const char *value;
41 static struct type_info func_table[] = {
42 { "unit_msecs_to_jiffies_timeout", UNITS, -1, "$", "unit_jiffy" },
43 { "round_jiffies_up_relative", UNITS, -1, "$", "unit_jiffy" },
46 struct name_unit {
47 const char *name;
48 struct smatch_state *unit;
51 static struct name_unit member_table[] = {
52 { "(struct vm_area_struct)->vm_pgoff", &unit_page },
53 { "(struct console_font)->width", &unit_pixel },
54 { "(struct console_font)->height", &unit_pixel },
57 static struct smatch_state *str_to_units(const char *str)
59 if (!str)
60 return NULL;
62 if (strcmp(str, "unit_bit") == 0)
63 return &unit_bit;
64 if (strcmp(str, "unit_byte") == 0)
65 return &unit_byte;
66 if (strcmp(str, "unit_page") == 0)
67 return &unit_page;
68 if (strcmp(str, "unit_msec") == 0)
69 return &unit_msec;
70 if (strcmp(str, "unit_jiffy") == 0)
71 return &unit_jiffy;
72 if (strcmp(str, "unit_long") == 0)
73 return &unit_long;
74 if (strcmp(str, "unit_array_size") == 0)
75 return &unit_array_size;
76 if (strcmp(str, "unknown") == 0)
77 return NULL;
79 return NULL;
82 static struct smatch_state *get_units_from_type(struct expression *expr);
84 static struct smatch_state *merge_units(struct smatch_state *s1, struct smatch_state *s2)
86 if (s1 == &undefined)
87 return s2;
88 if (s2 == &undefined)
89 return s1;
90 return &merged;
93 static bool is_ignored_type(char *name)
95 if (!name)
96 return false;
97 if (strcmp(name, "(union anonymous)->__val") == 0)
98 return true;
99 if (strncmp(name, "(struct fs_parse_result)", 24) == 0)
100 return true;
101 if (strncmp(name, "(struct pt_regs)", 16) == 0)
102 return true;
103 return false;
106 static bool ARRAY_SIZE_is_bytes(struct expression *expr)
108 sval_t sval;
110 expr = strip_expr(expr);
111 if (!expr)
112 return false;
115 * The kernel ARRAY_SIZE() macro is sizeof(array)/sizeof(array[0]) + 0
116 * where 0 does some fancy type checking.
119 if (expr->type == EXPR_BINOP &&
120 expr->op == '+' &&
121 expr_is_zero(expr->right))
122 return ARRAY_SIZE_is_bytes(expr->left);
124 if (expr->type != EXPR_BINOP || expr->op != '/')
125 return false;
126 if (!get_value(expr->right, &sval) || sval.value != 1)
127 return false;
128 return true;
131 static void store_type_in_db(struct expression *expr, struct smatch_state *state)
133 char *member;
135 member = get_member_name(expr);
136 if (!member)
137 return;
138 if (is_ignored_type(member))
139 return;
141 sql_insert_cache(type_info, "0x%llx, %d, '%s', '%s'", get_base_file_id(), UNITS, member, state->name);
144 static void set_units(struct expression *expr, struct smatch_state *state)
146 if (!state)
147 return;
149 set_state_expr(my_id, expr, state);
150 store_type_in_db(expr, state);
153 static bool is_PAGE_SHIFT(struct expression *expr)
155 char *macro;
157 macro = get_macro_name(expr->pos);
158 if (macro && strcmp(macro, "PAGE_SHIFT") == 0)
159 return true;
160 return false;
163 static bool is_PAGE_SIZE(struct expression *expr)
165 char *macro;
167 macro = get_macro_name(expr->pos);
168 if (macro && strcmp(macro, "PAGE_SIZE") == 0)
169 return true;
170 return false;
173 static bool is_BITS_PER_LONG(struct expression *expr)
175 char *macro;
177 macro = get_macro_name(expr->pos);
178 if (macro && strcmp(macro, "BITS_PER_LONG") == 0)
179 return true;
180 return false;
183 static struct smatch_state *binop_helper(struct expression *left, int op, struct expression *right)
185 struct smatch_state *left_state, *right_state;
187 switch(op) {
188 case '-':
189 // subtracting pointers gives unit_byte
190 /* fall through */
191 case '+':
192 left_state = get_units(left);
193 right_state = get_units(right);
194 if (left_state == &unit_array_size ||
195 right_state == &unit_array_size)
196 return NULL;
198 return left_state ? left_state : right_state;
199 case '*':
200 /* FIXME: A multiply is almost always bytes but it can be bits. */
201 if (is_PAGE_SIZE(right))
202 return &unit_byte;
203 return NULL;
204 case '/':
205 if (is_BITS_PER_LONG(right))
206 return &unit_long;
207 if (is_PAGE_SIZE(right))
208 return &unit_page;
209 return NULL;
210 case SPECIAL_LEFTSHIFT:
211 if (is_PAGE_SHIFT(right))
212 return &unit_byte;
213 return NULL;
214 case SPECIAL_RIGHTSHIFT:
215 if (is_PAGE_SHIFT(right))
216 return &unit_page;
217 return NULL;
219 return NULL;
222 static struct smatch_state *get_units_binop(struct expression *expr)
224 return binop_helper(expr->left, expr->op, expr->right);
227 static struct smatch_state *get_units_call(struct expression *expr)
229 expr = strip_expr(expr);
230 if (!expr || expr->type != EXPR_CALL)
231 return NULL;
233 if (sym_name_is("unit_msecs_to_jiffies", expr->fn))
234 return &unit_jiffy;
235 if (sym_name_is("jiffies_to_unit_msecs", expr->fn))
236 return &unit_msec;
238 return NULL;
241 static int db_units(void *_units, int argc, char **argv, char **azColName)
243 char **units = _units;
245 if (*units) {
246 if (strcmp(*units, argv[0]) == 0)
247 return 0;
248 free_string(*units);
249 *units = alloc_string("unknown");
250 return 0;
253 *units = alloc_string(argv[0]);
254 return 0;
257 static struct smatch_state *get_units_from_type(struct expression *expr)
259 char *member;
260 char *units = NULL;
261 struct smatch_state *ret = NULL;
262 int i;
264 member = get_member_name(expr);
265 if (!member)
266 return NULL;
268 for (i = 0; i < ARRAY_SIZE(member_table); i++) {
269 if (strcmp(member, member_table[i].name) == 0)
270 return member_table[i].unit;
273 cache_sql(&db_units, &units, "select value from type_info where type = %d and key = '%s';",
274 UNITS, member);
275 run_sql(&db_units, &units, "select value from type_info where type = %d and key = '%s';",
276 UNITS, member);
277 if (!units)
278 return NULL;
280 ret = str_to_units(units);
282 free_string(units);
284 return ret;
287 struct smatch_state *get_units(struct expression *expr)
289 struct smatch_state *state;
290 char *ident;
292 expr = strip_expr(expr);
293 if (!expr)
294 return NULL;
296 if (expr_is_zero(expr))
297 return NULL;
299 if (expr->type == EXPR_PTRSIZEOF ||
300 expr->type == EXPR_SIZEOF)
301 return &unit_byte;
303 ident = pos_ident(expr->pos);
304 if (ident) {
305 if (strcmp(ident, "sizeof") == 0 ||
306 strcmp(ident, "PAGE_SIZE") == 0)
307 return &unit_byte;
308 if (strcmp(ident, "jiffies") == 0)
309 return &unit_jiffy;
310 if (strcmp(ident, "BITS_PER_LONG") == 0)
311 return &unit_bit;
312 if (strcmp(ident, "BITS_PER_LONG_LONG") == 0)
313 return &unit_bit;
314 if (strcmp(ident, "ARRAY_SIZE") == 0) {
315 if (ARRAY_SIZE_is_bytes(expr))
316 return &unit_byte;
317 return &unit_array_size;
321 if (expr->type == EXPR_BINOP)
322 return get_units_binop(expr);
324 if (expr->type == EXPR_CALL)
325 return get_units_call(expr);
327 state = get_state_expr(my_id, expr);
328 if (state == &merged || state == &undefined)
329 return NULL;
330 if (state)
331 return state;
333 return get_units_from_type(expr);
336 bool is_array_size_units(struct expression *expr)
338 return get_units(expr) == &unit_array_size;
341 static void match_allocation(struct expression *expr,
342 const char *name, struct symbol *sym,
343 struct allocation_info *info)
345 struct expression *right, *left;
347 if (info->nr_elems && info->elem_size) {
348 left = info->nr_elems;
349 right = info->elem_size;
350 } else if (info->total_size &&
351 info->total_size->type == EXPR_BINOP &&
352 info->total_size->op == '*') {
353 left = strip_expr(info->total_size->left);
354 right = strip_expr(info->total_size->right);
355 } else {
356 return;
359 if (get_units(left) == &unit_byte)
360 set_units(right, &unit_array_size);
361 if (get_units(right) == &unit_byte)
362 set_units(left, &unit_array_size);
365 static void match_binop_set(struct expression *expr)
367 struct smatch_state *left, *right;
368 struct symbol *type;
370 if (expr->op == SPECIAL_LEFTSHIFT && is_PAGE_SHIFT(expr->right)) {
371 set_units(expr->left, &unit_page);
372 return;
375 if (expr->op == SPECIAL_RIGHTSHIFT && is_PAGE_SHIFT(expr->right)) {
376 set_units(expr->left, &unit_byte);
377 return;
380 if (expr->op != '+' && expr->op != '-')
381 return;
383 type = get_type(expr->left);
384 if (type && (type->type == SYM_PTR || type->type == SYM_ARRAY))
385 return;
387 left = get_units(expr->left);
388 right = get_units(expr->right);
390 if (left && !right)
391 set_units(expr->right, left);
392 if (right && !left)
393 set_units(expr->left, right);
396 static void match_condition_set(struct expression *expr)
398 struct smatch_state *left, *right;
400 if (expr->type != EXPR_COMPARE)
401 return;
403 left = get_units(expr->left);
404 right = get_units(expr->right);
406 if (left && !right)
407 set_units(expr->right, left);
408 if (right && !left)
409 set_units(expr->left, right);
412 static void match_assign(struct expression *expr)
414 struct smatch_state *state = NULL;
416 if (__in_fake_assign)
417 return;
419 switch(expr->op) {
420 case '=':
421 state = get_units(expr->right);
422 break;
423 case SPECIAL_SHR_ASSIGN:
424 case SPECIAL_SHL_ASSIGN:
425 case SPECIAL_DIV_ASSIGN:
426 case SPECIAL_MUL_ASSIGN:
427 state = binop_helper(expr->left, op_remove_assign(expr->op),
428 expr->right);
431 if (state)
432 set_units(expr->left, state);
433 // else
434 // clear_units(expr->left);
437 static void set_implied_units(struct expression *call, struct expression *arg, char *key, char *value)
439 struct smatch_state *state;
440 struct symbol *sym;
441 char *name;
443 state = str_to_units(value);
444 if (!state)
445 return;
446 name = get_variable_from_key(arg, key, &sym);
447 if (!name || !sym)
448 goto free;
449 set_state(my_id, name, sym, state);
450 free:
451 free_string(name);
454 static void set_param_units(const char *name, struct symbol *sym, char *key, char *value)
456 struct smatch_state *state;
458 state = str_to_units(value);
459 if (!state)
460 return;
461 set_state(my_id, sym->ident->name, sym, state);
464 static void set_param_units_from_table(struct expression *expr, const char *name, struct symbol *sym, void *data)
466 const char *value = data;
467 struct smatch_state *state;
469 state = str_to_units(value);
470 if (!state)
471 return;
472 set_state(my_id, name, sym, state);
475 static void match_call_info(struct expression *expr)
477 struct expression *arg;
478 struct smatch_state *state;
479 char *fn_name;
480 int param = -1;
482 if (expr->fn->type != EXPR_SYMBOL)
483 return;
484 fn_name = expr_to_var(expr->fn);
485 if (!fn_name)
486 return;
488 FOR_EACH_PTR(expr->args, arg) {
489 param++;
490 state = get_units(arg);
491 if (!state)
492 continue;
493 // sql_insert_cache(return_implies, "0x%llx, '%s', 0, %d, %d, %d, '%s', '%s'",
494 // get_base_file_id(), fn_name, is_static(expr->fn), UNITS, param, "$", state->name);
495 sql_insert_caller_info(expr, UNITS, param, "$", state->name);
497 } END_FOR_EACH_PTR(arg);
499 free_string(fn_name);
502 static void process_states(void)
504 struct symbol *arg;
505 struct smatch_state *state, *start_state;
506 int param = -1;
508 FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
509 param++;
510 state = get_state(my_id, arg->ident->name, arg);
511 if (!state || state == &merged || state == &undefined)
512 continue;
513 start_state = get_state_stree(get_start_states(), my_id, arg->ident->name, arg);
514 if (state == start_state)
515 continue;
516 sql_insert_cache(return_implies, "0x%llx, '%s', 0, %d, %d, %d, '%s', '%s'",
517 get_base_file_id(), get_function(), fn_static(), UNITS, param, "$", state->name);
518 } END_FOR_EACH_PTR(arg);
521 char *get_unit_str(struct expression *expr)
523 struct smatch_state *state;
525 state = get_units(expr);
526 if (!state)
527 return NULL;
528 return (char *)state->name;
531 void register_units(int id)
533 struct type_info *info;
534 int i;
536 my_id = id;
538 for (i = 0; i < ARRAY_SIZE(func_table); i++) {
539 info = &func_table[i];
540 add_function_param_key_hook(info->name,
541 set_param_units_from_table,
542 info->param, info->key,
543 (void *)info->value);
546 add_merge_hook(my_id, &merge_units);
548 add_hook(&match_binop_set, BINOP_HOOK);
549 add_hook(&match_condition_set, CONDITION_HOOK);
550 add_hook(&match_assign, ASSIGNMENT_HOOK);
551 add_hook(&match_call_info, FUNCTION_CALL_HOOK);
552 all_return_states_hook(&process_states);
554 select_return_implies_hook(UNITS, &set_implied_units);
555 select_caller_info_hook(&set_param_units, UNITS);
557 add_allocation_hook(&match_allocation);