bogus_address_param: warn about passing a bogus address
[smatch.git] / smatch_param_key.c
blob0329ae041c695a190bd8bf43b0584ecac4716f4c
1 /*
2 * Copyright (C) 2020 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 "ctype.h"
19 #include "smatch.h"
20 #include "smatch_extra.h"
21 #include "smatch_slist.h"
23 static int my_id;
25 static void undef(struct sm_state *sm, struct expression *mod_expr)
27 set_state(my_id, sm->name, sm->sym, &undefined);
30 char *swap_names(const char *orig, const char *remove, const char *add)
32 char buf[64];
33 int offset, len, ret;
34 bool is_addr = false;
35 bool is_star = false; /* fixme: this should be star_cnt */
36 bool is_end = false;
38 if (orig[0] == '*')
39 is_star = true;
41 if (add[0] == '&') {
42 if (is_star)
43 is_star = false;
44 else
45 is_addr = true;
46 add++;
49 offset = 0;
50 while(orig[offset] == '*' || orig[offset] == '&' || orig[offset] == '(')
51 offset++;
53 len = strlen(remove);
54 if (len + offset > strlen(orig))
55 return NULL;
56 if (orig[offset + len] == '\0')
57 is_end = true;
58 else if (orig[offset + len] != '-')
59 return NULL;
60 if (strncmp(orig + offset, remove, len) != 0)
61 return NULL;
63 if (!is_star && is_end)
64 return NULL;
66 ret = snprintf(buf, sizeof(buf), "%.*s%s%s%s", offset, orig,
67 add,
68 is_end ? "" : (is_addr ? "." : "->"),
69 is_end ? "" : orig + offset + 2 + len);
70 if (ret >= sizeof(buf))
71 return NULL;
72 return alloc_string(buf);
75 static char *swap_with_param(const char *name, struct symbol *sym, struct symbol **sym_p)
77 struct smatch_state *state;
78 struct var_sym *var_sym;
79 char *ret;
82 * Say you know that "foo = bar;" and you have a state "foo->baz" then
83 * we can just substitute "bar" for "foo" giving "bar->baz".
86 if (!sym || !sym->ident)
87 return NULL;
89 state = get_state(my_id, sym->ident->name, sym);
90 if (!state || !state->data)
91 return NULL;
92 var_sym = state->data;
94 ret = swap_names(name, sym->ident->name, var_sym->var);
95 if (!ret)
96 return NULL;
98 *sym_p = var_sym->sym;
99 return ret;
102 struct expression *map_container_of_to_simpler_expr_key(struct expression *expr, const char *orig_key, char **new_key)
104 struct expression *container;
105 int offset = -1;
106 char *p = (char *)orig_key;
107 char buf[64];
108 char *start;
109 int param;
110 int ret;
111 bool arrow = false;
112 bool no_member = false;
114 expr = strip_expr(expr);
115 if (expr->type != EXPR_DEREF &&
116 (expr->type != EXPR_PREOP && expr->op == '&'))
117 return NULL;
119 while (*p != '\0') {
120 if (*p == '(' && isdigit(*(p + 1))) {
121 start = p;
122 offset = strtoul(p + 1, &p, 10);
123 if (!p || strncmp(p, "<~$", 3) != 0)
124 return NULL;
125 p += 3;
126 if (!isdigit(p[0]))
127 return NULL;
128 param = strtoul(p + 1, &p, 10);
129 /* fixme */
130 if (param != 0)
131 return NULL;
132 if (!p)
133 return NULL;
134 if (strcmp(p, ")") == 0) {
135 no_member = true;
136 p++;
137 break;
139 if (strncmp(p, ")->", 3) != 0)
140 return NULL;
141 p += 3;
142 break;
144 p++;
146 if (!no_member && *p == '\0')
147 return NULL;
149 if (offset == get_member_offset_from_deref(expr)) {
150 if (expr->type == EXPR_PREOP && expr->op == '&') {
151 expr = strip_expr(expr->unop);
152 if (expr->type != EXPR_DEREF)
153 return NULL;
154 expr = strip_expr(expr->deref);
155 if (expr->type != EXPR_PREOP || expr->op != '*')
156 return NULL;
157 container = expr->unop;
158 arrow = true;
160 container = expr->deref;
161 } else {
162 container = get_stored_container(expr, offset);
163 if (!container)
164 return NULL;
165 arrow = true;
168 if (no_member) {
169 *new_key = alloc_sname("$");
170 return container;
173 ret = snprintf(buf, sizeof(buf), "%.*s$%s%s", (int)(start - orig_key), orig_key, arrow ? "->" : ".", p);
174 if (ret >= sizeof(buf))
175 return NULL;
176 *new_key = alloc_sname(buf);
178 return container;
181 char *get_variable_from_key(struct expression *arg, const char *key, struct symbol **sym)
183 struct symbol *type;
184 char buf[256];
185 char *tmp;
186 bool address = false;
187 int star_cnt = 0;
188 bool add_dot = false;
189 int ret;
191 // FIXME: this function has been marked for being made static
192 // Use get_name_sym_from_param_key().
194 if (sym)
195 *sym = NULL;
197 if (!arg)
198 return NULL;
200 arg = strip_expr(arg);
202 if (strcmp(key, "$") == 0)
203 return expr_to_var_sym(arg, sym);
205 if (strcmp(key, "*$") == 0) {
206 if (arg->type == EXPR_PREOP && arg->op == '&') {
207 arg = strip_expr(arg->unop);
208 return expr_to_var_sym(arg, sym);
209 } else {
210 tmp = expr_to_var_sym(arg, sym);
211 if (!tmp)
212 return NULL;
213 ret = snprintf(buf, sizeof(buf), "*%s", tmp);
214 free_string(tmp);
215 if (ret >= sizeof(buf))
216 return NULL;
217 return alloc_string(buf);
221 if (strncmp(key, "(*$)", 4) == 0) {
222 if (arg->type == EXPR_PREOP && arg->op == '&') {
223 arg = strip_expr(arg->unop);
224 snprintf(buf, sizeof(buf), "$%s", key + 4);
225 return get_variable_from_key(arg, buf, sym);
226 } else {
227 tmp = expr_to_var_sym(arg, sym);
228 if (!tmp)
229 return NULL;
230 ret = snprintf(buf, sizeof(buf), "(*%s)%s", tmp, key + 4);
231 free_string(tmp);
232 if (ret >= sizeof(buf))
233 return NULL;
234 return alloc_string(buf);
238 if (strstr(key, "<~$")) {
239 struct expression *expr;
240 char *new_key = NULL;
242 expr = map_container_of_to_simpler_expr_key(arg, key, &new_key);
243 if (!expr)
244 return NULL;
245 if (arg != expr) {
246 arg = expr;
247 *sym = expr_to_sym(expr);
249 key = new_key;
252 while (key[0] == '*') {
253 star_cnt++;
254 key++;
257 if (key[0] == '&') {
258 address = true;
259 key++;
263 * FIXME: This is a hack.
264 * We should be able to parse expressions like (*$)->foo and *$->foo.
266 type = get_type(arg);
267 if (is_struct_ptr(type))
268 add_dot = true;
270 if (arg->type == EXPR_PREOP && arg->op == '&' && star_cnt && !add_dot) {
271 arg = strip_expr(arg->unop);
272 star_cnt--;
275 if (arg->type == EXPR_PREOP && arg->op == '&') {
276 arg = strip_expr(arg->unop);
277 tmp = expr_to_var_sym(arg, sym);
278 if (!tmp)
279 return NULL;
280 ret = snprintf(buf, sizeof(buf), "%s%.*s%s.%s",
281 address ? "&" : "", star_cnt, "**********",
282 tmp, key + 3);
283 if (ret >= sizeof(buf))
284 return NULL;
285 return alloc_string(buf);
288 tmp = expr_to_var_sym(arg, sym);
289 if (!tmp)
290 return NULL;
291 ret = snprintf(buf, sizeof(buf), "%s%.*s%s%s",
292 address ? "&" : "", star_cnt, "**********", tmp, key + 1);
293 free_string(tmp);
294 if (ret >= sizeof(buf))
295 return NULL;
296 return alloc_string(buf);
299 bool split_param_key(const char *value, int *param, char *key, int len)
301 char *p;
302 int l, skip = 1;
304 l = snprintf(key, len, "%s", value);
305 if (l >= len)
306 return false;
308 p = key;
309 while (*p && *p != '$')
310 p++;
311 if (*p != '$')
312 return false;
313 p++;
315 *param = atoi(p);
316 if (*param < 0 || *param > 99)
317 return false;
319 p++;
320 if (*param > 9) {
321 skip = 2;
322 p++;
325 memmove(p - skip, p, l - (p - key) + 1);
327 return true;
330 bool get_implied_rl_from_call_str(struct expression *expr, const char *data, struct range_list **rl)
332 struct smatch_state *state;
333 struct expression *arg;
334 struct symbol *sym;
335 char buf[256];
336 char *name;
337 int param;
339 while (expr->type == EXPR_ASSIGNMENT)
340 expr = expr->right;
341 if (expr->type != EXPR_CALL)
342 return false;
344 if (!split_param_key(data, &param, buf, sizeof(buf)))
345 return false;
347 if (strcmp(buf, "$") == 0) {
348 arg = get_argument_from_call_expr(expr->args, param);
349 if (!arg)
350 return false;
351 return get_implied_rl(arg, rl);
354 name = get_name_sym_from_param_key(expr, param, buf, &sym);
355 if (!name)
356 return false;
358 state = get_state(SMATCH_EXTRA, name, sym);
359 if (!estate_rl(state))
360 return false;
361 *rl = estate_rl(state);
362 return true;
365 char *get_chunk_from_key(struct expression *arg, char *key, struct symbol **sym, struct var_sym_list **vsl)
367 *vsl = NULL;
369 if (strcmp("$", key) == 0)
370 return expr_to_chunk_sym_vsl(arg, sym, vsl);
371 return get_variable_from_key(arg, key, sym);
374 static char *state_name_to_param_name(const char *state_name, const char *param_name)
376 bool address = false;
377 int star_cnt = 0;
378 int name_len;
379 char buf[256];
380 int ret;
383 * Normally what happens is that we map "*foo->bar" to "*param->bar"
384 * but with container_of() there is no notation for that in C and it's
385 * just a Smatch invention. So in that case, the state name is the
386 * param name.
388 if (strstr(state_name, "<~$"))
389 return (char *)state_name;
391 name_len = strlen(param_name);
393 while (state_name[0] == '*') {
394 star_cnt++;
395 state_name++;
398 if (state_name[0] == '&') {
399 address = true;
400 state_name++;
403 /* ten out of ten stars! */
404 if (star_cnt > 10)
405 return NULL;
407 if (strncmp(state_name, "(*", 2) == 0 &&
408 strncmp(state_name + 2, param_name, name_len) == 0 &&
409 state_name[name_len + 2] == ')') {
410 ret = snprintf(buf, sizeof(buf), "%s%.*s(*$)%s",
411 address ? "&" : "",
412 star_cnt, "**********",
413 state_name + name_len + 3);
414 if (ret >= sizeof(buf))
415 return NULL;
416 return alloc_sname(buf);
419 if (strcmp(state_name, param_name) == 0) {
420 snprintf(buf, sizeof(buf), "%s%.*s$",
421 address ? "&" : "",
422 star_cnt, "**********");
423 return alloc_sname(buf);
426 /* check for '-' from "->" */
427 if (strncmp(state_name, param_name, name_len) == 0 &&
428 state_name[name_len] == '-') {
429 ret = snprintf(buf, sizeof(buf), "%s%.*s$%s",
430 address ? "&" : "",
431 star_cnt, "**********",
432 state_name + name_len);
433 if (ret >= sizeof(buf))
434 return NULL;
435 return alloc_sname(buf);
437 return NULL;
440 char *get_param_name_var_sym(const char *name, struct symbol *sym)
442 if (!sym || !sym->ident)
443 return NULL;
445 return state_name_to_param_name(name, sym->ident->name);
448 const char *get_mtag_name_var_sym(const char *state_name, struct symbol *sym)
450 struct symbol *type;
451 const char *sym_name;
452 int name_len;
453 static char buf[256];
456 * mtag_name is different from param_name because mtags can be a struct
457 * instead of a struct pointer. But we want to treat it like a pointer
458 * because really an mtag is a pointer. Or in other words, if you pass
459 * a struct foo then you want to talk about foo.bar but with an mtag
460 * you want to refer to it as foo->bar.
464 if (!sym || !sym->ident)
465 return NULL;
467 type = get_real_base_type(sym);
468 if (type && type->type == SYM_BASETYPE)
469 return "*$";
471 sym_name = sym->ident->name;
472 name_len = strlen(sym_name);
474 if (state_name[name_len] == '.' && /* check for '-' from "->" */
475 strncmp(state_name, sym_name, name_len) == 0) {
476 snprintf(buf, sizeof(buf), "$->%s", state_name + name_len + 1);
477 return buf;
480 return state_name_to_param_name(state_name, sym_name);
483 const char *get_mtag_name_expr(struct expression *expr)
485 char *name;
486 struct symbol *sym;
487 const char *ret = NULL;
489 name = expr_to_var_sym(expr, &sym);
490 if (!name || !sym)
491 goto free;
493 ret = get_mtag_name_var_sym(name, sym);
494 free:
495 free_string(name);
496 return ret;
499 char *get_param_name(struct sm_state *sm)
501 return get_param_name_var_sym(sm->name, sm->sym);
504 char *get_param_var_sym_var_sym(const char *name, struct symbol *sym, struct expression *ret_expr, struct symbol **sym_p)
506 struct smatch_state *state;
507 struct var_sym *var_sym;
508 int param;
510 *sym_p = NULL;
512 // FIXME was modified...
514 param = get_param_num_from_sym(sym);
515 if (param >= 0) {
516 *sym_p = sym;
517 return alloc_string(name);
520 state = get_state(my_id, name, sym);
521 if (state && state->data) {
522 var_sym = state->data;
523 if (!var_sym)
524 return NULL;
526 *sym_p = var_sym->sym;
527 return alloc_string(var_sym->var);
530 /* One would think that handling container_of() should be done here
531 * but it it's quite tricky because we only have a name and a sym
532 * and none of the assignments have been handled yet, either here or
533 * in smatch_assignments.c. On the other hand handling container_of()
534 * in the assignment hook has the advantage that it saves resources and
535 * it should work fine because of the fake assignments which we do.
538 return swap_with_param(name, sym, sym_p);
541 char *get_param_name_sym(struct expression *expr, struct symbol **sym_p)
543 struct symbol *sym;
544 const char *ret = NULL;
545 char *name;
547 name = expr_to_var_sym(expr, &sym);
548 if (!name || !sym)
549 goto free;
551 ret = get_param_var_sym_var_sym(name, sym, NULL, sym_p);
552 free:
553 free_string(name);
554 return alloc_string(ret);
557 int get_return_param_key_from_var_sym(const char *name, struct symbol *sym,
558 struct expression *ret_expr,
559 const char **key)
561 const char *param_name;
562 struct symbol *ret_sym;
563 char *ret_str;
565 if (!ret_expr)
566 return -2;
568 ret_str = expr_to_str_sym(ret_expr, &ret_sym);
569 if (ret_str && ret_sym == sym) {
570 param_name = state_name_to_param_name(name, ret_str);
571 if (param_name) {
572 free_string(ret_str);
573 if (key)
574 *key = param_name;
575 return -1;
578 free_string(ret_str);
580 return -2;
583 int get_param_key_from_var_sym(const char *name, struct symbol *sym,
584 struct expression *ret_expr,
585 const char **key)
587 const char *param_name;
588 char *other_name;
589 struct symbol *other_sym;
590 int param;
592 if (key)
593 *key = name;
595 /* straight forward param match */
596 param = get_param_num_from_sym(sym);
597 if (param >= 0) {
598 param_name = get_param_name_var_sym(name, sym);
599 if (param_name) {
600 if (key)
601 *key = param_name;
602 return param;
606 param = get_return_param_key_from_var_sym(name, sym, ret_expr, key);
607 if (param == -1)
608 return param;
610 other_name = get_param_var_sym_var_sym(name, sym, ret_expr, &other_sym);
611 if (!other_name || !other_sym)
612 return -2;
613 param = get_param_num_from_sym(other_sym);
614 if (param < 0) {
615 sm_msg("internal: '%s' parameter not found", other_name);
616 return -2;
619 param_name = get_param_name_var_sym(other_name, other_sym);
620 if (param_name) {
621 if (key)
622 *key = param_name;
623 return param;
625 return -2;
628 int get_param_key_from_sm(struct sm_state *sm, struct expression *ret_expr,
629 const char **key)
631 return get_param_key_from_var_sym(sm->name, sm->sym, ret_expr, key);
634 int get_param_key_from_expr(struct expression *expr, struct expression *ret_expr,
635 const char **key)
637 char *name;
638 struct symbol *sym;
639 int ret = -2;
641 *key = NULL;
642 name = expr_to_var_sym(expr, &sym);
643 if (!name || !sym)
644 goto free;
646 ret = get_param_key_from_var_sym(name, sym, ret_expr, key);
647 free:
648 free_string(name);
649 return ret;
652 const char *get_param_key_swap_dollar(struct expression *expr)
654 struct sm_state *sm;
655 const char *key, *p;
656 char buf[64];
657 int param;
659 sm = get_sm_state_expr(my_id, expr);
660 if (!sm || slist_has_state(sm->possible, &undefined))
661 return NULL;
663 param = get_param_key_from_expr(expr, NULL, &key);
664 if (param < 0)
665 return NULL;
667 p = strchr(key, '$');
668 if (!p)
669 return NULL;
671 snprintf(buf, sizeof(buf), "%.*s%d%s", (int)(p - key + 1), key, param, p + 1);
672 return alloc_sname(buf);
675 int map_to_param(const char *name, struct symbol *sym)
677 return get_param_key_from_var_sym(name, sym, NULL, NULL);
680 int get_param_num_from_sym(struct symbol *sym)
682 struct symbol *tmp;
683 int i;
685 if (!sym)
686 return UNKNOWN_SCOPE;
688 if (sym->ctype.modifiers & MOD_TOPLEVEL) {
689 if (sym->ctype.modifiers & MOD_STATIC)
690 return FILE_SCOPE;
691 return GLOBAL_SCOPE;
694 if (!cur_func_sym) {
695 if (!parse_error) {
696 sm_msg("warn: internal. problem with scope: %s",
697 sym->ident ? sym->ident->name : "<anon var>");
699 return GLOBAL_SCOPE;
703 i = 0;
704 FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, tmp) {
705 if (tmp == sym)
706 return i;
707 i++;
708 } END_FOR_EACH_PTR(tmp);
709 return LOCAL_SCOPE;
712 int get_param_num(struct expression *expr)
714 struct symbol *sym;
715 char *name;
717 if (!cur_func_sym)
718 return UNKNOWN_SCOPE;
719 name = expr_to_var_sym(expr, &sym);
720 free_string(name);
721 if (!sym)
722 return UNKNOWN_SCOPE;
723 return get_param_num_from_sym(sym);
726 struct symbol *get_param_sym_from_num(int num)
728 struct symbol *sym;
729 int i;
731 if (!cur_func_sym)
732 return NULL;
734 i = 0;
735 FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, sym) {
736 if (i++ == num)
737 return sym;
738 } END_FOR_EACH_PTR(sym);
739 return NULL;
742 char *get_name_sym_from_param_key(struct expression *expr, int param, const char *key, struct symbol **sym)
744 struct expression *call, *arg;
745 char *name;
747 if (sym)
748 *sym = NULL;
750 if (!expr) {
751 sm_msg("internal: null call_expr. param=%d key='%s'", param, key);
752 return NULL;
755 call = expr;
756 while (call->type == EXPR_ASSIGNMENT)
757 call = strip_expr(call->right);
759 if (call->type != EXPR_CALL)
760 return NULL;
762 if (param == -1 &&
763 expr->type == EXPR_ASSIGNMENT &&
764 expr->op == '=') {
765 name = get_variable_from_key(expr->left, key, sym);
766 if (!name || (sym && !*sym))
767 goto free;
768 } else if (param >= 0) {
769 arg = get_argument_from_call_expr(call->args, param);
770 if (!arg)
771 return NULL;
773 name = get_variable_from_key(arg, key, sym);
774 if (!name || (sym && !*sym))
775 goto free;
776 } else {
777 name = alloc_string(key);
780 return name;
781 free:
782 free_string(name);
783 return NULL;
786 static char *handle_container_of_assign(struct expression *expr, struct symbol **sym)
788 struct expression *right, *orig;
789 struct symbol *type;
790 sval_t sval;
791 int param;
792 char buf[64];
794 type = get_type(expr->left);
795 if (!type || type->type != SYM_PTR)
796 return NULL;
798 right = strip_expr(expr->right);
799 if (right->type != EXPR_BINOP || right->op != '-')
800 return NULL;
802 if (!get_value(right->right, &sval) ||
803 sval.value < 0 || sval.value > MTAG_OFFSET_MASK)
804 return NULL;
806 orig = get_assigned_expr(right->left);
807 if (!orig)
808 return NULL;
809 if (orig->type != EXPR_SYMBOL)
810 return NULL;
811 param = get_param_num_from_sym(orig->symbol);
812 if (param < 0)
813 return NULL;
815 snprintf(buf, sizeof(buf), "(%lld<~$%d)", sval.value, param);
816 *sym = orig->symbol;
817 return alloc_string(buf);
820 const char *get_container_of_str(struct expression *expr)
822 struct smatch_state *state;
824 state = get_state_expr(my_id, expr);
825 if (!state)
826 return NULL;
827 if (!strstr(state->name, "<~$"))
828 return NULL;
829 return state->name;
832 static void match_assign(struct expression *expr)
834 struct symbol *param_sym;
835 char *param_name;
837 if (expr->op != '=')
838 return;
840 /* __in_fake_parameter_assign is included deliberately */
841 if (is_fake_call(expr->right) ||
842 __in_fake_struct_assign)
843 return;
845 param_name = get_param_name_sym(expr->right, &param_sym);
846 if (param_name && param_sym)
847 goto set_state;
849 param_name = handle_container_of_assign(expr, &param_sym);
850 if (param_name && param_sym)
851 goto set_state;
853 goto free;
855 set_state:
856 set_state_expr(my_id, expr->left, alloc_var_sym_state(param_name, param_sym));
857 free:
858 free_string(param_name);
861 bool get_offset_param(const char *ret_str, int *offset, int *param)
863 const char *p;
865 if (!ret_str)
866 return false;
867 p = strstr(ret_str, "[(");
868 if (!p)
869 return false;
870 p += 2;
871 *offset = atoi(p);
872 p = strstr(p, "<~$");
873 if (!p)
874 return false;
875 p += 3;
876 if (!isdigit(p[0]))
877 return false;
878 *param = atoi(p);
879 return true;;
882 static void return_str_hook(struct expression *expr, const char *ret_str)
884 struct expression *call, *arg;
885 struct symbol *sym;
886 int offset, param;
887 char buf[32];
889 if (!expr || expr->type != EXPR_ASSIGNMENT)
890 return;
891 call = expr;
892 while (call && call->type == EXPR_ASSIGNMENT)
893 call = strip_expr(call->right);
894 if (!call || call->type != EXPR_CALL)
895 return;
897 if (!get_offset_param(ret_str, &offset, &param))
898 return;
900 arg = get_argument_from_call_expr(call->args, param);
901 arg = strip_expr(arg);
902 if (!arg)
903 return;
905 /* fixme this could be better */
906 if (arg->type != EXPR_SYMBOL)
907 return;
908 sym = arg->symbol;
910 param = get_param_num(arg);
911 if (param < 0)
912 return;
914 snprintf(buf, sizeof(buf), "(%d<~$%d)", offset, param);
915 set_state_expr(my_id, expr->left, alloc_var_sym_state(buf, sym));
918 void register_param_key(int id)
920 my_id = id;
922 set_dynamic_states(my_id);
923 add_hook(&match_assign, ASSIGNMENT_HOOK_AFTER);
924 add_return_string_hook(return_str_hook);
925 add_modification_hook(my_id, &undef);