Powered by Linux
Re: smatch and locking checks — Semantic Matching Tool

Re: smatch and locking checks

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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(&register_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 "&register_mutex".  I think there is a complication
	 * as well because sometimes it's like the assignment is:
	 *
	 *     scope = class_mutex_constructor(&register_mutex);
	 * but other times because we do a fake parameter assignement or
	 * something the assignment is more direct:
	 *     scope = &register_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, &param_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);
}

[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux