I started writing this code, but I ran into an issue where: lock = gen_expression_from_key(arg, key); doesnt' work. The arg is "&icom_port->uart_port" and the key is "&$->lock". The check_locking.c code does this as strings, but I want to do this as expressions... Anyway, here's the code that I have so far. regards, dan carpenter
/* * Copyright (C) 2009 Dan Carpenter. * Copyright (C) 2019 Oracle. * 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 <ctype.h> #include "parse.h" #include "smatch.h" #include "smatch_extra.h" #include "smatch_slist.h" static int my_id; STATE(lock); STATE(unlock); STATE(restore); #define NO_ARG -2 static void match_class_mutex_destructor(const char *fn, struct expression *expr, void *data); #define irq lock_irq #define sem lock_sem #define rcu lock_rcu #define rcu_read lock_rcu_read static struct lock_info lock_table[] = { {"spin_lock", LOCK, spin_lock, 0, "$"}, {"spin_unlock", UNLOCK, spin_lock, 0, "$"}, {"spin_lock_nested", LOCK, spin_lock, 0, "$"}, {"_spin_lock", LOCK, spin_lock, 0, "$"}, {"_spin_unlock", UNLOCK, spin_lock, 0, "$"}, {"_spin_lock_nested", LOCK, spin_lock, 0, "$"}, {"__spin_lock", LOCK, spin_lock, 0, "$"}, {"__spin_unlock", UNLOCK, spin_lock, 0, "$"}, {"__spin_lock_nested", LOCK, spin_lock, 0, "$"}, {"raw_spin_lock", LOCK, spin_lock, 0, "$"}, {"raw_spin_unlock", UNLOCK, spin_lock, 0, "$"}, {"_raw_spin_lock", LOCK, spin_lock, 0, "$"}, {"_raw_spin_lock_nested", LOCK, spin_lock, 0, "$"}, {"_raw_spin_unlock", UNLOCK, spin_lock, 0, "$"}, {"__raw_spin_lock", LOCK, spin_lock, 0, "$"}, {"__raw_spin_unlock", UNLOCK, spin_lock, 0, "$"}, {"spin_lock_irq", LOCK, spin_lock, 0, "$"}, {"spin_unlock_irq", UNLOCK, spin_lock, 0, "$"}, {"_spin_lock_irq", LOCK, spin_lock, 0, "$"}, {"_spin_unlock_irq", UNLOCK, spin_lock, 0, "$"}, {"__spin_lock_irq", LOCK, spin_lock, 0, "$"}, {"__spin_unlock_irq", UNLOCK, spin_lock, 0, "$"}, {"_raw_spin_lock_irq", LOCK, spin_lock, 0, "$"}, {"_raw_spin_unlock_irq", UNLOCK, spin_lock, 0, "$"}, {"__raw_spin_unlock_irq", UNLOCK, spin_lock, 0, "$"}, {"spin_lock_irqsave", LOCK, spin_lock, 0, "$"}, {"spin_unlock_irqrestore", UNLOCK, spin_lock, 0, "$"}, {"_spin_lock_irqsave", LOCK, spin_lock, 0, "$"}, {"_spin_unlock_irqrestore", UNLOCK, spin_lock, 0, "$"}, {"__spin_lock_irqsave", LOCK, spin_lock, 0, "$"}, {"__spin_unlock_irqrestore", UNLOCK, spin_lock, 0, "$"}, {"_raw_spin_lock_irqsave", LOCK, spin_lock, 0, "$"}, {"_raw_spin_unlock_irqrestore", UNLOCK, spin_lock, 0, "$"}, {"__raw_spin_lock_irqsave", LOCK, spin_lock, 0, "$"}, {"__raw_spin_unlock_irqrestore", UNLOCK, spin_lock, 0, "$"}, {"spin_lock_irqsave_nested", LOCK, spin_lock, 0, "$"}, {"_spin_lock_irqsave_nested", LOCK, spin_lock, 0, "$"}, {"__spin_lock_irqsave_nested", LOCK, spin_lock, 0, "$"}, {"_raw_spin_lock_irqsave_nested", LOCK, spin_lock, 0, "$"}, {"spin_lock_bh", LOCK, spin_lock, 0, "$"}, {"spin_unlock_bh", UNLOCK, spin_lock, 0, "$"}, {"_spin_lock_bh", LOCK, spin_lock, 0, "$"}, {"_spin_unlock_bh", UNLOCK, spin_lock, 0, "$"}, {"__spin_lock_bh", LOCK, spin_lock, 0, "$"}, {"__spin_unlock_bh", UNLOCK, spin_lock, 0, "$"}, {"spin_trylock", LOCK, spin_lock, 0, "$", &int_one, &int_one}, {"_spin_trylock", LOCK, spin_lock, 0, "$", &int_one, &int_one}, {"__spin_trylock", LOCK, spin_lock, 0, "$", &int_one, &int_one}, {"raw_spin_trylock", LOCK, spin_lock, 0, "$", &int_one, &int_one}, {"_raw_spin_trylock", LOCK, spin_lock, 0, "$", &int_one, &int_one}, {"spin_trylock_irq", LOCK, spin_lock, 0, "$", &int_one, &int_one}, {"spin_trylock_irqsave", LOCK, spin_lock, 0, "$", &int_one, &int_one}, {"spin_trylock_bh", LOCK, spin_lock, 0, "$", &int_one, &int_one}, {"_spin_trylock_bh", LOCK, spin_lock, 0, "$", &int_one, &int_one}, {"__spin_trylock_bh", LOCK, spin_lock, 0, "$", &int_one, &int_one}, {"__raw_spin_trylock", LOCK, spin_lock, 0, "$", &int_one, &int_one}, {"_atomic_dec_and_lock", LOCK, spin_lock, 1, "$", &int_one, &int_one}, {"read_lock", LOCK, read_lock, 0, "$"}, {"down_read", LOCK, read_lock, 0, "$"}, {"down_read_nested", LOCK, read_lock, 0, "$"}, {"down_read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one}, {"down_read_killable", LOCK, read_lock, 0, "$", &int_zero, &int_zero}, {"up_read", UNLOCK, read_lock, 0, "$"}, {"read_unlock", UNLOCK, read_lock, 0, "$"}, {"_read_lock", LOCK, read_lock, 0, "$"}, {"_read_unlock", UNLOCK, read_lock, 0, "$"}, {"__read_lock", LOCK, read_lock, 0, "$"}, {"__read_unlock", UNLOCK, read_lock, 0, "$"}, {"_raw_read_lock", LOCK, read_lock, 0, "$"}, {"_raw_read_unlock", UNLOCK, read_lock, 0, "$"}, {"__raw_read_lock", LOCK, read_lock, 0, "$"}, {"__raw_read_unlock", UNLOCK, read_lock, 0, "$"}, {"read_lock_irq", LOCK, read_lock, 0, "$"}, {"read_unlock_irq" , UNLOCK, read_lock, 0, "$"}, {"_read_lock_irq", LOCK, read_lock, 0, "$"}, {"_read_unlock_irq", UNLOCK, read_lock, 0, "$"}, {"__read_lock_irq", LOCK, read_lock, 0, "$"}, {"__read_unlock_irq", UNLOCK, read_lock, 0, "$"}, {"_raw_read_unlock_irq", UNLOCK, read_lock, 0, "$"}, {"_raw_read_lock_irq", LOCK, read_lock, 0, "$"}, {"_raw_read_lock_bh", LOCK, read_lock, 0, "$"}, {"_raw_read_unlock_bh", UNLOCK, read_lock, 0, "$"}, {"read_lock_irqsave", LOCK, read_lock, 0, "$"}, {"read_unlock_irqrestore", UNLOCK, read_lock, 0, "$"}, {"_read_lock_irqsave", LOCK, read_lock, 0, "$"}, {"_read_unlock_irqrestore", UNLOCK, read_lock, 0, "$"}, {"__read_lock_irqsave", LOCK, read_lock, 0, "$"}, {"__read_unlock_irqrestore", UNLOCK, read_lock, 0, "$"}, {"read_lock_bh", LOCK, read_lock, 0, "$"}, {"read_unlock_bh", UNLOCK, read_lock, 0, "$"}, {"_read_lock_bh", LOCK, read_lock, 0, "$"}, {"_read_unlock_bh", UNLOCK, read_lock, 0, "$"}, {"__read_lock_bh", LOCK, read_lock, 0, "$"}, {"__read_unlock_bh", UNLOCK, read_lock, 0, "$"}, {"__raw_read_lock_bh", LOCK, read_lock, 0, "$"}, {"__raw_read_unlock_bh", UNLOCK, read_lock, 0, "$"}, {"_raw_read_lock_irqsave", LOCK, read_lock, 0, "$"}, {"_raw_read_lock_irqsave", LOCK, irq, -1, "$"}, {"_raw_read_unlock_irqrestore", UNLOCK, read_lock, 0, "$"}, {"_raw_read_unlock_irqrestore", RESTORE, irq, 1, "$"}, {"_raw_spin_lock_bh", LOCK, read_lock, 0, "$"}, {"_raw_spin_lock_bh", LOCK, bottom_half, NO_ARG, "bh"}, {"_raw_spin_lock_nest_lock", LOCK, read_lock, 0, "$"}, {"_raw_spin_unlock_bh", UNLOCK, read_lock, 0, "$"}, {"_raw_spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"}, {"_raw_write_lock_irqsave", LOCK, write_lock, 0, "$"}, {"_raw_write_lock_irqsave", LOCK, irq, -1, "$"}, {"_raw_write_unlock_irqrestore", UNLOCK, write_lock, 0, "$"}, {"_raw_write_unlock_irqrestore", RESTORE, irq, 1, "$"}, {"__raw_write_unlock_irq", UNLOCK, write_lock, 0, "$"}, {"__raw_write_unlock_irq", UNLOCK, irq, 0, "irq"}, {"__raw_write_unlock_irqrestore", UNLOCK, write_lock, 0, "$"}, {"__raw_write_unlock_irqrestore", RESTORE, irq, 1, "$"}, {"generic__raw_read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one}, {"read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one}, {"_read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one}, {"raw_read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one}, {"_raw_read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one}, {"__raw_read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one}, {"__read_trylock", LOCK, read_lock, 0, "$", &int_one, &int_one}, {"write_lock", LOCK, write_lock, 0, "$"}, {"down_write", LOCK, write_lock, 0, "$"}, {"down_write_nested", LOCK, write_lock, 0, "$"}, {"up_write", UNLOCK, write_lock, 0, "$"}, {"write_unlock", UNLOCK, write_lock, 0, "$"}, {"_write_lock", LOCK, write_lock, 0, "$"}, {"_write_unlock", UNLOCK, write_lock, 0, "$"}, {"__write_lock", LOCK, write_lock, 0, "$"}, {"__write_unlock", UNLOCK, write_lock, 0, "$"}, {"write_lock_irq", LOCK, write_lock, 0, "$"}, {"write_unlock_irq", UNLOCK, write_lock, 0, "$"}, {"_write_lock_irq", LOCK, write_lock, 0, "$"}, {"_write_unlock_irq", UNLOCK, write_lock, 0, "$"}, {"__write_lock_irq", LOCK, write_lock, 0, "$"}, {"__write_unlock_irq", UNLOCK, write_lock, 0, "$"}, {"_raw_write_unlock_irq", UNLOCK, write_lock, 0, "$"}, {"write_lock_irqsave", LOCK, write_lock, 0, "$"}, {"write_unlock_irqrestore", UNLOCK, write_lock, 0, "$"}, {"_write_lock_irqsave", LOCK, write_lock, 0, "$"}, {"_write_unlock_irqrestore", UNLOCK, write_lock, 0, "$"}, {"__write_lock_irqsave", LOCK, write_lock, 0, "$"}, {"__write_unlock_irqrestore", UNLOCK, write_lock, 0, "$"}, {"write_lock_bh", LOCK, write_lock, 0, "$"}, {"write_unlock_bh", UNLOCK, write_lock, 0, "$"}, {"_write_lock_bh", LOCK, write_lock, 0, "$"}, {"_write_unlock_bh", UNLOCK, write_lock, 0, "$"}, {"__write_lock_bh", LOCK, write_lock, 0, "$"}, {"__write_unlock_bh", UNLOCK, write_lock, 0, "$"}, {"_raw_write_lock", LOCK, write_lock, 0, "$"}, {"__raw_write_lock", LOCK, write_lock, 0, "$"}, {"_raw_write_unlock", UNLOCK, write_lock, 0, "$"}, {"__raw_write_unlock", UNLOCK, write_lock, 0, "$"}, {"_raw_write_lock_bh", LOCK, write_lock, 0, "$"}, {"_raw_write_unlock_bh", UNLOCK, write_lock, 0, "$"}, {"_raw_write_lock_irq", LOCK, write_lock, 0, "$"}, {"write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one}, {"_write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one}, {"raw_write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one}, {"_raw_write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one}, {"__write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one}, {"__raw_write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one}, {"down_write_trylock", LOCK, write_lock, 0, "$", &int_one, &int_one}, {"down_write_killable", LOCK, write_lock, 0, "$", &int_zero, &int_zero}, {"down", LOCK, sem, 0, "$"}, {"up", UNLOCK, sem, 0, "$"}, {"down_trylock", LOCK, sem, 0, "$", &int_zero, &int_zero}, {"down_timeout", LOCK, sem, 0, "$", &int_zero, &int_zero}, {"down_interruptible", LOCK, sem, 0, "$", &int_zero, &int_zero}, {"down_killable", LOCK, sem, 0, "$", &int_zero, &int_zero}, {"mutex_lock", LOCK, mutex, 0, "$"}, {"mutex_unlock", UNLOCK, mutex, 0, "$"}, {"mutex_destroy", RESTORE, mutex, 0, "$"}, {"mutex_lock_nested", LOCK, mutex, 0, "$"}, {"mutex_lock_io", LOCK, mutex, 0, "$"}, {"mutex_lock_io_nested", LOCK, mutex, 0, "$"}, {"mutex_lock_interruptible", LOCK, mutex, 0, "$", &int_zero, &int_zero}, {"mutex_lock_interruptible_nested", LOCK, mutex, 0, "$", &int_zero, &int_zero}, {"mutex_lock_killable", LOCK, mutex, 0, "$", &int_zero, &int_zero}, {"mutex_lock_killable_nested", LOCK, mutex, 0, "$", &int_zero, &int_zero}, {"mutex_trylock", LOCK, mutex, 0, "$", &int_one, &int_one}, {"ww_mutex_lock", LOCK, mutex, 0, "$"}, {"__ww_mutex_lock", LOCK, mutex, 0, "$"}, {"ww_mutex_lock_interruptible", LOCK, mutex, 0, "$", &int_zero, &int_zero}, {"ww_mutex_unlock", UNLOCK, mutex, 0, "$"}, {"raw_local_irq_disable", LOCK, irq, NO_ARG, "irq"}, {"raw_local_irq_enable", UNLOCK, irq, NO_ARG, "irq"}, {"spin_lock_irq", LOCK, irq, NO_ARG, "irq"}, {"spin_unlock_irq", UNLOCK, irq, NO_ARG, "irq"}, {"_spin_lock_irq", LOCK, irq, NO_ARG, "irq"}, {"_spin_unlock_irq", UNLOCK, irq, NO_ARG, "irq"}, {"__spin_lock_irq", LOCK, irq, NO_ARG, "irq"}, {"__spin_unlock_irq", UNLOCK, irq, NO_ARG, "irq"}, {"_raw_spin_lock_irq", LOCK, irq, NO_ARG, "irq"}, {"_raw_spin_unlock_irq", UNLOCK, irq, NO_ARG, "irq"}, {"__raw_spin_unlock_irq", UNLOCK, irq, NO_ARG, "irq"}, {"spin_trylock_irq", LOCK, irq, NO_ARG, "irq", &int_one, &int_one}, {"read_lock_irq", LOCK, irq, NO_ARG, "irq"}, {"read_unlock_irq", UNLOCK, irq, NO_ARG, "irq"}, {"_read_lock_irq", LOCK, irq, NO_ARG, "irq"}, {"_read_unlock_irq", UNLOCK, irq, NO_ARG, "irq"}, {"__read_lock_irq", LOCK, irq, NO_ARG, "irq"}, {"_raw_read_lock_irq", LOCK, irq, NO_ARG, "irq"}, {"__read_unlock_irq", UNLOCK, irq, NO_ARG, "irq"}, {"_raw_read_unlock_irq", UNLOCK, irq, NO_ARG, "irq"}, {"write_lock_irq", LOCK, irq, NO_ARG, "irq"}, {"write_unlock_irq", UNLOCK, irq, NO_ARG, "irq"}, {"_write_lock_irq", LOCK, irq, NO_ARG, "irq"}, {"_write_unlock_irq", UNLOCK, irq, NO_ARG, "irq"}, {"__write_lock_irq", LOCK, irq, NO_ARG, "irq"}, {"__write_unlock_irq", UNLOCK, irq, NO_ARG, "irq"}, {"_raw_write_lock_irq", LOCK, irq, NO_ARG, "irq"}, {"_raw_write_unlock_irq", UNLOCK, irq, NO_ARG, "irq"}, {"arch_local_irq_save", LOCK, irq, -1, "$"}, {"arch_local_irq_restore", RESTORE, irq, 0, "$"}, {"__raw_local_irq_save", LOCK, irq, -1, "$"}, {"raw_local_irq_restore", RESTORE, irq, 0, "$"}, {"spin_lock_irqsave_nested", LOCK, irq, -1, "$"}, {"spin_lock_irqsave", LOCK, irq, 1, "$"}, {"spin_unlock_irqrestore", RESTORE, irq, 1, "$"}, {"_spin_lock_irqsave_nested", LOCK, irq, -1, "$"}, {"_spin_lock_irqsave", LOCK, irq, -1, "$"}, {"_spin_lock_irqsave", LOCK, irq, 1, "$"}, {"_spin_unlock_irqrestore", RESTORE, irq, 1, "$"}, {"__spin_lock_irqsave_nested", LOCK, irq, 1, "$"}, {"__spin_lock_irqsave", LOCK, irq, 1, "$"}, {"__spin_unlock_irqrestore", RESTORE, irq, 1, "$"}, {"_raw_spin_lock_irqsave", LOCK, irq, -1, "$"}, {"_raw_spin_lock_irqsave", LOCK, irq, 1, "$"}, {"_raw_spin_unlock_irqrestore", RESTORE, irq, 1, "$"}, {"__raw_spin_lock_irqsave", LOCK, irq, -1, "$"}, {"__raw_spin_unlock_irqrestore", RESTORE, irq, 1, "$"}, {"_raw_spin_lock_irqsave_nested", LOCK, irq, -1, "$"}, {"spin_trylock_irqsave", LOCK, irq, 1, "$", &int_one, &int_one}, {"read_lock_irqsave", LOCK, irq, -1, "$"}, {"read_lock_irqsave", LOCK, irq, 1, "$"}, {"read_unlock_irqrestore", RESTORE, irq, 1, "$"}, {"_read_lock_irqsave", LOCK, irq, -1, "$"}, {"_read_lock_irqsave", LOCK, irq, 1, "$"}, {"_read_unlock_irqrestore", RESTORE, irq, 1, "$"}, {"__read_lock_irqsave", LOCK, irq, -1, "$"}, {"__read_unlock_irqrestore", RESTORE, irq, 1, "$"}, {"write_lock_irqsave", LOCK, irq, -1, "$"}, {"write_lock_irqsave", LOCK, irq, 1, "$"}, {"write_unlock_irqrestore", RESTORE, irq, 1, "$"}, {"_write_lock_irqsave", LOCK, irq, -1, "$"}, {"_write_lock_irqsave", LOCK, irq, 1, "$"}, {"_write_unlock_irqrestore", RESTORE, irq, 1, "$"}, {"__write_lock_irqsave", LOCK, irq, -1, "$"}, {"__write_unlock_irqrestore", RESTORE, irq, 1, "$"}, {"local_bh_disable", LOCK, bottom_half, NO_ARG, "bh"}, {"_local_bh_disable", LOCK, bottom_half, NO_ARG, "bh"}, {"__local_bh_disable", LOCK, bottom_half, NO_ARG, "bh"}, {"local_bh_enable", UNLOCK, bottom_half, NO_ARG, "bh"}, {"_local_bh_enable", UNLOCK, bottom_half, NO_ARG, "bh"}, {"__local_bh_enable", UNLOCK, bottom_half, NO_ARG, "bh"}, {"spin_lock_bh", LOCK, bottom_half, NO_ARG, "bh"}, {"spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"}, {"_spin_lock_bh", LOCK, bottom_half, NO_ARG, "bh"}, {"_spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"}, {"__spin_lock_bh", LOCK, bottom_half, NO_ARG, "bh"}, {"__spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"}, {"read_lock_bh", LOCK, bottom_half, NO_ARG, "bh"}, {"read_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"}, {"_read_lock_bh", LOCK, bottom_half, NO_ARG, "bh"}, {"_read_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"}, {"__read_lock_bh", LOCK, bottom_half, NO_ARG, "bh"}, {"__read_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"}, {"_raw_read_lock_bh", LOCK, bottom_half, NO_ARG, "bh"}, {"_raw_read_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"}, {"write_lock_bh", LOCK, bottom_half, NO_ARG, "bh"}, {"write_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"}, {"_write_lock_bh", LOCK, bottom_half, NO_ARG, "bh"}, {"_write_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"}, {"__write_lock_bh", LOCK, bottom_half, NO_ARG, "bh"}, {"__write_unlock_bh", UNLOCK, bottom_half, NO_ARG, "bh"}, {"_raw_write_lock_bh", LOCK, bottom_half, NO_ARG, "bh"}, {"_raw_write_unlock_bh",UNLOCK, bottom_half, NO_ARG, "bh"}, {"spin_trylock_bh", LOCK, bottom_half, NO_ARG, "bh", &int_one, &int_one}, {"_spin_trylock_bh", LOCK, bottom_half, NO_ARG, "bh", &int_one, &int_one}, {"__spin_trylock_bh", LOCK, bottom_half, NO_ARG, "bh", &int_one, &int_one}, {"ffs_mutex_lock", LOCK, mutex, 0, "$", &int_zero, &int_zero}, {"clk_prepare_lock", LOCK, prepare_lock, NO_ARG, "clk"}, {"clk_prepare_unlock", UNLOCK, prepare_lock, NO_ARG, "clk"}, {"clk_enable_lock", LOCK, enable_lock, -1, "$"}, {"clk_enable_unlock", UNLOCK, enable_lock, 0, "$"}, {"dma_resv_lock", LOCK, mutex, 0, "$", &int_zero, &int_zero}, {"dma_resv_trylock", LOCK, mutex, 0, "$", &int_one, &int_one}, {"dma_resv_lock_interruptible", LOCK, mutex, 0, "$", &int_zero, &int_zero}, {"dma_resv_unlock", UNLOCK, mutex, 0, "$"}, {"modeset_lock", LOCK, mutex, 0, "$", &int_zero, &int_zero}, {"drm_ modeset_lock", LOCK, mutex, 0, "$", &int_zero, &int_zero}, {"drm_modeset_lock_single_interruptible", LOCK, mutex, 0, "$", &int_zero, &int_zero}, {"drm_exec_unlock_obj", UNLOCK, mutex, 1, "$->resv" }, {"modeset_unlock", UNLOCK, mutex, 0, "$"}, // {"nvkm_i2c_aux_acquire", LOCK, mutex, {"i915_gem_object_lock_interruptible", LOCK, mutex, 0, "$->base.resv", &int_zero, &int_zero}, {"i915_gem_object_lock", LOCK, mutex, 0, "$->base.resv"}, {"msm_gem_lock", LOCK, mutex, 0, "$->resv"}, {"reiserfs_write_lock_nested", LOCK, mutex, 0, "$"}, {"reiserfs_write_unlock_nested", UNLOCK, mutex, 0, "$"}, {"rw_lock", LOCK, write_lock, 1, "$"}, {"rw_unlock", UNLOCK, write_lock, 1, "$"}, {"sem_lock", LOCK, mutex, 0, "$"}, {"sem_unlock", UNLOCK, mutex, 0, "$"}, {"rcu_lock_acquire", LOCK, rcu, NO_ARG, "rcu"}, {"rcu_lock_release", UNLOCK, rcu, NO_ARG, "rcu"}, {"rcu_read_lock", LOCK, rcu_read, NO_ARG, "rcu_read"}, {"rcu_read_unlock", UNLOCK, rcu_read, NO_ARG, "rcu_read"}, {"rcu_read_lock_bh", LOCK, rcu_read, NO_ARG, "rcu_read"}, {"rcu_read_unlock_bh", UNLOCK, rcu_read, NO_ARG, "rcu_read"}, {"rcu_read_lock_sched", LOCK, rcu_read, NO_ARG, "rcu_read"}, {"rcu_read_lock_sched_notrace", LOCK, rcu_read, NO_ARG, "rcu_read"}, {"rcu_read_unlock_sched", UNLOCK, rcu_read, NO_ARG, "rcu_read"}, {"rcu_read_unlock_sched_notrace", UNLOCK, rcu_read, NO_ARG, "rcu_read"}, {"gfs2_trans_begin", LOCK, sem, 0, "&$->sd_log_flush_lock", &int_zero, &int_zero}, {"lock_sock", LOCK, spin_lock, 0, "&$->sk_lock.slock"}, {"lock_sock_nested", LOCK, spin_lock, 0, "&$->sk_lock.slock"}, {"lock_sock_fast", LOCK, spin_lock, 0, "&$->sk_lock.slock"}, {"__lock_sock", LOCK, spin_lock, 0, "&$->sk_lock.slock"}, {"release_sock", UNLOCK, spin_lock, 0, "&$->sk_lock.slock"}, {"__release_sock", UNLOCK, spin_lock, 0, "&$->sk_lock.slock"}, {"lock_task_sighand", LOCK, spin_lock, 0, "&$->sighand->siglock", &valid_ptr_min_sval, &valid_ptr_max_sval}, {"rcu_nocb_unlock_irqrestore", RESTORE, spin_lock, 0, "&$->nocb_lock"}, {"rcu_nocb_unlock_irqrestore", RESTORE, irq, 1, "$" }, {"bch_write_bdev_super", IGNORE_LOCK, sem, 0, "&$->sb_write_mutex"}, {"bcache_write_super", IGNORE_LOCK, sem, 0, "&$->set->sb_write_mutex"}, {"uuid_io", IGNORE_LOCK, sem, 0, "&$->uuid_write_mutex" }, {"dlfb_set_video_mode", IGNORE_LOCK, sem, 0, "&$->urbs.limit_sem"}, {"efx_rwsem_assert_write_locked", IGNORE_LOCK, sem, 0, "&"}, // The i915_gem_ww_ctx_unlock_all() is too complicated {"i915_gem_object_pin_pages_unlocked", IGNORE_LOCK, mutex, 0, "$->base.resv"}, {"i915_gem_object_pin_map_unlocked", IGNORE_LOCK, mutex, 0, "$->base.resv"}, {"i915_gem_object_fill_blt", IGNORE_LOCK, mutex, 0, "$->base.resv"}, {"i915_vma_pin", IGNORE_LOCK, mutex, 0, "$->base.resv"}, { "perf_event_period", IGNORE_LOCK, mutex, 0, "&$->ctx->mutex"}, { "perf_event_enable", IGNORE_LOCK, mutex, 0, "&$->ctx->mutex"}, { "qede_load", IGNORE_LOCK, mutex, 0, "&$->qede_lock" }, { "qede_unload", IGNORE_LOCK, mutex, 0, "&$->qede_lock" }, { "deactivate_locked_super", UNLOCK, spin_lock, 0, "&$->s_umount"}, { "ext4_lock_group", LOCK, spin_lock, 0, "$"}, { "ext4_unlock_group", UNLOCK, spin_lock, 0, "$"}, {"__pte_offset_map_lock", LOCK, spin_lock, 3, "*$", &valid_ptr_min_sval, &valid_ptr_max_sval}, {"pte_offset_map_lock", LOCK, spin_lock, 3, "*$", &valid_ptr_min_sval, &valid_ptr_max_sval}, {"uart_unlock_and_check_sysrq_irqrestore", UNLOCK, spin_lock, 0, "&$->lock"}, {"mt7530_mutex_lock", LOCK, mutex, 0, "&$->bus->mdio_lock"}, {"mt7530_mutex_unlock", UNLOCK, mutex, 0, "&$->bus->mdio_lock"}, {"class_mutex_destructor", UNLOCK, mutex, 0, "*$", NULL, NULL, &match_class_mutex_destructor}, {"class_rwsem_write_destructor", UNLOCK, sem, 0, "*$", NULL, NULL, &match_class_mutex_destructor}, {}, }; static struct locking_hook_list *lock_hooks, *unlock_hooks, *restore_hooks; void add_lock_hook(locking_hook *hook) { add_ptr_list(&lock_hooks, hook); } void add_unlock_hook(locking_hook *hook) { add_ptr_list(&unlock_hooks, hook); } void add_restore_hook(locking_hook *hook) { add_ptr_list(&restore_hooks, hook); } static void call_locking_hooks(struct locking_hook_list *list, struct lock_info *info, struct expression *expr, const char *name) { locking_hook *hook; FOR_EACH_PTR(list, hook) { (hook)(info, expr, name); } END_FOR_EACH_PTR(hook); } static void update_state(struct expression *expr, const char *name, struct smatch_state *state) { if (expr) { if (get_state_expr(my_id, expr)) { set_state_expr(my_id, expr, &undefined); return; } set_state_expr(my_id, expr, state); return; } else { if (get_state(my_id, name, NULL)) { set_state(my_id, name, NULL, &undefined); return; } set_state(my_id, name, NULL, state); } } static void do_lock(struct lock_info *info, struct expression *expr, const char *name) { if (local_debug) sm_msg("%s: expr='%s'", __func__, expr_to_str(expr)); call_locking_hooks(lock_hooks, info, expr, name); update_state(expr, name, &lock); } static void do_unlock(struct lock_info *info, struct expression *expr, const char *name) { call_locking_hooks(unlock_hooks, info, expr, name); update_state(expr, name, &unlock); } static void do_restore(struct lock_info *info, struct expression *expr, const char *name) { call_locking_hooks(restore_hooks, info, expr, name); update_state(expr, name, &restore); } static void match_class_mutex_destructor(const char *fn, struct expression *expr, void *data) { struct expression *arg, *lock; /* * What happens here is that the code looks like: * class_mutex_t scope __attribute__((__cleanup__(class_mutex_destructor))) = * class_mutex_constructor(®ister_mutex); * Then here in this hook functions expr is set to * "class_mutex_destructor(&scope)". So we need to take "&scope" and * trace it back to "®ister_mutex". I think there is a complication * as well because sometimes it's like the assignment is: * * scope = class_mutex_constructor(®ister_mutex); * but other times because we do a fake parameter assignement or * something the assignment is more direct: * scope = ®ister_mutex; * */ if (!expr || expr->type != EXPR_CALL) return; arg = get_argument_from_call_expr(expr->args, 0); arg = strip_expr(arg); if (!arg || arg->type != EXPR_PREOP || arg->op != '&') return; arg = strip_expr(arg->unop); lock = get_assigned_expr(arg); if (!lock) return; if (lock->type == EXPR_CALL) lock = get_argument_from_call_expr(lock->args, 0); lock = strip_expr(lock); if (!lock || lock->type != EXPR_PREOP || lock->op != '&') return; do_unlock(NULL, lock, NULL); } static struct expression *remove_spinlock_check(struct expression *expr) { if (expr->type != EXPR_CALL) return expr; if (expr->fn->type != EXPR_SYMBOL) return expr; if (strcmp(expr->fn->symbol_name->name, "spinlock_check")) return expr; expr = get_argument_from_call_expr(expr->args, 0); return expr; } static struct expression *filter_kernel_args(struct expression *arg) { if (arg->type == EXPR_PREOP && arg->op == '&') return strip_expr(arg->unop); if (!is_pointer(arg)) return arg; return deref_expression(strip_expr(arg)); } static bool func_in_lock_table(struct expression *expr) { struct symbol *sym; int i; if (expr->type != EXPR_SYMBOL) return false; sym = expr->symbol; if (!sym || !sym->ident) return false; for (i = 0; lock_table[i].function != NULL; i++) { if (strcmp(lock_table[i].function, sym->ident->name) == 0) return true; } return false; } static void db_param_locked_unlocked(struct expression *expr, int param, const char *key, int lock_unlock, struct lock_info *info) { struct expression *call, *arg, *lock = NULL; char *name = NULL; if (info && info->action == IGNORE_LOCK) return; call = expr; while (call->type == EXPR_ASSIGNMENT) call = strip_expr(call->right); if (call->type != EXPR_CALL) return; if (!info && func_in_lock_table(call->fn)) return; if (param == -2) { name = alloc_string(info->key); } else if (param == -1) { lock = gen_expr_from_param_key(expr, param, key); if (local_debug) sm_msg("%s: expr='%s' param=%d key='%s' lock='%s'", __func__, expr_to_str(expr), param, key, expr_to_str(lock)); } else { arg = get_argument_from_call_expr(call->args, param); if (!arg) return; lock = gen_expression_from_key(arg, key); if (local_debug) sm_msg("%s: arg='%s' key='%s' lock='%s'", __func__, expr_to_str(arg), key, expr_to_str(lock)); } if (lock_unlock == LOCK) do_lock(info, lock, name); else if (lock_unlock == UNLOCK) do_unlock(info, lock, name); else if (lock_unlock == RESTORE) do_restore(info, lock, name); } static void db_param_locked(struct expression *expr, int param, char *key, char *value) { db_param_locked_unlocked(expr, param, key, LOCK, NULL); } static void db_param_unlocked(struct expression *expr, int param, char *key, char *value) { db_param_locked_unlocked(expr, param, key, UNLOCK, NULL); } static void db_param_restore(struct expression *expr, int param, char *key, char *value) { db_param_locked_unlocked(expr, param, key, RESTORE, NULL); } static void match_lock_unlock(const char *fn, struct expression *expr, void *data) { struct lock_info *info = data; struct expression *parent; if (info->arg == -1) { parent = expr_get_parent_expr(expr); while (parent && parent->type != EXPR_ASSIGNMENT) parent = expr_get_parent_expr(parent); if (!parent || parent->type != EXPR_ASSIGNMENT) return; expr = parent; } db_param_locked_unlocked(expr, info->arg, info->key, info->action, info); } static void match_lock_held(const char *fn, struct expression *call_expr, struct expression *assign_expr, void *data) { struct lock_info *info = data; db_param_locked_unlocked(assign_expr ?: call_expr, info->arg, info->key, info->action, info); } static int get_db_type(struct sm_state *sm) { if (sm->state == &lock) return LOCK2; if (sm->state == &unlock) return UNLOCK2; if (sm->state == &restore) return RESTORE2; return -1; } static void match_return_info(int return_id, char *return_ranges, struct expression *expr) { const char *param_name; struct sm_state *sm; int param, type; FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { type = get_db_type(sm); if (type == -1) continue; param = get_param_key_from_sm(sm, expr, ¶m_name); sql_insert_return_states(return_id, return_ranges, type, param, param_name, ""); } END_FOR_EACH_SM(sm); } static void match_dma_resv_lock_NULL(const char *fn, struct expression *call_expr, struct expression *assign_expr, void *_index) { struct expression *lock, *ctx; lock = get_argument_from_call_expr(call_expr->args, 0); ctx = get_argument_from_call_expr(call_expr->args, 1); if (!expr_is_zero(ctx)) return; lock = remove_spinlock_check(lock); lock = filter_kernel_args(lock); do_lock(NULL, lock, NULL); } static void load_table(struct lock_info *lock_table) { int i; for (i = 0; lock_table[i].function != NULL; i++) { struct lock_info *lock = &lock_table[i]; if (lock->call_back) { add_function_hook(lock->function, lock->call_back, lock); } else if (lock->implies_start) { return_implies_state_sval(lock->function, *lock->implies_start, *lock->implies_end, &match_lock_held, lock); } else { add_function_hook(lock->function, &match_lock_unlock, lock); } } } static bool is_smp_config(void) { struct ident *id; id = built_in_ident("CONFIG_SMP"); return !!lookup_symbol(id, NS_MACRO); } void register_locking_info(int id) { my_id = id; locking_id = id; if (option_project != PROJ_KERNEL) return; if (!is_smp_config()) return; load_table(lock_table); set_dynamic_states(my_id); select_return_states_hook(LOCK2, &db_param_locked); select_return_states_hook(UNLOCK2, &db_param_unlocked); select_return_states_hook(RESTORE2, &db_param_restore); add_split_return_callback(match_return_info); // FIXME why is this not handled in load table? return_implies_state("dma_resv_lock", -4095, -1, &match_dma_resv_lock_NULL, 0); }
/* * 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); }