kernel_atomic_dec_test_path: move this into a separate module
[smatch.git] / smatch_param_key.c
blob31476ce9b040a1f86ee8fd6daf7d0b738f4b2c30
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
19 * The param/key stuff is changes "0/$->foo" into a name/sym pair.
20 * It also handles more complicated mappings like container_of(param).
21 * The problem with the current implementation of the container_of()
22 * code in 2023 is that it does stuff like (1624<~$0)->sk.sk_write_queue.next
23 * where 1624 is the number of bytes. It's difficult to write that kind
24 * of code by hand.
26 * We also need to be able to handle things like netdev_priv(). When you
27 * allocate a netdev struct then you have to specify how much space you need
28 * for your private data and it allocates a enough data to hold everything.
29 * the netdev_priv() function returns a pointer to beyond the end of the
30 * netdev struct. So the container_of() macro subtracts and the netdev_priv()
31 * function adds.
35 #include "ctype.h"
36 #include "smatch.h"
37 #include "smatch_extra.h"
38 #include "smatch_slist.h"
40 static int my_id;
42 char *swap_names(const char *orig, const char *remove, const char *add)
44 char buf[64];
45 int offset, len, ret;
46 bool is_addr = false;
47 bool is_star = false; /* fixme: this should be star_cnt */
48 bool is_end = false;
50 if (orig[0] == '*')
51 is_star = true;
53 if (add[0] == '&') {
54 if (is_star)
55 is_star = false;
56 else
57 is_addr = true;
58 add++;
61 offset = 0;
62 while(orig[offset] == '*' || orig[offset] == '&' || orig[offset] == '(')
63 offset++;
65 len = strlen(remove);
66 if (len + offset > strlen(orig))
67 return NULL;
68 if (orig[offset + len] == '\0')
69 is_end = true;
70 else if (orig[offset + len] != '-')
71 return NULL;
72 if (strncmp(orig + offset, remove, len) != 0)
73 return NULL;
75 if (!is_star && is_end)
76 return NULL;
78 ret = snprintf(buf, sizeof(buf), "%.*s%s%s%s", offset, orig,
79 add,
80 is_end ? "" : (is_addr ? "." : "->"),
81 is_end ? "" : orig + offset + 2 + len);
82 if (ret >= sizeof(buf))
83 return NULL;
84 return alloc_string(buf);
87 static char *swap_with_param(const char *name, struct symbol *sym, struct symbol **sym_p)
89 struct smatch_state *state;
90 struct var_sym *var_sym;
91 char *ret;
94 * Say you know that "foo = bar;" and you have a state "foo->baz" then
95 * we can just substitute "bar" for "foo" giving "bar->baz".
98 if (!sym || !sym->ident)
99 return NULL;
101 state = get_state(my_id, sym->ident->name, sym);
102 if (!state || !state->data)
103 return NULL;
104 var_sym = state->data;
106 ret = swap_names(name, sym->ident->name, var_sym->var);
107 if (!ret)
108 return NULL;
110 *sym_p = var_sym->sym;
111 return ret;
114 struct expression *map_container_of_to_simpler_expr_key(struct expression *expr, const char *orig_key, char **new_key)
116 struct expression *container;
117 int offset = -1;
118 char *p = (char *)orig_key;
119 char buf[64];
120 char *start;
121 int param;
122 int ret;
123 bool arrow = false;
124 bool no_member = false;
126 expr = strip_expr(expr);
127 if (expr->type != EXPR_DEREF &&
128 (expr->type != EXPR_PREOP && expr->op == '&'))
129 return NULL;
131 while (*p != '\0') {
132 if (*p == '(' && isdigit(*(p + 1))) {
133 start = p;
134 offset = strtoul(p + 1, &p, 10);
135 if (!p || strncmp(p, "<~$", 3) != 0)
136 return NULL;
137 p += 3;
138 if (!isdigit(p[0]))
139 return NULL;
140 param = strtoul(p + 1, &p, 10);
141 /* fixme */
142 if (param != 0)
143 return NULL;
144 if (!p)
145 return NULL;
146 if (strcmp(p, ")") == 0) {
147 no_member = true;
148 p++;
149 break;
151 if (strncmp(p, ")->", 3) != 0)
152 return NULL;
153 p += 3;
154 break;
156 p++;
158 if (!no_member && *p == '\0')
159 return NULL;
161 if (offset == get_member_offset_from_deref(expr)) {
162 if (expr->type == EXPR_PREOP && expr->op == '&') {
163 expr = strip_expr(expr->unop);
164 if (expr->type != EXPR_DEREF)
165 return NULL;
166 expr = strip_expr(expr->deref);
167 if (expr->type != EXPR_PREOP || expr->op != '*')
168 return NULL;
169 container = expr->unop;
170 arrow = true;
172 container = expr->deref;
173 } else {
174 container = get_stored_container(expr, offset);
175 if (!container)
176 return NULL;
177 arrow = true;
180 if (no_member) {
181 *new_key = alloc_sname("$");
182 return container;
185 ret = snprintf(buf, sizeof(buf), "%.*s$%s%s", (int)(start - orig_key), orig_key, arrow ? "->" : ".", p);
186 if (ret >= sizeof(buf))
187 return NULL;
188 *new_key = alloc_sname(buf);
190 return container;
193 struct expression *map_netdev_priv_to_simpler_expr_key(struct expression *expr, const char *orig_key, char **new_key)
195 struct expression *priv;
196 char buf[64];
197 int diff;
198 char *p;
200 priv = get_netdev_priv(expr);
201 if (!priv)
202 return NULL;
203 if (priv->type != EXPR_SYMBOL || !priv->symbol_name)
204 return NULL;
206 p = strstr(orig_key, "r netdev_priv($)");
207 if (!p)
208 return NULL;
209 diff = p - orig_key;
210 if (diff == 1) /* remove () */
211 snprintf(buf, sizeof(buf), "$%s", orig_key + 18);
212 else
213 snprintf(buf, sizeof(buf), "%.*s$%s", diff, orig_key, orig_key + diff + 16);
214 *new_key = alloc_sname(buf);
215 return priv;
218 char *get_variable_from_key(struct expression *arg, const char *key, struct symbol **sym)
220 struct symbol *type;
221 char buf[256];
222 char *tmp;
223 bool address = false;
224 int star_cnt = 0;
225 bool add_dot = false;
226 int ret;
228 // FIXME: this function has been marked for being made static
229 // Use get_name_sym_from_param_key().
231 if (sym)
232 *sym = NULL;
234 if (!arg)
235 return NULL;
237 arg = strip_expr(arg);
239 if (strcmp(key, "$") == 0)
240 return expr_to_var_sym(arg, sym);
242 if (strcmp(key, "*$") == 0) {
243 if (arg->type == EXPR_PREOP && arg->op == '&') {
244 arg = strip_expr(arg->unop);
245 return expr_to_var_sym(arg, sym);
246 } else {
247 tmp = expr_to_var_sym(arg, sym);
248 if (!tmp)
249 return NULL;
250 ret = snprintf(buf, sizeof(buf), "*%s", tmp);
251 free_string(tmp);
252 if (ret >= sizeof(buf))
253 return NULL;
254 return alloc_string(buf);
258 if (strncmp(key, "(*$)", 4) == 0) {
259 if (arg->type == EXPR_PREOP && arg->op == '&') {
260 arg = strip_expr(arg->unop);
261 snprintf(buf, sizeof(buf), "$%s", key + 4);
262 return get_variable_from_key(arg, buf, sym);
263 } else {
264 tmp = expr_to_var_sym(arg, sym);
265 if (!tmp)
266 return NULL;
267 ret = snprintf(buf, sizeof(buf), "(*%s)%s", tmp, key + 4);
268 free_string(tmp);
269 if (ret >= sizeof(buf))
270 return NULL;
271 return alloc_string(buf);
275 if (strstr(key, "<~$")) {
276 struct expression *expr;
277 char *new_key = NULL;
279 expr = map_container_of_to_simpler_expr_key(arg, key, &new_key);
280 if (!expr)
281 return NULL;
282 if (arg != expr) {
283 arg = expr;
284 *sym = expr_to_sym(expr);
286 key = new_key;
289 if (strstr(key, "(r netdev_priv($))")) {
290 struct expression *expr;
291 char *new_key = NULL;
293 expr = map_netdev_priv_to_simpler_expr_key(arg, key, &new_key);
294 if (!expr)
295 return NULL;
296 if (arg != expr) {
297 arg = expr;
298 *sym = expr_to_sym(expr);
300 key = new_key;
303 while (key[0] == '*') {
304 star_cnt++;
305 key++;
308 if (key[0] == '&') {
309 address = true;
310 key++;
314 * FIXME: This is a hack.
315 * We should be able to parse expressions like (*$)->foo and *$->foo.
317 type = get_type(arg);
318 if (is_struct_ptr(type))
319 add_dot = true;
321 if (arg->type == EXPR_PREOP && arg->op == '&' && star_cnt && !add_dot) {
322 arg = strip_expr(arg->unop);
323 star_cnt--;
326 if (arg->type == EXPR_PREOP && arg->op == '&') {
327 arg = strip_expr(arg->unop);
328 tmp = expr_to_var_sym(arg, sym);
329 if (!tmp)
330 return NULL;
331 ret = snprintf(buf, sizeof(buf), "%s%.*s%s.%s",
332 address ? "&" : "", star_cnt, "**********",
333 tmp, key + 3);
334 if (ret >= sizeof(buf))
335 return NULL;
336 return alloc_string(buf);
339 tmp = expr_to_var_sym(arg, sym);
340 if (!tmp)
341 return NULL;
342 ret = snprintf(buf, sizeof(buf), "%s%.*s%s%s",
343 address ? "&" : "", star_cnt, "**********", tmp, key + 1);
344 free_string(tmp);
345 if (ret >= sizeof(buf))
346 return NULL;
347 return alloc_string(buf);
350 bool split_param_key(const char *value, int *param, char *key, int len)
352 char *p;
353 int l, skip = 1;
355 l = snprintf(key, len, "%s", value);
356 if (l >= len)
357 return false;
359 p = key;
360 while (*p && *p != '$')
361 p++;
362 if (*p != '$')
363 return false;
364 p++;
366 *param = atoi(p);
367 if (*param < 0 || *param > 99)
368 return false;
370 p++;
371 if (*param > 9) {
372 skip = 2;
373 p++;
376 memmove(p - skip, p, l - (p - key) + 1);
378 return true;
381 bool get_implied_rl_from_call_str(struct expression *expr, const char *data, struct range_list **rl)
383 struct smatch_state *state;
384 struct expression *arg;
385 struct symbol *sym;
386 char buf[256];
387 char *name;
388 int param;
390 while (expr->type == EXPR_ASSIGNMENT)
391 expr = expr->right;
392 if (expr->type != EXPR_CALL)
393 return false;
395 if (!split_param_key(data, &param, buf, sizeof(buf)))
396 return false;
398 if (strcmp(buf, "$") == 0) {
399 arg = get_argument_from_call_expr(expr->args, param);
400 if (!arg)
401 return false;
402 return get_implied_rl(arg, rl);
405 name = get_name_sym_from_param_key(expr, param, buf, &sym);
406 if (!name)
407 return false;
409 state = get_state(SMATCH_EXTRA, name, sym);
410 if (!estate_rl(state))
411 return false;
412 *rl = estate_rl(state);
413 return true;
416 char *get_chunk_from_key(struct expression *arg, char *key, struct symbol **sym, struct var_sym_list **vsl)
418 *vsl = NULL;
420 if (strcmp("$", key) == 0)
421 return expr_to_chunk_sym_vsl(arg, sym, vsl);
422 return get_variable_from_key(arg, key, sym);
425 static char *state_name_to_param_name(const char *state_name, const char *param_name)
427 bool address = false;
428 int star_cnt = 0;
429 int name_len;
430 char buf[256];
431 int ret;
434 * Normally what happens is that we map "*foo->bar" to "*param->bar"
435 * but with container_of() there is no notation for that in C and it's
436 * just a Smatch invention. So in that case, the state name is the
437 * param name.
439 if (strstr(state_name, "<~$"))
440 return (char *)state_name;
442 if (strstr(state_name, "r netdev_priv($)"))
443 return (char *)state_name;
445 name_len = strlen(param_name);
447 while (state_name[0] == '*') {
448 star_cnt++;
449 state_name++;
452 if (state_name[0] == '&') {
453 address = true;
454 state_name++;
457 /* ten out of ten stars! */
458 if (star_cnt > 10)
459 return NULL;
461 if (strncmp(state_name, "(*", 2) == 0 &&
462 strncmp(state_name + 2, param_name, name_len) == 0 &&
463 state_name[name_len + 2] == ')') {
464 ret = snprintf(buf, sizeof(buf), "%s%.*s(*$)%s",
465 address ? "&" : "",
466 star_cnt, "**********",
467 state_name + name_len + 3);
468 if (ret >= sizeof(buf))
469 return NULL;
470 return alloc_sname(buf);
473 if (strcmp(state_name, param_name) == 0) {
474 snprintf(buf, sizeof(buf), "%s%.*s$",
475 address ? "&" : "",
476 star_cnt, "**********");
477 return alloc_sname(buf);
480 /* check for '-' from "->" */
481 if (strncmp(state_name, param_name, name_len) == 0 &&
482 state_name[name_len] == '-') {
483 ret = snprintf(buf, sizeof(buf), "%s%.*s$%s",
484 address ? "&" : "",
485 star_cnt, "**********",
486 state_name + name_len);
487 if (ret >= sizeof(buf))
488 return NULL;
489 return alloc_sname(buf);
491 return NULL;
494 char *get_param_name_var_sym(const char *name, struct symbol *sym)
496 if (!sym || !sym->ident)
497 return NULL;
499 return state_name_to_param_name(name, sym->ident->name);
502 const char *get_mtag_name_var_sym(const char *state_name, struct symbol *sym)
504 struct symbol *type;
505 const char *sym_name;
506 int name_len;
507 static char buf[256];
510 * mtag_name is different from param_name because mtags can be a struct
511 * instead of a struct pointer. But we want to treat it like a pointer
512 * because really an mtag is a pointer. Or in other words, if you pass
513 * a struct foo then you want to talk about foo.bar but with an mtag
514 * you want to refer to it as foo->bar.
518 if (!sym || !sym->ident)
519 return NULL;
521 type = get_real_base_type(sym);
522 if (type && type->type == SYM_BASETYPE)
523 return "*$";
525 sym_name = sym->ident->name;
526 name_len = strlen(sym_name);
528 if (state_name[name_len] == '.' && /* check for '-' from "->" */
529 strncmp(state_name, sym_name, name_len) == 0) {
530 snprintf(buf, sizeof(buf), "$->%s", state_name + name_len + 1);
531 return buf;
534 return state_name_to_param_name(state_name, sym_name);
537 const char *get_mtag_name_expr(struct expression *expr)
539 char *name;
540 struct symbol *sym;
541 const char *ret = NULL;
543 name = expr_to_var_sym(expr, &sym);
544 if (!name || !sym)
545 goto free;
547 ret = get_mtag_name_var_sym(name, sym);
548 free:
549 free_string(name);
550 return ret;
553 char *get_param_name(struct sm_state *sm)
555 return get_param_name_var_sym(sm->name, sm->sym);
558 char *get_param_var_sym_var_sym(const char *name, struct symbol *sym, struct expression *ret_expr, struct symbol **sym_p)
560 struct smatch_state *state;
561 struct var_sym *var_sym;
562 int param;
564 *sym_p = NULL;
566 // FIXME was modified...
568 param = get_param_num_from_sym(sym);
569 if (param >= 0) {
570 *sym_p = sym;
571 return alloc_string(name);
574 state = get_state(my_id, name, sym);
575 if (state && state->data) {
576 var_sym = state->data;
577 if (!var_sym)
578 return NULL;
580 *sym_p = var_sym->sym;
581 return alloc_string(var_sym->var);
584 /* One would think that handling container_of() should be done here
585 * but it it's quite tricky because we only have a name and a sym
586 * and none of the assignments have been handled yet, either here or
587 * in smatch_assignments.c. On the other hand handling container_of()
588 * in the assignment hook has the advantage that it saves resources and
589 * it should work fine because of the fake assignments which we do.
592 return swap_with_param(name, sym, sym_p);
595 char *get_param_name_sym(struct expression *expr, struct symbol **sym_p)
597 struct symbol *sym;
598 const char *ret = NULL;
599 char *name;
601 name = expr_to_var_sym(expr, &sym);
602 if (!name || !sym)
603 goto free;
605 ret = get_param_var_sym_var_sym(name, sym, NULL, sym_p);
606 free:
607 free_string(name);
608 return alloc_string(ret);
611 int get_return_param_key_from_var_sym(const char *name, struct symbol *sym,
612 struct expression *ret_expr,
613 const char **key)
615 const char *param_name;
616 struct symbol *ret_sym;
617 char *ret_str;
619 if (!ret_expr)
620 return -2;
622 ret_str = expr_to_str_sym(ret_expr, &ret_sym);
623 if (ret_str && ret_sym == sym) {
624 param_name = state_name_to_param_name(name, ret_str);
625 if (param_name) {
626 free_string(ret_str);
627 if (key)
628 *key = param_name;
629 return -1;
632 free_string(ret_str);
634 return -2;
637 int get_param_key_from_var_sym(const char *name, struct symbol *sym,
638 struct expression *ret_expr,
639 const char **key)
641 const char *param_name;
642 char *other_name;
643 struct symbol *other_sym;
644 int param;
646 if (key)
647 *key = name;
649 /* straight forward param match */
650 param = get_param_num_from_sym(sym);
651 if (param >= 0) {
652 param_name = get_param_name_var_sym(name, sym);
653 if (param_name) {
654 if (key)
655 *key = param_name;
656 return param;
660 param = get_return_param_key_from_var_sym(name, sym, ret_expr, key);
661 if (param == -1)
662 return param;
664 other_name = get_param_var_sym_var_sym(name, sym, ret_expr, &other_sym);
665 if (!other_name || !other_sym)
666 return -2;
667 param = get_param_num_from_sym(other_sym);
668 if (param < 0)
669 return -2;
671 param_name = get_param_name_var_sym(other_name, other_sym);
672 if (param_name) {
673 if (key)
674 *key = param_name;
675 return param;
677 return -2;
680 int get_param_key_from_sm(struct sm_state *sm, struct expression *ret_expr,
681 const char **key)
683 return get_param_key_from_var_sym(sm->name, sm->sym, ret_expr, key);
686 int get_param_key_from_expr(struct expression *expr, struct expression *ret_expr,
687 const char **key)
689 char *name;
690 struct symbol *sym;
691 int ret = -2;
693 *key = NULL;
694 name = expr_to_var_sym(expr, &sym);
695 if (!name || !sym)
696 goto free;
698 ret = get_param_key_from_var_sym(name, sym, ret_expr, key);
699 free:
700 free_string(name);
701 return ret;
704 const char *get_param_key_swap_dollar(struct expression *expr)
706 struct sm_state *sm;
707 const char *key, *p;
708 char buf[64];
709 int param;
711 sm = get_sm_state_expr(my_id, expr);
712 if (!sm || slist_has_state(sm->possible, &undefined))
713 return NULL;
715 param = get_param_key_from_expr(expr, NULL, &key);
716 if (param < 0)
717 return NULL;
719 p = strchr(key, '$');
720 if (!p)
721 return NULL;
723 snprintf(buf, sizeof(buf), "%.*s%d%s", (int)(p - key + 1), key, param, p + 1);
724 return alloc_sname(buf);
727 int map_to_param(const char *name, struct symbol *sym)
729 return get_param_key_from_var_sym(name, sym, NULL, NULL);
732 int get_param_num_from_sym(struct symbol *sym)
734 struct symbol *tmp;
735 int i;
737 if (!sym)
738 return UNKNOWN_SCOPE;
740 if (sym->ctype.modifiers & MOD_TOPLEVEL) {
741 if (sym->ctype.modifiers & MOD_STATIC)
742 return FILE_SCOPE;
743 return GLOBAL_SCOPE;
746 if (!cur_func_sym)
747 return GLOBAL_SCOPE;
749 i = 0;
750 FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, tmp) {
751 if (tmp == sym)
752 return i;
753 i++;
754 } END_FOR_EACH_PTR(tmp);
755 return LOCAL_SCOPE;
758 int get_param_num(struct expression *expr)
760 struct symbol *sym;
761 char *name;
763 if (!cur_func_sym)
764 return UNKNOWN_SCOPE;
765 name = expr_to_var_sym(expr, &sym);
766 free_string(name);
767 if (!sym)
768 return UNKNOWN_SCOPE;
769 return get_param_num_from_sym(sym);
772 struct symbol *get_param_sym_from_num(int num)
774 struct symbol *sym;
775 int i;
777 if (!cur_func_sym)
778 return NULL;
780 i = 0;
781 FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, sym) {
782 if (i++ == num)
783 return sym;
784 } END_FOR_EACH_PTR(sym);
785 return NULL;
788 struct expression *get_function_param(struct expression *expr, int param)
790 struct expression *call;
792 if (!expr) {
793 sm_msg("internal: null call_expr. param=%d", param);
794 return NULL;
797 call = expr;
798 while (call->type == EXPR_ASSIGNMENT)
799 call = strip_expr(call->right);
801 if (call->type != EXPR_CALL)
802 return NULL;
804 if (param < -1)
805 return NULL;
807 if (param == -1) {
808 if (expr->type == EXPR_ASSIGNMENT && expr->op == '=')
809 return expr->left;
810 return NULL;
813 return get_argument_from_call_expr(call->args, param);
816 char *get_name_sym_from_param_key(struct expression *expr, int param, const char *key, struct symbol **sym)
818 struct expression *arg;
819 char *name;
821 if (sym)
822 *sym = NULL;
824 if (param == -2) // Really? Is this sane?
825 return alloc_string(key);
827 arg = get_function_param(expr, param);
828 if (!arg)
829 return NULL;
831 name = get_variable_from_key(arg, key, sym);
832 if (!name || (sym && !*sym))
833 goto free;
835 return name;
836 free:
837 free_string(name);
838 return NULL;
841 static char *handle_container_of_assign(struct expression *expr, struct symbol **sym)
843 struct expression *right, *orig;
844 struct symbol *type;
845 sval_t sval;
846 int param;
847 char buf[64];
849 type = get_type(expr->left);
850 if (!type || type->type != SYM_PTR)
851 return NULL;
853 right = strip_expr(expr->right);
854 if (right->type != EXPR_BINOP || right->op != '-')
855 return NULL;
857 if (!get_value(right->right, &sval) ||
858 sval.value < 0 || sval.value > MTAG_OFFSET_MASK)
859 return NULL;
861 orig = get_assigned_expr(right->left);
862 if (!orig)
863 return NULL;
864 if (orig->type != EXPR_SYMBOL)
865 return NULL;
866 param = get_param_num_from_sym(orig->symbol);
867 if (param < 0)
868 return NULL;
870 snprintf(buf, sizeof(buf), "(%lld<~$%d)", sval.value, param);
871 *sym = orig->symbol;
872 return alloc_string(buf);
875 static char *handle_netdev_priv_assign(struct expression *expr, struct symbol **sym)
877 struct expression *right, *arg;
878 char buf[64];
880 if (option_project != PROJ_KERNEL)
881 return NULL;
883 if (!expr || expr->type != EXPR_ASSIGNMENT)
884 return NULL;
885 right = strip_expr(expr->right);
886 if (!right || right->type != EXPR_CALL)
887 return NULL;
889 if (!sym_name_is("netdev_priv", right->fn))
890 return NULL;
892 arg = get_argument_from_call_expr(right->args, 0);
893 arg = strip_expr(arg);
894 if (!arg || arg->type != EXPR_SYMBOL)
895 return NULL;
897 /* This isn't necessarily tied to a param */
898 snprintf(buf, sizeof(buf), "(r netdev_priv($))");
899 *sym = arg->symbol;
900 return alloc_string(buf);
903 const char *get_container_of_str(struct expression *expr)
905 struct smatch_state *state;
907 state = get_state_expr(my_id, expr);
908 if (!state)
909 return NULL;
910 if (!strstr(state->name, "<~$"))
911 return NULL;
912 return state->name;
915 static void match_assign(struct expression *expr)
917 struct symbol *param_sym;
918 char *param_name;
920 if (expr->op != '=')
921 return;
923 /* __in_fake_parameter_assign is included deliberately */
924 if (is_fake_call(expr->right) ||
925 __in_fake_struct_assign)
926 return;
928 param_name = get_param_name_sym(expr->right, &param_sym);
929 if (param_name && param_sym)
930 goto set_state;
932 param_name = handle_container_of_assign(expr, &param_sym);
933 if (param_name && param_sym)
934 goto set_state;
936 param_name = handle_netdev_priv_assign(expr, &param_sym);
937 if (param_name && param_sym)
938 goto set_state;
940 goto free;
942 set_state:
943 set_state_expr(my_id, expr->left, alloc_var_sym_state(param_name, param_sym));
944 free:
945 free_string(param_name);
948 bool get_offset_param(const char *ret_str, int *offset, int *param)
950 const char *p;
952 if (!ret_str)
953 return false;
954 p = strstr(ret_str, "[(");
955 if (!p)
956 return false;
957 p += 2;
958 *offset = atoi(p);
959 p = strstr(p, "<~$");
960 if (!p)
961 return false;
962 p += 3;
963 if (!isdigit(p[0]))
964 return false;
965 *param = atoi(p);
966 return true;;
969 static void return_str_hook(struct expression *expr, const char *ret_str)
971 struct expression *call, *arg;
972 struct symbol *sym;
973 int offset, param;
974 char buf[32];
976 if (!expr || expr->type != EXPR_ASSIGNMENT)
977 return;
978 call = expr;
979 while (call && call->type == EXPR_ASSIGNMENT)
980 call = strip_expr(call->right);
981 if (!call || call->type != EXPR_CALL)
982 return;
984 if (!get_offset_param(ret_str, &offset, &param))
985 return;
987 arg = get_argument_from_call_expr(call->args, param);
988 arg = strip_expr(arg);
989 if (!arg)
990 return;
992 /* fixme this could be better */
993 if (arg->type != EXPR_SYMBOL)
994 return;
995 sym = arg->symbol;
997 param = get_param_num(arg);
998 if (param < 0)
999 return;
1001 snprintf(buf, sizeof(buf), "(%d<~$%d)", offset, param);
1002 set_state_expr(my_id, expr->left, alloc_var_sym_state(buf, sym));
1005 void register_param_key(int id)
1007 my_id = id;
1009 set_dynamic_states(my_id);
1010 add_hook(&match_assign, ASSIGNMENT_HOOK_AFTER);
1011 add_return_string_hook(return_str_hook);
1012 add_modification_hook(my_id, &set_undefined);