/* * Copyright 2024 Linaro Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt */ #include "smatch.h" static int my_id; STATE(locked); static char *get_lock_member(struct expression *expr) { if (!expr) return NULL; if (expr->type != EXPR_PREOP || expr->op != '&') return NULL; expr = expr->unop; return get_member_name(expr); } static void match_lock(struct lock_info *info, struct expression *expr, const char *name) { char *member; member = get_lock_member(expr); if (local_debug) sm_msg("%s: expr='%s' member='%s' name='%s'", __func__, expr_to_str(expr), member, name); if (!member) return; set_state(my_id, member, NULL, &locked); } static void match_unlock(struct lock_info *info, struct expression *expr, const char *name) { char *member; member = get_lock_member(expr); if (!member) return; if (!get_state(my_id, member, NULL)) return; set_state(my_id, member, NULL, &undefined); } static void insert_caller_locked(struct expression *expr) { struct sm_state *sm; if (!has_states(__get_cur_stree(), my_id)) return; FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { if (sm->state != &locked) continue; sql_insert_caller_info(expr, TYPE_LOCKED, -2, sm->name, ""); } END_FOR_EACH_SM(sm); } void select_caller_locked(const char *name, struct symbol *sym, char *key, char *value) { set_state(my_id, key, NULL, &locked); } static void match_assert_held(struct expression *expr) { struct smatch_state *state; char *member; /* lock_is_held() is called like this "lock_is_held(&(lock)->dep_map)", * and we want to look at just "lock". */ if (expr->type != EXPR_PREOP || expr->op != '&') return; expr = expr->unop; if (expr->type != EXPR_DEREF) return; expr = strip_expr(expr->deref); member = get_member_name(expr); if (!member) return; state = get_state(my_id, member, NULL); if (state == &locked) return; sm_warning("lock '%s' might not be held", member); } void check_lock_held_type(int id) { my_id = id; add_lock_hook(&match_lock); add_unlock_hook(&match_unlock); select_caller_info_hook(select_caller_locked, TYPE_LOCKED); add_hook(&insert_caller_locked, FUNCTION_CALL_HOOK); add_param_key_expr_hook("lock_is_held", &match_assert_held, 0, "$", NULL); }