From: Dan Carpenter <dan.carpenter@linaro.org>
To: Rasmus Villemoes <linux@rasmusvillemoes.dk>
Cc: Dan Carpenter <error27@gmail.com>, smatch@vger.kernel.org
Subject: Re: smatch and locking checks
Date: Fri, 15 Mar 2024 13:46:29 +0300 [thread overview]
Message-ID: <e1db4969-549b-4a9d-afbb-356dbb0c4ace@moroto.mountain> (raw)
In-Reply-To: <ZfQkbGyHM4AjlFsm@moroto>
[-- Attachment #1: Type: text/plain, Size: 339 bytes --]
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
[-- Attachment #2: smatch_locking_info.c --]
[-- Type: text/x-csrc, Size: 31909 bytes --]
/*
* 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);
}
[-- Attachment #3: check_lock_type_held.c --]
[-- Type: text/x-csrc, Size: 2850 bytes --]
/*
* 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);
}
next prev parent reply other threads:[~2024-03-15 10:46 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <fac976aa-6172-4846-9fcb-c7564b291ed8@rasmusvillemoes.dk>
2024-03-15 10:35 ` smatch and locking checks Dan Carpenter
2024-03-15 10:46 ` Dan Carpenter [this message]
2024-03-15 10:56 ` Dan Carpenter
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=e1db4969-549b-4a9d-afbb-356dbb0c4ace@moroto.mountain \
--to=dan.carpenter@linaro.org \
--cc=error27@gmail.com \
--cc=linux@rasmusvillemoes.dk \
--cc=smatch@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).