2 * Copyright (C) 2021 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 #include "smatch_extra.h"
22 static struct expr_fn_list
*deref_hooks
;
28 const sval_t
*implies_start
, *implies_end
;
30 static struct deref_info fn_deref_table
[] = {
31 { "nla_data", 0, "$" },
33 { "__builtin_strlen", 0, "$" },
34 { "__fortify_strlen", 0, "$" },
35 { "spinlock_check", 0, "$" },
38 void add_dereference_hook(expr_func
*fn
)
40 add_ptr_list(&deref_hooks
, fn
);
43 static void call_deref_hooks(struct expression
*expr
)
45 if (__in_fake_assign
|| __in_fake_parameter_assign
)
50 call_expr_fns(deref_hooks
, expr
);
53 static bool is_array_pointer_math(struct expression
*expr
)
55 struct expression
*p
, *parent
;
58 /* Ignore "&array[0]". Fixme: wouldn't the & mean it was already ignored? */
59 p
= strip_expr(expr
->unop
);
61 if (!type
|| type
->type
!= SYM_PTR
)
64 /* Ignore "p->array". Here p is not actually dereferenced. */
65 parent
= expr_get_parent_expr(expr
);
69 type
= get_type(parent
);
70 if (!type
|| type
->type
!= SYM_ARRAY
)
72 while ((parent
= expr_get_parent_expr(parent
))) {
73 if (parent
->type
== EXPR_PREOP
&&
81 static void match_dereference(struct expression
*expr
)
83 struct expression
*p
, *tmp
;
86 if (expr
->type
!= EXPR_PREOP
||
90 if (is_array_pointer_math(expr
))
93 p
= strip_expr(expr
->unop
);
96 tmp
= get_assigned_expr(p
);
104 * Note that we only care about address assignments because other
105 * dereferences would have been handled already.
107 if (tmp
->type
!= EXPR_PREOP
|| tmp
->op
!= '&')
109 p
= strip_expr(tmp
->unop
);
110 if (p
->type
!= EXPR_DEREF
)
112 p
= strip_expr(p
->deref
);
113 if (p
->type
!= EXPR_PREOP
|| p
->op
!= '*')
115 p
= strip_expr(p
->unop
);
117 if (!type
|| type
->type
!= SYM_PTR
)
122 static void match_pointer_as_array(struct expression
*expr
)
124 struct expression
*array
;
129 if (getting_address(expr
))
132 array
= get_array_base(expr
);
133 type
= get_type(array
);
134 if (!type
|| type
->type
!= SYM_PTR
)
137 call_deref_hooks(array
);
140 static void dereference_inner_pointer(struct expression
*expr
)
142 if (expr
->type
!= EXPR_PREOP
||
145 expr
= strip_expr(expr
->unop
);
146 if (!expr
|| expr
->type
!= EXPR_DEREF
)
148 expr
= strip_expr(expr
->unop
);
149 if (!expr
|| expr
->type
!= EXPR_PREOP
|| expr
->op
!= '*')
151 expr
= strip_expr(expr
->unop
);
155 call_deref_hooks(expr
);
158 static void set_param_dereferenced(struct expression
*call
, struct expression
*arg
, char *key
, char *unused
)
160 struct expression
*deref
;
162 deref
= gen_expression_from_key(arg
, key
);
166 call_deref_hooks(deref
);
169 * Generally, this stuff should be handled by smatch_flow.c but
170 * smatch_flow.c doesn't have the PARAM_DEREFERENCED information so
171 * we go one level down to mark it as dereferenced.
174 dereference_inner_pointer(deref
);
177 static void param_deref(struct expression
*expr
)
179 call_deref_hooks(expr
);
182 void register_dereferences(int id
)
184 struct deref_info
*info
;
189 add_hook(&match_dereference
, DEREF_HOOK
);
190 add_hook(&match_pointer_as_array
, OP_HOOK
);
191 select_return_implies_hook_early(DEREFERENCE
, &set_param_dereferenced
);
193 for (i
= 0; i
< ARRAY_SIZE(fn_deref_table
); i
++) {
194 info
= &fn_deref_table
[i
];
195 add_param_key_expr_hook(info
->name
, ¶m_deref
, info
->param
, info
->key
, info
);