95 __mptr = get_assigned_expr_name_sym(sym->ident->name, sym);
96 param = get_param_from_container_of(__mptr);
97
98 return param;
99 }
100
101 static int get_container_offset(struct symbol *sym)
102 {
103 struct expression *__mptr;
104 int offset;
105
106 if (!sym || !sym->ident)
107 return -1;
108
109 __mptr = get_assigned_expr_name_sym(sym->ident->name, sym);
110 offset = get_offset_from_container_of(__mptr);
111
112 return offset;
113 }
114
115 static char *get_container_name(struct sm_state *sm, int offset)
116 {
117 static char buf[256];
118 const char *name;
119
120 name = get_param_name(sm);
121 if (!name)
122 return NULL;
123
124 if (name[0] == '$')
125 snprintf(buf, sizeof(buf), "$(-%d)%s", offset, name + 1);
126 else if (name[0] == '*' || name[1] == '$')
127 snprintf(buf, sizeof(buf), "*$(-%d)%s", offset, name + 2);
128 else
129 return NULL;
130
131 return buf;
132 }
133
134 static void get_state_hook(int owner, const char *name, struct symbol *sym)
135 {
156 goto free;
157
158 arg_nr = get_container_arg(sym);
159 if (arg_nr >= 0)
160 set_state(my_id, name, sym, &used);
161 free:
162 free_string(name);
163 }
164
165 static void process_states(void)
166 {
167 struct sm_state *tmp;
168 int arg, offset;
169 const char *name;
170
171 FOR_EACH_SM(used_stree, tmp) {
172 arg = get_container_arg(tmp->sym);
173 offset = get_container_offset(tmp->sym);
174 if (arg < 0 || offset < 0)
175 continue;
176 name = get_container_name(tmp, offset);
177 if (!name)
178 continue;
179 sql_insert_return_implies(CONTAINER, arg, name, "");
180 } END_FOR_EACH_SM(tmp);
181
182 free_stree(&used_stree);
183 }
184
185 static void match_function_def(struct symbol *sym)
186 {
187 free_stree(&used_stree);
188 }
189
190 static void match_save_states(struct expression *expr)
191 {
192 push_stree(&saved_stack, used_stree);
193 used_stree = NULL;
194 }
195
196 static void match_restore_states(struct expression *expr)
232 call = strip_expr(expr->right);
233 if (call->type != EXPR_CALL)
234 return;
235 if (param != -1)
236 return;
237 param = atoi(key);
238 offset = atoi(value);
239
240 arg = get_argument_from_call_expr(call->args, param);
241 if (!arg)
242 return;
243 if (arg->type != EXPR_SYMBOL)
244 return;
245 param = get_param_num(arg);
246 if (param < 0)
247 return;
248 snprintf(buf, sizeof(buf), "$(%d)", offset);
249 sql_insert_return_implies(CONTAINER, param, buf, "");
250 }
251
252 static int get_shared_cnt(const char *one, const char *two)
253 {
254 int i;
255 int on_end = false;
256
257 i = 0;
258 while (true) {
259 if (!one[i] || !two[i]) {
260 on_end = true;
261 break;
262 }
263 if (one[i] != two[i])
264 break;
265 i++;
266 }
267 if (i == 0)
268 return 0;
269 i--;
270 while (i > 0 && (one[i] == '>' || one[i] == '-' || one[i] == '.')) {
271 on_end = true;
272 i--;
273 }
274 if (!on_end)
275 return 0;
276
277 return i + 1;
278 }
279
280 static int build_offset_str(struct expression *expr, const char *name,
281 int shared, char *buf, int size, int op)
282 {
283 int chop = 0;
284 int offset;
285 int i;
286
287 i = shared;
288 while (name[i]) {
289 if (name[i] == '.' || name[i] == '-')
290 chop++;
291 i++;
292 }
293
294 // FIXME: Handle more chops
295 if (chop > 1)
296 return 0;
297
298 if (chop == 0) {
299 offset = 0;
300 } else {
301 offset = get_member_offset_from_deref(expr);
302 if (offset < 0)
303 return 0;
304 }
305
306 snprintf(buf, size, "%c%d", (op == '+') ? '+' : '-', offset);
307 return 1;
308 }
309
310 static void match_call(struct expression *call)
311 {
312 struct expression *fn, *arg;
313 char *fn_name, *arg_name;
314 int param, shared;
315 char minus_str[64];
316 char plus_str[64];
317 char offset_str[64];
318 bool star;
319
320 /*
321 * We're trying to link the function with the parameter. There are a
322 * couple ways this can be passed:
323 * foo->func(foo, ...);
324 * foo->func(foo->x, ...);
325 * foo->bar.func(&foo->bar, ...);
326 * foo->bar->baz->func(foo, ...);
327 *
328 * So the method is basically to subtract the offsets until we get to
329 * the common bit, then add the member offsets to get the parameter.
330 *
331 * If we're taking an address then the offset math is not stared,
332 * otherwise it is. Starred means dereferenced.
333 */
334 fn = strip_expr(call->fn);
335 fn_name = expr_to_var(fn);
336 if (!fn_name)
337 return;
338
339 param = -1;
340 FOR_EACH_PTR(call->args, arg) {
341 param++;
342
343 arg = strip_expr(arg);
344 star = true;
345 if (arg->type == EXPR_PREOP && arg->op == '&') {
346 arg = strip_expr(arg->unop);
347 star = false;
348 }
349
350 arg_name = expr_to_var(arg);
351 if (!arg_name)
352 continue;
353 shared = get_shared_cnt(fn_name, arg_name);
354 if (!shared)
355 goto free_arg_name;
356 if (!build_offset_str(fn, fn_name, shared, minus_str, sizeof(minus_str), '-'))
357 goto free_arg_name;
358 if (!build_offset_str(arg, arg_name, shared, plus_str, sizeof(plus_str), '+'))
359 goto free_arg_name;
360 if (star)
361 snprintf(offset_str, sizeof(offset_str), "*(%s%s)", minus_str, plus_str);
362 else
363 snprintf(offset_str, sizeof(offset_str), "%s%s", minus_str, plus_str);
364 sql_insert_caller_info(call, CONTAINER, param, offset_str, "$(-1)");
365 free_arg_name:
366 free_string(arg_name);
367 } END_FOR_EACH_PTR(arg);
368
369 free_string(fn_name);
370 }
371
372 static void db_passed_container(const char *name, struct symbol *sym, char *key, char *value)
373 {
374 sval_t offset = {
375 .type = &int_ctype,
376 };
377 const char *arg_offset;
378 int star = 0;
379 int val;
380
381 if (key[0] == '*') {
382 star = 1;
383 key += 2;
384 }
385
386 val = atoi(key);
387 if (val < -4095 || val > 0)
388 return;
389 offset.value = -val;
390 arg_offset = strchr(key, '+');
391 if (!arg_offset)
392 return;
393 val = atoi(arg_offset + 1);
394 if (val > 4095 || val < 0)
395 return;
396 offset.value |= val << 16;
397 if (star)
398 offset.value |= 1ULL << 31;
399
400 set_state(param_id, name, sym, alloc_estate_sval(offset));
401 }
402
403 struct db_info {
404 struct symbol *arg;
405 int prev_offset;
406 struct range_list *rl;
407 int star;
408 struct stree *stree;
409 };
410
411 static struct symbol *get_member_from_offset(struct symbol *sym, int offset)
412 {
413 struct symbol *type, *tmp;
414 int cur;
415
416 type = get_real_base_type(sym);
417 if (!type || type->type != SYM_PTR)
418 return NULL;
419 type = get_real_base_type(type);
420 if (!type || type->type != SYM_STRUCT)
557 } else { /* presumably the parameter is a struct pointer */
558 run_sql(save_vals, &db_info,
559 "select offset, value from mtag_data where tag = %lld and type = %d;",
560 tag, DATA_VALUE);
561 }
562
563 if (db_info.prev_offset != -1)
564 set_param_value(&db_info.stree, arg, db_info.prev_offset, db_info.rl);
565
566 // FIXME: handle an offset correctly
567 if (!star && !arg_offset) {
568 sval_t sval;
569
570 sval.type = get_real_base_type(arg);
571 sval.uvalue = tag;
572 set_state_stree(&db_info.stree, SMATCH_EXTRA, arg->ident->name, arg, alloc_estate_sval(sval));
573 }
574 return db_info.stree;
575 }
576
577 static void handle_passed_container(struct symbol *sym)
578 {
579 struct symbol *arg;
580 struct smatch_state *state;
581 struct sm_state *sm;
582 struct stree *stree;
583 mtag_t fn_tag, container_tag, arg_tag;
584 sval_t offset;
585 int container_offset, arg_offset;
586 int star;
587
588 FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
589 state = get_state(param_id, arg->ident->name, arg);
590 if (state)
591 goto found;
592 } END_FOR_EACH_PTR(arg);
593
594 return;
595 found:
596 if (!estate_get_single_value(state, &offset))
597 return;
598 container_offset = -(offset.value & 0xffff);
599 arg_offset = (offset.value & 0xfff0000) >> 16;
600 star = !!(offset.value & (1ULL << 31));
601
602 if (!get_toplevel_mtag(cur_func_sym, &fn_tag))
603 return;
604 if (!mtag_map_select_container(fn_tag, container_offset, &container_tag))
605 return;
606 if (!arg_offset || star) {
607 arg_tag = container_tag;
608 } else {
609 if (!mtag_map_select_tag(container_tag, -arg_offset, &arg_tag))
610 return;
611 }
612
613 stree = load_tag_info_sym(arg_tag, arg, arg_offset, star);
614 FOR_EACH_SM(stree, sm) {
615 set_state(sm->owner, sm->name, sm->sym, sm->state);
616 } END_FOR_EACH_SM(sm);
617 free_stree(&stree);
618 }
619
620 void register_container_of(int id)
621 {
622 my_id = id;
623
624 add_hook(&match_function_def, FUNC_DEF_HOOK);
625
626 add_get_state_hook(&get_state_hook);
627
628 add_hook(&match_save_states, INLINE_FN_START);
629 add_hook(&match_restore_states, INLINE_FN_END);
630
631 select_return_implies_hook(CONTAINER, &set_param_used);
632 all_return_states_hook(&process_states);
633
634 add_split_return_callback(&print_returns_container_of);
635 select_return_states_hook(CONTAINER, &returns_container_of);
636
637 add_hook(&match_call, FUNCTION_CALL_HOOK);
638 }
639
640 static struct smatch_state *unmatched_state(struct sm_state *sm)
641 {
642 return alloc_estate_whole(estate_type(sm->state));
643 }
644
645 void register_container_of2(int id)
646 {
647 param_id = id;
648
649 select_caller_info_hook(db_passed_container, CONTAINER);
650 add_hook(&handle_passed_container, AFTER_DEF_HOOK);
651 add_unmatched_state_hook(param_id, &unmatched_state);
652 add_merge_hook(param_id, &merge_estates);
653 }
654
|
95 __mptr = get_assigned_expr_name_sym(sym->ident->name, sym);
96 param = get_param_from_container_of(__mptr);
97
98 return param;
99 }
100
101 static int get_container_offset(struct symbol *sym)
102 {
103 struct expression *__mptr;
104 int offset;
105
106 if (!sym || !sym->ident)
107 return -1;
108
109 __mptr = get_assigned_expr_name_sym(sym->ident->name, sym);
110 offset = get_offset_from_container_of(__mptr);
111
112 return offset;
113 }
114
115 static char *get_container_name_sm(struct sm_state *sm, int offset)
116 {
117 static char buf[256];
118 const char *name;
119
120 name = get_param_name(sm);
121 if (!name)
122 return NULL;
123
124 if (name[0] == '$')
125 snprintf(buf, sizeof(buf), "$(-%d)%s", offset, name + 1);
126 else if (name[0] == '*' || name[1] == '$')
127 snprintf(buf, sizeof(buf), "*$(-%d)%s", offset, name + 2);
128 else
129 return NULL;
130
131 return buf;
132 }
133
134 static void get_state_hook(int owner, const char *name, struct symbol *sym)
135 {
156 goto free;
157
158 arg_nr = get_container_arg(sym);
159 if (arg_nr >= 0)
160 set_state(my_id, name, sym, &used);
161 free:
162 free_string(name);
163 }
164
165 static void process_states(void)
166 {
167 struct sm_state *tmp;
168 int arg, offset;
169 const char *name;
170
171 FOR_EACH_SM(used_stree, tmp) {
172 arg = get_container_arg(tmp->sym);
173 offset = get_container_offset(tmp->sym);
174 if (arg < 0 || offset < 0)
175 continue;
176 name = get_container_name_sm(tmp, offset);
177 if (!name)
178 continue;
179 sql_insert_return_implies(CONTAINER, arg, name, "");
180 } END_FOR_EACH_SM(tmp);
181
182 free_stree(&used_stree);
183 }
184
185 static void match_function_def(struct symbol *sym)
186 {
187 free_stree(&used_stree);
188 }
189
190 static void match_save_states(struct expression *expr)
191 {
192 push_stree(&saved_stack, used_stree);
193 used_stree = NULL;
194 }
195
196 static void match_restore_states(struct expression *expr)
232 call = strip_expr(expr->right);
233 if (call->type != EXPR_CALL)
234 return;
235 if (param != -1)
236 return;
237 param = atoi(key);
238 offset = atoi(value);
239
240 arg = get_argument_from_call_expr(call->args, param);
241 if (!arg)
242 return;
243 if (arg->type != EXPR_SYMBOL)
244 return;
245 param = get_param_num(arg);
246 if (param < 0)
247 return;
248 snprintf(buf, sizeof(buf), "$(%d)", offset);
249 sql_insert_return_implies(CONTAINER, param, buf, "");
250 }
251
252 static int get_deref_count(struct expression *expr)
253 {
254 int cnt = 0;
255
256 while (expr && expr->type == EXPR_DEREF) {
257 expr = expr->deref;
258 if (expr->type == EXPR_PREOP && expr->op == '*')
259 expr = expr->unop;
260 cnt++;
261 if (cnt > 100)
262 return -1;
263 }
264 return cnt;
265 }
266
267 static struct expression *get_partial_deref(struct expression *expr, int cnt)
268 {
269 while (--cnt >= 0) {
270 if (!expr || expr->type != EXPR_DEREF)
271 return expr;
272 expr = expr->deref;
273 if (expr->type == EXPR_PREOP && expr->op == '*')
274 expr = expr->unop;
275 }
276 return expr;
277 }
278
279 static int partial_deref_to_offset_str(struct expression *expr, int cnt, char op, char *buf, int size)
280 {
281 int n, offset;
282
283 if (cnt == 0)
284 return snprintf(buf, size, "%c0", op);
285
286 n = 0;
287 while (--cnt >= 0) {
288 offset = get_member_offset_from_deref(expr);
289 if (offset < 0)
290 return -1;
291 n += snprintf(buf + n, size - n, "%c%d", op, offset);
292 if (expr->type != EXPR_DEREF)
293 return -1;
294 expr = expr->deref;
295 if (expr->type == EXPR_PREOP && expr->op == '*')
296 expr = expr->unop;
297 }
298
299 return n;
300 }
301
302 static char *get_shared_str(struct expression *container, struct expression *expr)
303 {
304 struct expression *one, *two;
305 int cont, exp, min, ret, n;
306 static char buf[48];
307
308 cont = get_deref_count(container);
309 exp = get_deref_count(expr);
310 if (cont < 0 || exp < 0)
311 return NULL;
312
313 min = (cont < exp) ? cont : exp;
314 while (min >= 0) {
315 one = get_partial_deref(container, cont - min);
316 two = get_partial_deref(expr, exp - min);
317 if (expr_equiv(one, two))
318 goto found;
319 min--;
320 }
321
322 return NULL;
323
324 found:
325 ret = partial_deref_to_offset_str(container, cont - min, '-', buf, sizeof(buf));
326 if (ret < 0)
327 return NULL;
328 n = ret;
329 ret = partial_deref_to_offset_str(expr, exp - min, '+', buf + ret, sizeof(buf) - ret);
330 if (ret < 0)
331 return NULL;
332 n += ret;
333 if (n >= sizeof(buf))
334 return NULL;
335
336 return buf;
337 }
338
339 char *get_container_name(struct expression *container, struct expression *expr)
340 {
341 struct symbol *container_sym, *sym;
342 struct expression *tmp;
343 static char buf[64];
344 char *shared;
345 bool star;
346 int cnt;
347
348 container_sym = expr_to_sym(container);
349 sym = expr_to_sym(expr);
350 if (container_sym && container_sym == sym)
351 goto found;
352
353 cnt = 0;
354 while ((tmp = get_assigned_expr(expr))) {
355 expr = tmp;
356 if (cnt++ > 3)
357 break;
358 }
359
360 cnt = 0;
361 while ((tmp = get_assigned_expr(container))) {
362 container = tmp;
363 if (cnt++ > 3)
364 break;
365 }
366
367 found:
368 expr = strip_expr(expr);
369 star = true;
370 if (expr->type == EXPR_PREOP && expr->op == '&') {
371 expr = strip_expr(expr->unop);
372 star = false;
373 }
374
375 container_sym = expr_to_sym(container);
376 if (!container_sym)
377 return NULL;
378 sym = expr_to_sym(expr);
379 if (!sym || container_sym != sym)
380 return NULL;
381
382 shared = get_shared_str(container, expr);
383 if (star)
384 snprintf(buf, sizeof(buf), "*(%s)", shared);
385 else
386 snprintf(buf, sizeof(buf), "%s", shared);
387
388 return buf;
389 }
390
391 static void match_call(struct expression *call)
392 {
393 struct expression *fn, *arg;
394 char *name;
395 int param;
396
397 /*
398 * We're trying to link the function with the parameter. There are a
399 * couple ways this can be passed:
400 * foo->func(foo, ...);
401 * foo->func(foo->x, ...);
402 * foo->bar.func(&foo->bar, ...);
403 * foo->bar->baz->func(foo, ...);
404 *
405 * So the method is basically to subtract the offsets until we get to
406 * the common bit, then add the member offsets to get the parameter.
407 *
408 * If we're taking an address then the offset math is not stared,
409 * otherwise it is. Starred means dereferenced.
410 */
411 fn = strip_expr(call->fn);
412
413 param = -1;
414 FOR_EACH_PTR(call->args, arg) {
415 param++;
416
417 name = get_container_name(fn, arg);
418 if (!name)
419 continue;
420
421 sql_insert_caller_info(call, CONTAINER, param, name, "$(-1)");
422 } END_FOR_EACH_PTR(arg);
423 }
424
425 static void db_passed_container(const char *name, struct symbol *sym, char *key, char *value)
426 {
427 set_state(param_id, name, sym, alloc_state_str(key));
428 }
429
430 struct db_info {
431 struct symbol *arg;
432 int prev_offset;
433 struct range_list *rl;
434 int star;
435 struct stree *stree;
436 };
437
438 static struct symbol *get_member_from_offset(struct symbol *sym, int offset)
439 {
440 struct symbol *type, *tmp;
441 int cur;
442
443 type = get_real_base_type(sym);
444 if (!type || type->type != SYM_PTR)
445 return NULL;
446 type = get_real_base_type(type);
447 if (!type || type->type != SYM_STRUCT)
584 } else { /* presumably the parameter is a struct pointer */
585 run_sql(save_vals, &db_info,
586 "select offset, value from mtag_data where tag = %lld and type = %d;",
587 tag, DATA_VALUE);
588 }
589
590 if (db_info.prev_offset != -1)
591 set_param_value(&db_info.stree, arg, db_info.prev_offset, db_info.rl);
592
593 // FIXME: handle an offset correctly
594 if (!star && !arg_offset) {
595 sval_t sval;
596
597 sval.type = get_real_base_type(arg);
598 sval.uvalue = tag;
599 set_state_stree(&db_info.stree, SMATCH_EXTRA, arg->ident->name, arg, alloc_estate_sval(sval));
600 }
601 return db_info.stree;
602 }
603
604 static void load_container_data(struct symbol *arg, const char *info)
605 {
606 mtag_t cur_tag, container_tag, arg_tag;
607 int container_offset, arg_offset;
608 char *p = (char *)info;
609 struct sm_state *sm;
610 struct stree *stree;
611 bool star = 0;
612
613 if (p[0] == '*') {
614 star = 1;
615 p += 2;
616 }
617
618 if (!get_toplevel_mtag(cur_func_sym, &cur_tag))
619 return;
620
621 while (true) {
622 container_offset = strtoul(p, &p, 0);
623 if (local_debug)
624 sm_msg("%s: cur_tag = %llu container_offset = %d",
625 __func__, cur_tag, container_offset);
626 if (!mtag_map_select_container(cur_tag, container_offset, &container_tag))
627 return;
628 cur_tag = container_tag;
629 if (local_debug)
630 sm_msg("%s: container_tag = %llu p = '%s'",
631 __func__, container_tag, p);
632 if (!p)
633 return;
634 if (p[0] != '-')
635 break;
636 p++;
637 }
638
639 if (p[0] != '+')
640 return;
641
642 p++;
643 arg_offset = strtoul(p, &p, 0);
644 if (p && *p && *p != ')')
645 return;
646
647 if (!arg_offset || star) {
648 arg_tag = container_tag;
649 } else {
650 if (!mtag_map_select_tag(container_tag, -arg_offset, &arg_tag))
651 return;
652 }
653
654 stree = load_tag_info_sym(arg_tag, arg, arg_offset, star);
655 FOR_EACH_SM(stree, sm) {
656 set_state(sm->owner, sm->name, sm->sym, sm->state);
657 } END_FOR_EACH_SM(sm);
658 free_stree(&stree);
659 }
660
661 static void handle_passed_container(struct symbol *sym)
662 {
663 struct symbol *arg;
664 struct smatch_state *state;
665
666 FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
667 state = get_state(param_id, arg->ident->name, arg);
668 if (!state || state == &merged)
669 continue;
670 load_container_data(arg, state->name);
671 } END_FOR_EACH_PTR(arg);
672 }
673
674 void register_container_of(int id)
675 {
676 my_id = id;
677
678 add_hook(&match_function_def, FUNC_DEF_HOOK);
679
680 add_get_state_hook(&get_state_hook);
681
682 add_hook(&match_save_states, INLINE_FN_START);
683 add_hook(&match_restore_states, INLINE_FN_END);
684
685 select_return_implies_hook(CONTAINER, &set_param_used);
686 all_return_states_hook(&process_states);
687
688 add_split_return_callback(&print_returns_container_of);
689 select_return_states_hook(CONTAINER, &returns_container_of);
690
691 add_hook(&match_call, FUNCTION_CALL_HOOK);
692 }
693
694 void register_container_of2(int id)
695 {
696 param_id = id;
697
698 set_dynamic_states(param_id);
699 select_caller_info_hook(db_passed_container, CONTAINER);
700 add_merge_hook(param_id, &merge_str_state);
701 add_hook(&handle_passed_container, AFTER_DEF_HOOK);
702 }
703
|