* Re: smatch and locking checks
[not found] <fac976aa-6172-4846-9fcb-c7564b291ed8@rasmusvillemoes.dk>
@ 2024-03-15 10:35 ` Dan Carpenter
2024-03-15 10:46 ` Dan Carpenter
0 siblings, 1 reply; 3+ messages in thread
From: Dan Carpenter @ 2024-03-15 10:35 UTC (permalink / raw)
To: Rasmus Villemoes; +Cc: Dan Carpenter, smatch
[ Heh. I asked Rasmus if I could add the list the the CC but I forgot
to do that. Let me resend - dan ]
Hi Rasmus,
On Mon, Mar 11, 2024 at 01:18:35PM +0100, Rasmus Villemoes wrote:
> Hi Dan
>
> It's been a million years since I had time to work with smatch. I do
> vaguely remember that it can do some lock sanity checks, but I don't
> know how much it can do, or if the code needs some special markup to
> help smatch.
As a rule, I'd prefer to avoid markup. These days we prefer to put
stuff into tables like in check_preempt_info.c and check_unwind.c. Use
that along with the cross function database instead of markup.
>
> A colleague of mine hit the lockdep_assert_held_once() in
> uart_handle_cts_change(). The driver in question is mxs-auart.c, which
> sure enough calls uart_handle_cts_change() from mxs_auart_irq_handle()
> [both directly and via the mxs_auart_modem_status() helper] without
> holding that lock.
>
> The question is, can smatch read lockdep annotations, and if so, can it
> also know that on entry to an irq handler, no locks are held?
Smatch doesn't read lockdep annotations. It would be interesting to
check for lockdep assert failures.
Smatch does tracking locking information in the DB. The cross function
DB is explained in this blog. (I had thought I added it to the
Documentation/ directory but apparently not).
https://staticthinking.wordpress.com/2023/05/02/the-cross-function-db/
Below is the output from:
smdb.py uart_handle_cts_change | egrep '(INTERNAL|LOCK)'
INTERNAL means the start of a new call. As you can see, to Smatch it
looks like only three of the callers are holding &uport->lock. The
problem is that &icom_port->uart_port.lock and &u->lock are probably
the lock but Smatch isn't clever enough to map those names correctly.
For serial8250_modem_status() it knows that the callers are each holding
a lock but neither of them are mapped correctly.
drivers/tty/serial/8250/8250_port.c | serial8250_modem_status | uart_handle_cts_change | HALF_LOCKED | -2 | &port->lock |
drivers/tty/serial/8250/8250_port.c | serial8250_modem_status | uart_handle_cts_change | HALF_LOCKED | -2 | &up->port.lock |
The -2 in the lines above means that Smatch can't connect the lock to
a function parameter.
I recently created a module to track IRQ handlers so that I could print
a warning about scheduling in IRQ handlers. But that's probably not
the best way to handle this warning.
The locking information is *almost* good enough to be useful on its own.
My idea is that instead of recording the lock as '&port->lock', we'd
create a new module that would store basically the same information but
with the lock name as '(struct struct uart_port)->lock'. That would be
easier except it wouldn't be really useful for anything except warning
about lockdep_assert_held().
regards,
dan carpenter
drivers/tty/serial/icom.c | check_modem_status | uart_handle_cts_change | INTERNAL | -1 | | void(*)(struct uart_port*, bool)
drivers/tty/serial/icom.c | check_modem_status | uart_handle_cts_change | HALF_LOCKED | -2 | &desc->request_mutex |
drivers/tty/serial/icom.c | check_modem_status | uart_handle_cts_change | LOCKED | -2 | &icom_port->uart_port.lock |
drivers/tty/serial/icom.c | check_modem_status | uart_handle_cts_change | HALF_LOCKED | -2 | flags |
drivers/tty/serial/serial-tegra.c | tegra_uart_handle_modem_signal_change | uart_handle_cts_change | INTERNAL | -1 | | void(*)(struct uart_port*, bool)
drivers/tty/serial/serial-tegra.c | tegra_uart_handle_modem_signal_change | uart_handle_cts_change | HALF_LOCKED | -2 | &desc->request_mutex |
drivers/tty/serial/serial-tegra.c | tegra_uart_handle_modem_signal_change | uart_handle_cts_change | LOCKED | -2 | &u->lock |
drivers/tty/serial/serial-tegra.c | tegra_uart_handle_modem_signal_change | uart_handle_cts_change | HALF_LOCKED | -2 | flags |
drivers/tty/serial/sc16is7xx.c | sc16is7xx_update_mlines | uart_handle_cts_change | INTERNAL | -1 | | void(*)(struct uart_port*, bool)
drivers/tty/serial/sc16is7xx.c | sc16is7xx_update_mlines | uart_handle_cts_change | LOCKED | 0 | &uport->lock |
drivers/tty/serial/sc16is7xx.c | sc16is7xx_update_mlines | uart_handle_cts_change | LOCKED | -2 | &one->efr_lock |
drivers/tty/serial/sc16is7xx.c | sc16is7xx_update_mlines | uart_handle_cts_change | LOCKED | -2 | flags |
drivers/tty/serial/8250/8250_port.c | serial8250_modem_status | uart_handle_cts_change | INTERNAL | -1 | | void(*)(struct uart_port*, bool)
drivers/tty/serial/8250/8250_port.c | serial8250_modem_status | uart_handle_cts_change | HALF_LOCKED | -2 | &port->lock |
drivers/tty/serial/8250/8250_port.c | serial8250_modem_status | uart_handle_cts_change | HALF_LOCKED | -2 | &up->port.lock |
drivers/tty/serial/8250/8250_port.c | serial8250_modem_status | uart_handle_cts_change | HALF_LOCKED | -2 | flags |
drivers/tty/serial/mxs-auart.c | mxs_auart_modem_status | uart_handle_cts_change | INTERNAL | -1 | | void(*)(struct uart_port*, bool)
drivers/tty/serial/mxs-auart.c | mxs_auart_modem_status | uart_handle_cts_change | HALF_LOCKED | -2 | &desc->request_mutex |
drivers/tty/serial/mxs-auart.c | mxs_auart_modem_status | uart_handle_cts_change | HALF_LOCKED | -2 | flags |
drivers/tty/serial/mxs-auart.c | mxs_auart_irq_handle | uart_handle_cts_change | INTERNAL | -1 | | void(*)(struct uart_port*, bool)
drivers/tty/serial/mxs-auart.c | mxs_auart_irq_handle | uart_handle_cts_change | HALF_LOCKED | -2 | &desc->request_mutex |
drivers/tty/serial/mxs-auart.c | mxs_auart_irq_handle | uart_handle_cts_change | HALF_LOCKED | -2 | flags |
drivers/tty/serial/serial_mctrl_gpio.c | mctrl_gpio_irq_handle | uart_handle_cts_change | INTERNAL | -1 | | void(*)(struct uart_port*, bool)
drivers/tty/serial/serial_mctrl_gpio.c | mctrl_gpio_irq_handle | uart_handle_cts_change | LOCKED | 0 | &uport->lock |
drivers/tty/serial/serial_mctrl_gpio.c | mctrl_gpio_irq_handle | uart_handle_cts_change | HALF_LOCKED | -2 | &desc->request_mutex |
drivers/tty/serial/serial_mctrl_gpio.c | mctrl_gpio_irq_handle | uart_handle_cts_change | LOCKED | -2 | flags |
drivers/tty/serial/serial_mctrl_gpio.c | mctrl_gpio_irq_handle | uart_handle_cts_change | HALF_LOCKED | -2 | flags |
drivers/tty/serial/atmel_serial.c | atmel_handle_status | uart_handle_cts_change | INTERNAL | -1 | | void(*)(struct uart_port*, bool)
drivers/tty/serial/atmel_serial.c | atmel_handle_status | uart_handle_cts_change | LOCKED | -2 | &atmel_port->lock_suspended |
drivers/tty/serial/atmel_serial.c | atmel_handle_status | uart_handle_cts_change | HALF_LOCKED | -2 | flags |
drivers/tty/serial/max310x.c | max310x_port_irq | uart_handle_cts_change | INTERNAL | -1 | | void(*)(struct uart_port*, bool)
drivers/tty/serial/imx.c | __imx_uart_rtsint | uart_handle_cts_change | INTERNAL | -1 | | void(*)(struct uart_port*, bool)
drivers/tty/serial/imx.c | __imx_uart_rtsint | uart_handle_cts_change | HALF_LOCKED | -2 | &desc->request_mutex |
drivers/tty/serial/imx.c | __imx_uart_rtsint | uart_handle_cts_change | LOCKED | -2 | &sport->port.lock |
drivers/tty/serial/imx.c | __imx_uart_rtsint | uart_handle_cts_change | HALF_LOCKED | -2 | flags |
drivers/tty/serial/imx.c | imx_uart_mctrl_check | uart_handle_cts_change | INTERNAL | -1 | | void(*)(struct uart_port*, bool)
drivers/tty/serial/imx.c | imx_uart_mctrl_check | uart_handle_cts_change | LOCKED | -2 | &sport->port.lock |
drivers/tty/serial/imx.c | imx_uart_mctrl_check | uart_handle_cts_change | HALF_LOCKED | -2 | flags |
drivers/tty/serial/omap-serial.c | check_modem_status | uart_handle_cts_change | INTERNAL | -1 | | void(*)(struct uart_port*, bool)
drivers/tty/serial/omap-serial.c | check_modem_status | uart_handle_cts_change | HALF_LOCKED | -2 | &port->lock |
drivers/tty/serial/omap-serial.c | check_modem_status | uart_handle_cts_change | HALF_LOCKED | -2 | &up->port.lock |
drivers/tty/serial/men_z135_uart.c | men_z135_handle_modem_status | uart_handle_cts_change | INTERNAL | -1 | | void(*)(struct uart_port*, bool)
drivers/tty/serial/men_z135_uart.c | men_z135_handle_modem_status | uart_handle_cts_change | HALF_LOCKED | -2 | &desc->request_mutex |
drivers/tty/serial/men_z135_uart.c | men_z135_handle_modem_status | uart_handle_cts_change | LOCKED | -2 | &port->lock |
drivers/tty/serial/men_z135_uart.c | men_z135_handle_modem_status | uart_handle_cts_change | HALF_LOCKED | -2 | flags |
drivers/tty/serial/timbuart.c | timbuart_mctrl_check | uart_handle_cts_change | INTERNAL | -1 | | void(*)(struct uart_port*, bool)
drivers/tty/serial/timbuart.c | timbuart_mctrl_check | uart_handle_cts_change | LOCKED | -2 | &uart->port.lock |
drivers/tty/serial/jsm/jsm_neo.c | neo_parse_modem | uart_handle_cts_change | INTERNAL | -1 | | void(*)(struct uart_port*, bool)
drivers/tty/serial/jsm/jsm_neo.c | neo_parse_modem | uart_handle_cts_change | HALF_LOCKED | -2 | &brd->bd_intr_lock |
drivers/tty/serial/jsm/jsm_neo.c | neo_parse_modem | uart_handle_cts_change | HALF_LOCKED | -2 | &ch->uart_port.lock |
drivers/tty/serial/jsm/jsm_neo.c | neo_parse_modem | uart_handle_cts_change | HALF_LOCKED | -2 | &port->lock |
drivers/tty/serial/jsm/jsm_neo.c | neo_parse_modem | uart_handle_cts_change | LOCKED | -2 | lock_flags |
drivers/tty/serial/bcm63xx_uart.c | bcm_uart_interrupt | uart_handle_cts_change | INTERNAL | -1 | | void(*)(struct uart_port*, bool)
drivers/tty/serial/bcm63xx_uart.c | bcm_uart_interrupt | uart_handle_cts_change | LOCKED | 0 | &uport->lock |
drivers/tty/serial/bcm63xx_uart.c | bcm_uart_interrupt | uart_handle_cts_change | HALF_LOCKED | -2 | &desc->request_mutex |
drivers/tty/serial/bcm63xx_uart.c | bcm_uart_interrupt | uart_handle_cts_change | HALF_LOCKED | -2 | flags |
drivers/tty/serial/max3100.c | max3100_handlerx | uart_handle_cts_change | INTERNAL | -1 | | void(*)(struct uart_port*, bool)
drivers/tty/serial/max3100.c | max3100_handlerx | uart_handle_cts_change | HALF_LOCKED | -2 | &pool->lock |
drivers/tty/serial/amba-pl010.c | pl010_modem_status | uart_handle_cts_change | INTERNAL | -1 | | void(*)(struct uart_port*, bool)
drivers/tty/serial/amba-pl010.c | pl010_modem_status | uart_handle_cts_change | HALF_LOCKED | -2 | &desc->request_mutex |
drivers/tty/serial/amba-pl010.c | pl010_modem_status | uart_handle_cts_change | LOCKED | -2 | &port->lock |
drivers/tty/serial/amba-pl010.c | pl010_modem_status | uart_handle_cts_change | HALF_LOCKED | -2 | flags |
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: smatch and locking checks
2024-03-15 10:35 ` smatch and locking checks Dan Carpenter
@ 2024-03-15 10:46 ` Dan Carpenter
2024-03-15 10:56 ` Dan Carpenter
0 siblings, 1 reply; 3+ messages in thread
From: Dan Carpenter @ 2024-03-15 10:46 UTC (permalink / raw)
To: Rasmus Villemoes; +Cc: Dan Carpenter, smatch
[-- 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);
}
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: smatch and locking checks
2024-03-15 10:46 ` Dan Carpenter
@ 2024-03-15 10:56 ` Dan Carpenter
0 siblings, 0 replies; 3+ messages in thread
From: Dan Carpenter @ 2024-03-15 10:56 UTC (permalink / raw)
To: Rasmus Villemoes; +Cc: Dan Carpenter, smatch
On Fri, Mar 15, 2024 at 01:46:29PM +0300, Dan Carpenter wrote:
> 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.
Actually instead of fixing gen_expression_from_key() what I could do is:
var = get_variable_from_key(arg, key, &sym);
lock = gen_expression_from_name_sym(var, sym);
It's slightly immoral to work around bugs instead of fixing them but it
has the advantage of being much faster.
I think the check will work now. I left out a bunch of stuff in the
code I sent. The changes to smatch.h, the Makefile and check_list.h.
But I'll test this code out and resend.
regards,
dan carpenter
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2024-03-15 10:56 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[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
2024-03-15 10:56 ` Dan Carpenter
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox