param_key: remove some bogus warning messages
[smatch.git] / smatch_param_key.c
blob4e2ce6fcea1715c39d2dd1c2940b31efa4f3690a
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 return -2;
617 param_name = get_param_name_var_sym(other_name, other_sym);
618 if (param_name) {
619 if (key)
620 *key = param_name;
621 return param;
623 return -2;
626 int get_param_key_from_sm(struct sm_state *sm, struct expression *ret_expr,
627 const char **key)
629 return get_param_key_from_var_sym(sm->name, sm->sym, ret_expr, key);
632 int get_param_key_from_expr(struct expression *expr, struct expression *ret_expr,
633 const char **key)
635 char *name;
636 struct symbol *sym;
637 int ret = -2;
639 *key = NULL;
640 name = expr_to_var_sym(expr, &sym);
641 if (!name || !sym)
642 goto free;
644 ret = get_param_key_from_var_sym(name, sym, ret_expr, key);
645 free:
646 free_string(name);
647 return ret;
650 const char *get_param_key_swap_dollar(struct expression *expr)
652 struct sm_state *sm;
653 const char *key, *p;
654 char buf[64];
655 int param;
657 sm = get_sm_state_expr(my_id, expr);
658 if (!sm || slist_has_state(sm->possible, &undefined))
659 return NULL;
661 param = get_param_key_from_expr(expr, NULL, &key);
662 if (param < 0)
663 return NULL;
665 p = strchr(key, '$');
666 if (!p)
667 return NULL;
669 snprintf(buf, sizeof(buf), "%.*s%d%s", (int)(p - key + 1), key, param, p + 1);
670 return alloc_sname(buf);
673 int map_to_param(const char *name, struct symbol *sym)
675 return get_param_key_from_var_sym(name, sym, NULL, NULL);
678 int get_param_num_from_sym(struct symbol *sym)
680 struct symbol *tmp;
681 int i;
683 if (!sym)
684 return UNKNOWN_SCOPE;
686 if (sym->ctype.modifiers & MOD_TOPLEVEL) {
687 if (sym->ctype.modifiers & MOD_STATIC)
688 return FILE_SCOPE;
689 return GLOBAL_SCOPE;
692 if (!cur_func_sym)
693 return GLOBAL_SCOPE;
695 i = 0;
696 FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, tmp) {
697 if (tmp == sym)
698 return i;
699 i++;
700 } END_FOR_EACH_PTR(tmp);
701 return LOCAL_SCOPE;
704 int get_param_num(struct expression *expr)
706 struct symbol *sym;
707 char *name;
709 if (!cur_func_sym)
710 return UNKNOWN_SCOPE;
711 name = expr_to_var_sym(expr, &sym);
712 free_string(name);
713 if (!sym)
714 return UNKNOWN_SCOPE;
715 return get_param_num_from_sym(sym);
718 struct symbol *get_param_sym_from_num(int num)
720 struct symbol *sym;
721 int i;
723 if (!cur_func_sym)
724 return NULL;
726 i = 0;
727 FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, sym) {
728 if (i++ == num)
729 return sym;
730 } END_FOR_EACH_PTR(sym);
731 return NULL;
734 char *get_name_sym_from_param_key(struct expression *expr, int param, const char *key, struct symbol **sym)
736 struct expression *call, *arg;
737 char *name;
739 if (sym)
740 *sym = NULL;
742 if (!expr) {
743 sm_msg("internal: null call_expr. param=%d key='%s'", param, key);
744 return NULL;
747 call = expr;
748 while (call->type == EXPR_ASSIGNMENT)
749 call = strip_expr(call->right);
751 if (call->type != EXPR_CALL)
752 return NULL;
754 if (param == -1 &&
755 expr->type == EXPR_ASSIGNMENT &&
756 expr->op == '=') {
757 name = get_variable_from_key(expr->left, key, sym);
758 if (!name || (sym && !*sym))
759 goto free;
760 } else if (param >= 0) {
761 arg = get_argument_from_call_expr(call->args, param);
762 if (!arg)
763 return NULL;
765 name = get_variable_from_key(arg, key, sym);
766 if (!name || (sym && !*sym))
767 goto free;
768 } else {
769 name = alloc_string(key);
772 return name;
773 free:
774 free_string(name);
775 return NULL;
778 static char *handle_container_of_assign(struct expression *expr, struct symbol **sym)
780 struct expression *right, *orig;
781 struct symbol *type;
782 sval_t sval;
783 int param;
784 char buf[64];
786 type = get_type(expr->left);
787 if (!type || type->type != SYM_PTR)
788 return NULL;
790 right = strip_expr(expr->right);
791 if (right->type != EXPR_BINOP || right->op != '-')
792 return NULL;
794 if (!get_value(right->right, &sval) ||
795 sval.value < 0 || sval.value > MTAG_OFFSET_MASK)
796 return NULL;
798 orig = get_assigned_expr(right->left);
799 if (!orig)
800 return NULL;
801 if (orig->type != EXPR_SYMBOL)
802 return NULL;
803 param = get_param_num_from_sym(orig->symbol);
804 if (param < 0)
805 return NULL;
807 snprintf(buf, sizeof(buf), "(%lld<~$%d)", sval.value, param);
808 *sym = orig->symbol;
809 return alloc_string(buf);
812 const char *get_container_of_str(struct expression *expr)
814 struct smatch_state *state;
816 state = get_state_expr(my_id, expr);
817 if (!state)
818 return NULL;
819 if (!strstr(state->name, "<~$"))
820 return NULL;
821 return state->name;
824 static void match_assign(struct expression *expr)
826 struct symbol *param_sym;
827 char *param_name;
829 if (expr->op != '=')
830 return;
832 /* __in_fake_parameter_assign is included deliberately */
833 if (is_fake_call(expr->right) ||
834 __in_fake_struct_assign)
835 return;
837 param_name = get_param_name_sym(expr->right, &param_sym);
838 if (param_name && param_sym)
839 goto set_state;
841 param_name = handle_container_of_assign(expr, &param_sym);
842 if (param_name && param_sym)
843 goto set_state;
845 goto free;
847 set_state:
848 set_state_expr(my_id, expr->left, alloc_var_sym_state(param_name, param_sym));
849 free:
850 free_string(param_name);
853 bool get_offset_param(const char *ret_str, int *offset, int *param)
855 const char *p;
857 if (!ret_str)
858 return false;
859 p = strstr(ret_str, "[(");
860 if (!p)
861 return false;
862 p += 2;
863 *offset = atoi(p);
864 p = strstr(p, "<~$");
865 if (!p)
866 return false;
867 p += 3;
868 if (!isdigit(p[0]))
869 return false;
870 *param = atoi(p);
871 return true;;
874 static void return_str_hook(struct expression *expr, const char *ret_str)
876 struct expression *call, *arg;
877 struct symbol *sym;
878 int offset, param;
879 char buf[32];
881 if (!expr || expr->type != EXPR_ASSIGNMENT)
882 return;
883 call = expr;
884 while (call && call->type == EXPR_ASSIGNMENT)
885 call = strip_expr(call->right);
886 if (!call || call->type != EXPR_CALL)
887 return;
889 if (!get_offset_param(ret_str, &offset, &param))
890 return;
892 arg = get_argument_from_call_expr(call->args, param);
893 arg = strip_expr(arg);
894 if (!arg)
895 return;
897 /* fixme this could be better */
898 if (arg->type != EXPR_SYMBOL)
899 return;
900 sym = arg->symbol;
902 param = get_param_num(arg);
903 if (param < 0)
904 return;
906 snprintf(buf, sizeof(buf), "(%d<~$%d)", offset, param);
907 set_state_expr(my_id, expr->left, alloc_var_sym_state(buf, sym));
910 void register_param_key(int id)
912 my_id = id;
914 set_dynamic_states(my_id);
915 add_hook(&match_assign, ASSIGNMENT_HOOK_AFTER);
916 add_return_string_hook(return_str_hook);
917 add_modification_hook(my_id, &undef);