2 * sparse/smatch_struct_assignment.c
4 * Copyright (C) 2014 Oracle.
6 * Licensed under the Open Software License version 1.1
11 * This file started out by saying that if you have:
13 * struct foo one, two;
17 * That's equivalent to saying:
22 * Turning an assignment like that into a bunch of small fake assignments is
25 * The call to memcpy(&one, &two, sizeof(foo)); is the same as "one = two;" so
26 * we can re-use the code. And we may as well use it for memset() too.
27 * Assigning pointers is almost the same:
36 * The problem is that you can go a bit crazy with pointers to pointers.
38 * p1->x->y->z->one->two->three = p2->x->y->z->one->two->three;
40 * I don't have a proper solution for this problem right now. I just copy one
41 * level and don't nest. It should handle limitted nesting but intelligently.
43 * The other thing is that you end up with a lot of garbage assignments where
44 * we record "x could be anything. x->y could be anything. x->y->z->a->b->c
45 * could *also* be anything!". There should be a better way to filter this
46 * useless information.
52 #include "smatch_slist.h"
53 #include "smatch_extra.h"
55 static struct symbol
*get_struct_type(struct expression
*expr
)
59 type
= get_type(expr
);
62 if (type
->type
== SYM_PTR
)
63 type
= get_real_base_type(type
);
64 if (type
&& type
->type
== SYM_STRUCT
)
69 static int known_struct_member_states(struct expression
*expr
)
71 struct state_list
*slist
= __get_cur_slist();
78 if (expr
->type
== EXPR_PREOP
&& expr
->op
== '&')
79 expr
= strip_expr(expr
->unop
);
81 name
= expr_to_var(expr
);
86 FOR_EACH_PTR(slist
, sm
) {
87 if (sm
->owner
!= SMATCH_EXTRA
)
89 cmp
= strncmp(sm
->name
, name
, len
);
93 if (sm
->name
[len
] == '.' ||
94 sm
->name
[len
] == '-' ||
95 sm
->name
[len
] == '.') {
102 } END_FOR_EACH_PTR(sm
);
108 static struct expression
*get_matching_member_expr(struct symbol
*left_type
, struct expression
*right
, struct symbol
*left_member
)
110 struct symbol
*struct_type
;
113 if (!left_member
->ident
)
116 struct_type
= get_struct_type(right
);
119 if (struct_type
!= left_type
)
122 if (right
->type
== EXPR_PREOP
&& right
->op
== '&')
123 right
= strip_expr(right
->unop
);
125 if (is_pointer(right
)) {
126 right
= deref_expression(right
);
130 return member_expression(right
, op
, left_member
->ident
);
133 void __struct_members_copy(int mode
, struct expression
*left
, struct expression
*right
)
135 struct symbol
*struct_type
, *tmp
, *type
;
136 struct expression
*left_member
, *right_member
, *assign
;
140 if (__in_fake_assign
)
143 left
= strip_expr(left
);
144 right
= strip_expr(right
);
146 struct_type
= get_struct_type(left
);
150 if (is_pointer(left
)) {
151 left
= deref_expression(left
);
155 FOR_EACH_PTR(struct_type
->symbol_list
, tmp
) {
156 type
= get_real_base_type(tmp
);
157 if (type
&& type
->type
== SYM_ARRAY
)
160 left_member
= member_expression(left
, op
, tmp
->ident
);
165 right_member
= get_matching_member_expr(struct_type
, right
, tmp
);
168 right_member
= right
;
172 right_member
= unknown_value_expression(left_member
);
173 assign
= assign_expression(left_member
, right_member
);
175 __split_expr(assign
);
177 } END_FOR_EACH_PTR(tmp
);
180 void __fake_struct_member_assignments(struct expression
*expr
)
182 __struct_members_copy(COPY_NORMAL
, expr
->left
, expr
->right
);
185 static struct expression
*remove_addr(struct expression
*expr
)
187 expr
= strip_expr(expr
);
189 if (expr
->type
== EXPR_PREOP
&& expr
->op
== '&')
190 return strip_expr(expr
->unop
);
194 static void match_memset(const char *fn
, struct expression
*expr
, void *_size_arg
)
196 struct expression
*buf
;
197 struct expression
*val
;
199 buf
= get_argument_from_call_expr(expr
->args
, 0);
200 val
= get_argument_from_call_expr(expr
->args
, 1);
202 buf
= strip_expr(buf
);
203 __struct_members_copy(COPY_MEMSET
, remove_addr(buf
), val
);
206 static void match_memcpy(const char *fn
, struct expression
*expr
, void *_arg
)
208 struct expression
*dest
;
209 struct expression
*src
;
211 dest
= get_argument_from_call_expr(expr
->args
, 0);
212 src
= get_argument_from_call_expr(expr
->args
, 1);
214 __struct_members_copy(COPY_MEMCPY
, remove_addr(dest
), remove_addr(src
));
217 static void match_memcpy_unknown(const char *fn
, struct expression
*expr
, void *_arg
)
219 struct expression
*dest
;
221 dest
= get_argument_from_call_expr(expr
->args
, 0);
222 __struct_members_copy(COPY_MEMCPY
, remove_addr(dest
), NULL
);
225 static void register_clears_param(void)
229 const char *function
;
232 if (option_project
== PROJ_NONE
)
235 snprintf(name
, 256, "%s.clears_argument", option_project_str
);
237 token
= get_tokens_file(name
);
240 if (token_type(token
) != TOKEN_STREAMBEGIN
)
243 while (token_type(token
) != TOKEN_STREAMEND
) {
244 if (token_type(token
) != TOKEN_IDENT
)
246 function
= show_ident(token
->ident
);
248 if (token_type(token
) != TOKEN_NUMBER
)
250 param
= atoi(token
->number
);
251 add_function_hook(function
, &match_memcpy_unknown
, INT_PTR(param
));
257 void register_struct_assignment(int id
)
259 add_function_hook("memset", &match_memset
, NULL
);
261 add_function_hook("memcpy", &match_memcpy
, INT_PTR(0));
262 add_function_hook("memmove", &match_memcpy
, INT_PTR(0));
264 register_clears_param();