11f5207b7SJohn Levon /*
21f5207b7SJohn Levon * Copyright (C) 2009 Dan Carpenter.
35a0e240fSJohn Levon * Copyright (C) 2019 Oracle.
41f5207b7SJohn Levon *
51f5207b7SJohn Levon * This program is free software; you can redistribute it and/or
61f5207b7SJohn Levon * modify it under the terms of the GNU General Public License
71f5207b7SJohn Levon * as published by the Free Software Foundation; either version 2
81f5207b7SJohn Levon * of the License, or (at your option) any later version.
91f5207b7SJohn Levon *
101f5207b7SJohn Levon * This program is distributed in the hope that it will be useful,
111f5207b7SJohn Levon * but WITHOUT ANY WARRANTY; without even the implied warranty of
121f5207b7SJohn Levon * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
131f5207b7SJohn Levon * GNU General Public License for more details.
141f5207b7SJohn Levon *
151f5207b7SJohn Levon * You should have received a copy of the GNU General Public License
161f5207b7SJohn Levon * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
171f5207b7SJohn Levon */
181f5207b7SJohn Levon
195a0e240fSJohn Levon #include <ctype.h>
201f5207b7SJohn Levon #include "parse.h"
211f5207b7SJohn Levon #include "smatch.h"
221f5207b7SJohn Levon #include "smatch_extra.h"
231f5207b7SJohn Levon #include "smatch_slist.h"
241f5207b7SJohn Levon
251f5207b7SJohn Levon static int my_id;
261f5207b7SJohn Levon
271f5207b7SJohn Levon STATE(locked);
285a0e240fSJohn Levon STATE(half_locked);
291f5207b7SJohn Levon STATE(start_state);
301f5207b7SJohn Levon STATE(unlocked);
311f5207b7SJohn Levon STATE(impossible);
325a0e240fSJohn Levon STATE(restore);
331f5207b7SJohn Levon
341f5207b7SJohn Levon enum action {
351f5207b7SJohn Levon LOCK,
361f5207b7SJohn Levon UNLOCK,
375a0e240fSJohn Levon RESTORE,
385a0e240fSJohn Levon };
395a0e240fSJohn Levon
405a0e240fSJohn Levon enum lock_type {
415a0e240fSJohn Levon spin_lock,
425a0e240fSJohn Levon read_lock,
435a0e240fSJohn Levon write_lock,
445a0e240fSJohn Levon mutex,
455a0e240fSJohn Levon bottom_half,
465a0e240fSJohn Levon irq,
475a0e240fSJohn Levon sem,
485a0e240fSJohn Levon prepare_lock,
495a0e240fSJohn Levon enable_lock,
501f5207b7SJohn Levon };
511f5207b7SJohn Levon
get_lock_name(enum lock_type type)525a0e240fSJohn Levon const char *get_lock_name(enum lock_type type)
535a0e240fSJohn Levon {
545a0e240fSJohn Levon static const char *names[] = {
555a0e240fSJohn Levon [spin_lock] = "spin_lock",
565a0e240fSJohn Levon [read_lock] = "read_lock",
575a0e240fSJohn Levon [write_lock] = "write_lock",
585a0e240fSJohn Levon [mutex] = "mutex",
595a0e240fSJohn Levon [bottom_half] = "bottom_half",
605a0e240fSJohn Levon [irq] = "irq",
615a0e240fSJohn Levon [sem] = "sem",
625a0e240fSJohn Levon [prepare_lock] = "prepare_lock",
635a0e240fSJohn Levon [enable_lock] = "enable_lock",
645a0e240fSJohn Levon };
655a0e240fSJohn Levon
665a0e240fSJohn Levon return names[type];
675a0e240fSJohn Levon }
685a0e240fSJohn Levon
691f5207b7SJohn Levon enum return_type {
701f5207b7SJohn Levon ret_any,
711f5207b7SJohn Levon ret_zero,
72efe51d0cSJohn Levon ret_one,
731f5207b7SJohn Levon ret_negative,
741f5207b7SJohn Levon ret_positive,
755a0e240fSJohn Levon ret_valid_ptr,
761f5207b7SJohn Levon };
771f5207b7SJohn Levon
781f5207b7SJohn Levon #define RETURN_VAL -1
791f5207b7SJohn Levon #define NO_ARG -2
801f5207b7SJohn Levon
811f5207b7SJohn Levon struct lock_info {
821f5207b7SJohn Levon const char *function;
831f5207b7SJohn Levon enum action action;
845a0e240fSJohn Levon enum lock_type type;
851f5207b7SJohn Levon int arg;
861f5207b7SJohn Levon enum return_type return_type;
871f5207b7SJohn Levon };
881f5207b7SJohn Levon
895a0e240fSJohn Levon static struct lock_info lock_table[] = {
905a0e240fSJohn Levon {"spin_lock", LOCK, spin_lock, 0, ret_any},
915a0e240fSJohn Levon {"spin_unlock", UNLOCK, spin_lock, 0, ret_any},
925a0e240fSJohn Levon {"spin_lock_nested", LOCK, spin_lock, 0, ret_any},
935a0e240fSJohn Levon {"_spin_lock", LOCK, spin_lock, 0, ret_any},
945a0e240fSJohn Levon {"_spin_unlock", UNLOCK, spin_lock, 0, ret_any},
955a0e240fSJohn Levon {"_spin_lock_nested", LOCK, spin_lock, 0, ret_any},
965a0e240fSJohn Levon {"__spin_lock", LOCK, spin_lock, 0, ret_any},
975a0e240fSJohn Levon {"__spin_unlock", UNLOCK, spin_lock, 0, ret_any},
985a0e240fSJohn Levon {"__spin_lock_nested", LOCK, spin_lock, 0, ret_any},
995a0e240fSJohn Levon {"raw_spin_lock", LOCK, spin_lock, 0, ret_any},
1005a0e240fSJohn Levon {"raw_spin_unlock", UNLOCK, spin_lock, 0, ret_any},
1015a0e240fSJohn Levon {"_raw_spin_lock", LOCK, spin_lock, 0, ret_any},
1025a0e240fSJohn Levon {"_raw_spin_lock_nested", LOCK, spin_lock, 0, ret_any},
1035a0e240fSJohn Levon {"_raw_spin_unlock", UNLOCK, spin_lock, 0, ret_any},
1045a0e240fSJohn Levon {"__raw_spin_lock", LOCK, spin_lock, 0, ret_any},
1055a0e240fSJohn Levon {"__raw_spin_unlock", UNLOCK, spin_lock, 0, ret_any},
1065a0e240fSJohn Levon
1075a0e240fSJohn Levon {"spin_lock_irq", LOCK, spin_lock, 0, ret_any},
1085a0e240fSJohn Levon {"spin_unlock_irq", UNLOCK, spin_lock, 0, ret_any},
1095a0e240fSJohn Levon {"_spin_lock_irq", LOCK, spin_lock, 0, ret_any},
1105a0e240fSJohn Levon {"_spin_unlock_irq", UNLOCK, spin_lock, 0, ret_any},
1115a0e240fSJohn Levon {"__spin_lock_irq", LOCK, spin_lock, 0, ret_any},
1125a0e240fSJohn Levon {"__spin_unlock_irq", UNLOCK, spin_lock, 0, ret_any},
1135a0e240fSJohn Levon {"_raw_spin_lock_irq", LOCK, spin_lock, 0, ret_any},
1145a0e240fSJohn Levon {"_raw_spin_unlock_irq", UNLOCK, spin_lock, 0, ret_any},
1155a0e240fSJohn Levon {"__raw_spin_unlock_irq", UNLOCK, spin_lock, 0, ret_any},
1165a0e240fSJohn Levon {"spin_lock_irqsave", LOCK, spin_lock, 0, ret_any},
1175a0e240fSJohn Levon {"spin_unlock_irqrestore", UNLOCK, spin_lock, 0, ret_any},
1185a0e240fSJohn Levon {"_spin_lock_irqsave", LOCK, spin_lock, 0, ret_any},
1195a0e240fSJohn Levon {"_spin_unlock_irqrestore", UNLOCK, spin_lock, 0, ret_any},
1205a0e240fSJohn Levon {"__spin_lock_irqsave", LOCK, spin_lock, 0, ret_any},
1215a0e240fSJohn Levon {"__spin_unlock_irqrestore", UNLOCK, spin_lock, 0, ret_any},
1225a0e240fSJohn Levon {"_raw_spin_lock_irqsave", LOCK, spin_lock, 0, ret_any},
1235a0e240fSJohn Levon {"_raw_spin_unlock_irqrestore", UNLOCK, spin_lock, 0, ret_any},
1245a0e240fSJohn Levon {"__raw_spin_lock_irqsave", LOCK, spin_lock, 0, ret_any},
1255a0e240fSJohn Levon {"__raw_spin_unlock_irqrestore", UNLOCK, spin_lock, 0, ret_any},
1265a0e240fSJohn Levon {"spin_lock_irqsave_nested", LOCK, spin_lock, 0, ret_any},
1275a0e240fSJohn Levon {"_spin_lock_irqsave_nested", LOCK, spin_lock, 0, ret_any},
1285a0e240fSJohn Levon {"__spin_lock_irqsave_nested", LOCK, spin_lock, 0, ret_any},
1295a0e240fSJohn Levon {"_raw_spin_lock_irqsave_nested", LOCK, spin_lock, 0, ret_any},
1305a0e240fSJohn Levon {"spin_lock_bh", LOCK, spin_lock, 0, ret_any},
1315a0e240fSJohn Levon {"spin_unlock_bh", UNLOCK, spin_lock, 0, ret_any},
1325a0e240fSJohn Levon {"_spin_lock_bh", LOCK, spin_lock, 0, ret_any},
1335a0e240fSJohn Levon {"_spin_unlock_bh", UNLOCK, spin_lock, 0, ret_any},
1345a0e240fSJohn Levon {"__spin_lock_bh", LOCK, spin_lock, 0, ret_any},
1355a0e240fSJohn Levon {"__spin_unlock_bh", UNLOCK, spin_lock, 0, ret_any},
1365a0e240fSJohn Levon
1375a0e240fSJohn Levon {"spin_trylock", LOCK, spin_lock, 0, ret_one},
1385a0e240fSJohn Levon {"_spin_trylock", LOCK, spin_lock, 0, ret_one},
1395a0e240fSJohn Levon {"__spin_trylock", LOCK, spin_lock, 0, ret_one},
1405a0e240fSJohn Levon {"raw_spin_trylock", LOCK, spin_lock, 0, ret_one},
1415a0e240fSJohn Levon {"_raw_spin_trylock", LOCK, spin_lock, 0, ret_one},
1425a0e240fSJohn Levon {"spin_trylock_irq", LOCK, spin_lock, 0, ret_one},
1435a0e240fSJohn Levon {"spin_trylock_irqsave", LOCK, spin_lock, 0, ret_one},
1445a0e240fSJohn Levon {"spin_trylock_bh", LOCK, spin_lock, 0, ret_one},
1455a0e240fSJohn Levon {"_spin_trylock_bh", LOCK, spin_lock, 0, ret_one},
1465a0e240fSJohn Levon {"__spin_trylock_bh", LOCK, spin_lock, 0, ret_one},
1475a0e240fSJohn Levon {"__raw_spin_trylock", LOCK, spin_lock, 0, ret_one},
1485a0e240fSJohn Levon {"_atomic_dec_and_lock", LOCK, spin_lock, 1, ret_one},
1495a0e240fSJohn Levon
1505a0e240fSJohn Levon {"read_lock", LOCK, read_lock, 0, ret_any},
1515a0e240fSJohn Levon {"down_read", LOCK, read_lock, 0, ret_any},
1525a0e240fSJohn Levon {"down_read_nested", LOCK, read_lock, 0, ret_any},
1535a0e240fSJohn Levon {"down_read_trylock", LOCK, read_lock, 0, ret_one},
1545a0e240fSJohn Levon {"up_read", UNLOCK, read_lock, 0, ret_any},
1555a0e240fSJohn Levon {"read_unlock", UNLOCK, read_lock, 0, ret_any},
1565a0e240fSJohn Levon {"_read_lock", LOCK, read_lock, 0, ret_any},
1575a0e240fSJohn Levon {"_read_unlock", UNLOCK, read_lock, 0, ret_any},
1585a0e240fSJohn Levon {"__read_lock", LOCK, read_lock, 0, ret_any},
1595a0e240fSJohn Levon {"__read_unlock", UNLOCK, read_lock, 0, ret_any},
1605a0e240fSJohn Levon {"_raw_read_lock", LOCK, read_lock, 0, ret_any},
1615a0e240fSJohn Levon {"_raw_read_unlock", UNLOCK, read_lock, 0, ret_any},
1625a0e240fSJohn Levon {"__raw_read_lock", LOCK, read_lock, 0, ret_any},
1635a0e240fSJohn Levon {"__raw_read_unlock", UNLOCK, read_lock, 0, ret_any},
1645a0e240fSJohn Levon {"read_lock_irq", LOCK, read_lock, 0, ret_any},
1655a0e240fSJohn Levon {"read_unlock_irq" , UNLOCK, read_lock, 0, ret_any},
1665a0e240fSJohn Levon {"_read_lock_irq", LOCK, read_lock, 0, ret_any},
1675a0e240fSJohn Levon {"_read_unlock_irq", UNLOCK, read_lock, 0, ret_any},
1685a0e240fSJohn Levon {"__read_lock_irq", LOCK, read_lock, 0, ret_any},
1695a0e240fSJohn Levon {"__read_unlock_irq", UNLOCK, read_lock, 0, ret_any},
1705a0e240fSJohn Levon {"_raw_read_unlock_irq", UNLOCK, read_lock, 0, ret_any},
1715a0e240fSJohn Levon {"_raw_read_lock_irq", LOCK, read_lock, 0, ret_any},
1725a0e240fSJohn Levon {"_raw_read_lock_bh", LOCK, read_lock, 0, ret_any},
1735a0e240fSJohn Levon {"_raw_read_unlock_bh", UNLOCK, read_lock, 0, ret_any},
1745a0e240fSJohn Levon {"read_lock_irqsave", LOCK, read_lock, 0, ret_any},
1755a0e240fSJohn Levon {"read_unlock_irqrestore", UNLOCK, read_lock, 0, ret_any},
1765a0e240fSJohn Levon {"_read_lock_irqsave", LOCK, read_lock, 0, ret_any},
1775a0e240fSJohn Levon {"_read_unlock_irqrestore", UNLOCK, read_lock, 0, ret_any},
1785a0e240fSJohn Levon {"__read_lock_irqsave", LOCK, read_lock, 0, ret_any},
1795a0e240fSJohn Levon {"__read_unlock_irqrestore", UNLOCK, read_lock, 0, ret_any},
1805a0e240fSJohn Levon {"read_lock_bh", LOCK, read_lock, 0, ret_any},
1815a0e240fSJohn Levon {"read_unlock_bh", UNLOCK, read_lock, 0, ret_any},
1825a0e240fSJohn Levon {"_read_lock_bh", LOCK, read_lock, 0, ret_any},
1835a0e240fSJohn Levon {"_read_unlock_bh", UNLOCK, read_lock, 0, ret_any},
1845a0e240fSJohn Levon {"__read_lock_bh", LOCK, read_lock, 0, ret_any},
1855a0e240fSJohn Levon {"__read_unlock_bh", UNLOCK, read_lock, 0, ret_any},
1865a0e240fSJohn Levon {"__raw_read_lock_bh", LOCK, read_lock, 0, ret_any},
1875a0e240fSJohn Levon {"__raw_read_unlock_bh", UNLOCK, read_lock, 0, ret_any},
1885a0e240fSJohn Levon
1895a0e240fSJohn Levon {"_raw_read_lock_irqsave", LOCK, read_lock, 0, ret_any},
1905a0e240fSJohn Levon {"_raw_read_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
1915a0e240fSJohn Levon {"_raw_read_unlock_irqrestore", UNLOCK, read_lock, 0, ret_any},
1925a0e240fSJohn Levon {"_raw_read_unlock_irqrestore", RESTORE, irq, 1, ret_any},
1935a0e240fSJohn Levon {"_raw_spin_lock_bh", LOCK, read_lock, 0, ret_any},
1945a0e240fSJohn Levon {"_raw_spin_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
1955a0e240fSJohn Levon {"_raw_spin_lock_nest_lock", LOCK, read_lock, 0, ret_any},
1965a0e240fSJohn Levon {"_raw_spin_unlock_bh", UNLOCK, read_lock, 0, ret_any},
1975a0e240fSJohn Levon {"_raw_spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
1985a0e240fSJohn Levon {"_raw_write_lock_irqsave", LOCK, write_lock, 0, ret_any},
1995a0e240fSJohn Levon {"_raw_write_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
2005a0e240fSJohn Levon {"_raw_write_unlock_irqrestore", UNLOCK, write_lock, 0, ret_any},
2015a0e240fSJohn Levon {"_raw_write_unlock_irqrestore", RESTORE, irq, 1, ret_any},
2025a0e240fSJohn Levon {"__raw_write_unlock_irqrestore", UNLOCK, write_lock, 0, ret_any},
2035a0e240fSJohn Levon {"__raw_write_unlock_irqrestore", RESTORE, irq, 1, ret_any},
2045a0e240fSJohn Levon
2055a0e240fSJohn Levon {"generic__raw_read_trylock", LOCK, read_lock, 0, ret_one},
2065a0e240fSJohn Levon {"read_trylock", LOCK, read_lock, 0, ret_one},
2075a0e240fSJohn Levon {"_read_trylock", LOCK, read_lock, 0, ret_one},
2085a0e240fSJohn Levon {"raw_read_trylock", LOCK, read_lock, 0, ret_one},
2095a0e240fSJohn Levon {"_raw_read_trylock", LOCK, read_lock, 0, ret_one},
2105a0e240fSJohn Levon {"__raw_read_trylock", LOCK, read_lock, 0, ret_one},
2115a0e240fSJohn Levon {"__read_trylock", LOCK, read_lock, 0, ret_one},
2125a0e240fSJohn Levon
2135a0e240fSJohn Levon {"write_lock", LOCK, write_lock, 0, ret_any},
2145a0e240fSJohn Levon {"down_write", LOCK, write_lock, 0, ret_any},
2155a0e240fSJohn Levon {"down_write_nested", LOCK, write_lock, 0, ret_any},
2165a0e240fSJohn Levon {"up_write", UNLOCK, write_lock, 0, ret_any},
2175a0e240fSJohn Levon {"write_unlock", UNLOCK, write_lock, 0, ret_any},
2185a0e240fSJohn Levon {"_write_lock", LOCK, write_lock, 0, ret_any},
2195a0e240fSJohn Levon {"_write_unlock", UNLOCK, write_lock, 0, ret_any},
2205a0e240fSJohn Levon {"__write_lock", LOCK, write_lock, 0, ret_any},
2215a0e240fSJohn Levon {"__write_unlock", UNLOCK, write_lock, 0, ret_any},
2225a0e240fSJohn Levon {"write_lock_irq", LOCK, write_lock, 0, ret_any},
2235a0e240fSJohn Levon {"write_unlock_irq", UNLOCK, write_lock, 0, ret_any},
2245a0e240fSJohn Levon {"_write_lock_irq", LOCK, write_lock, 0, ret_any},
2255a0e240fSJohn Levon {"_write_unlock_irq", UNLOCK, write_lock, 0, ret_any},
2265a0e240fSJohn Levon {"__write_lock_irq", LOCK, write_lock, 0, ret_any},
2275a0e240fSJohn Levon {"__write_unlock_irq", UNLOCK, write_lock, 0, ret_any},
2285a0e240fSJohn Levon {"_raw_write_unlock_irq", UNLOCK, write_lock, 0, ret_any},
2295a0e240fSJohn Levon {"write_lock_irqsave", LOCK, write_lock, 0, ret_any},
2305a0e240fSJohn Levon {"write_unlock_irqrestore", UNLOCK, write_lock, 0, ret_any},
2315a0e240fSJohn Levon {"_write_lock_irqsave", LOCK, write_lock, 0, ret_any},
2325a0e240fSJohn Levon {"_write_unlock_irqrestore", UNLOCK, write_lock, 0, ret_any},
2335a0e240fSJohn Levon {"__write_lock_irqsave", LOCK, write_lock, 0, ret_any},
2345a0e240fSJohn Levon {"__write_unlock_irqrestore", UNLOCK, write_lock, 0, ret_any},
2355a0e240fSJohn Levon {"write_lock_bh", LOCK, write_lock, 0, ret_any},
2365a0e240fSJohn Levon {"write_unlock_bh", UNLOCK, write_lock, 0, ret_any},
2375a0e240fSJohn Levon {"_write_lock_bh", LOCK, write_lock, 0, ret_any},
2385a0e240fSJohn Levon {"_write_unlock_bh", UNLOCK, write_lock, 0, ret_any},
2395a0e240fSJohn Levon {"__write_lock_bh", LOCK, write_lock, 0, ret_any},
2405a0e240fSJohn Levon {"__write_unlock_bh", UNLOCK, write_lock, 0, ret_any},
2415a0e240fSJohn Levon {"_raw_write_lock", LOCK, write_lock, 0, ret_any},
2425a0e240fSJohn Levon {"__raw_write_lock", LOCK, write_lock, 0, ret_any},
2435a0e240fSJohn Levon {"_raw_write_unlock", UNLOCK, write_lock, 0, ret_any},
2445a0e240fSJohn Levon {"__raw_write_unlock", UNLOCK, write_lock, 0, ret_any},
2455a0e240fSJohn Levon {"_raw_write_lock_bh", LOCK, write_lock, 0, ret_any},
2465a0e240fSJohn Levon {"_raw_write_unlock_bh", UNLOCK, write_lock, 0, ret_any},
2475a0e240fSJohn Levon {"_raw_write_lock_irq", LOCK, write_lock, 0, ret_any},
2485a0e240fSJohn Levon
2495a0e240fSJohn Levon {"write_trylock", LOCK, write_lock, 0, ret_one},
2505a0e240fSJohn Levon {"_write_trylock", LOCK, write_lock, 0, ret_one},
2515a0e240fSJohn Levon {"raw_write_trylock", LOCK, write_lock, 0, ret_one},
2525a0e240fSJohn Levon {"_raw_write_trylock", LOCK, write_lock, 0, ret_one},
2535a0e240fSJohn Levon {"__write_trylock", LOCK, write_lock, 0, ret_one},
2545a0e240fSJohn Levon {"__raw_write_trylock", LOCK, write_lock, 0, ret_one},
2555a0e240fSJohn Levon {"down_write_trylock", LOCK, write_lock, 0, ret_one},
2565a0e240fSJohn Levon {"down_write_killable", LOCK, write_lock, 0, ret_zero},
2575a0e240fSJohn Levon
2585a0e240fSJohn Levon {"down", LOCK, sem, 0, ret_any},
2595a0e240fSJohn Levon {"up", UNLOCK, sem, 0, ret_any},
2605a0e240fSJohn Levon {"down_trylock", LOCK, sem, 0, ret_zero},
2615a0e240fSJohn Levon {"down_timeout", LOCK, sem, 0, ret_zero},
2625a0e240fSJohn Levon {"down_interruptible", LOCK, sem, 0, ret_zero},
2635a0e240fSJohn Levon {"down_killable", LOCK, sem, 0, ret_zero},
2645a0e240fSJohn Levon
2655a0e240fSJohn Levon
2665a0e240fSJohn Levon {"mutex_lock", LOCK, mutex, 0, ret_any},
2675a0e240fSJohn Levon {"mutex_unlock", UNLOCK, mutex, 0, ret_any},
2685a0e240fSJohn Levon {"mutex_lock_nested", LOCK, mutex, 0, ret_any},
2695a0e240fSJohn Levon {"mutex_lock_io", LOCK, mutex, 0, ret_any},
2705a0e240fSJohn Levon {"mutex_lock_io_nested", LOCK, mutex, 0, ret_any},
2715a0e240fSJohn Levon
2725a0e240fSJohn Levon {"mutex_lock_interruptible", LOCK, mutex, 0, ret_zero},
2735a0e240fSJohn Levon {"mutex_lock_interruptible_nested", LOCK, mutex, 0, ret_zero},
2745a0e240fSJohn Levon {"mutex_lock_killable", LOCK, mutex, 0, ret_zero},
2755a0e240fSJohn Levon {"mutex_lock_killable_nested", LOCK, mutex, 0, ret_zero},
2765a0e240fSJohn Levon
2775a0e240fSJohn Levon {"mutex_trylock", LOCK, mutex, 0, ret_one},
2785a0e240fSJohn Levon
2795a0e240fSJohn Levon {"ww_mutex_lock", LOCK, mutex, 0, ret_any},
2805a0e240fSJohn Levon {"__ww_mutex_lock", LOCK, mutex, 0, ret_any},
2815a0e240fSJohn Levon {"ww_mutex_lock_interruptible", LOCK, mutex, 0, ret_zero},
2825a0e240fSJohn Levon {"ww_mutex_unlock", UNLOCK, mutex, 0, ret_any},
2835a0e240fSJohn Levon
2845a0e240fSJohn Levon {"raw_local_irq_disable", LOCK, irq, NO_ARG, ret_any},
2855a0e240fSJohn Levon {"raw_local_irq_enable", UNLOCK, irq, NO_ARG, ret_any},
2865a0e240fSJohn Levon {"spin_lock_irq", LOCK, irq, NO_ARG, ret_any},
2875a0e240fSJohn Levon {"spin_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
2885a0e240fSJohn Levon {"_spin_lock_irq", LOCK, irq, NO_ARG, ret_any},
2895a0e240fSJohn Levon {"_spin_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
2905a0e240fSJohn Levon {"__spin_lock_irq", LOCK, irq, NO_ARG, ret_any},
2915a0e240fSJohn Levon {"__spin_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
2925a0e240fSJohn Levon {"_raw_spin_lock_irq", LOCK, irq, NO_ARG, ret_any},
2935a0e240fSJohn Levon {"_raw_spin_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
2945a0e240fSJohn Levon {"__raw_spin_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
2955a0e240fSJohn Levon {"spin_trylock_irq", LOCK, irq, NO_ARG, ret_one},
2965a0e240fSJohn Levon {"read_lock_irq", LOCK, irq, NO_ARG, ret_any},
2975a0e240fSJohn Levon {"read_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
2985a0e240fSJohn Levon {"_read_lock_irq", LOCK, irq, NO_ARG, ret_any},
2995a0e240fSJohn Levon {"_read_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
3005a0e240fSJohn Levon {"__read_lock_irq", LOCK, irq, NO_ARG, ret_any},
3015a0e240fSJohn Levon {"_raw_read_lock_irq", LOCK, irq, NO_ARG, ret_any},
3025a0e240fSJohn Levon {"__read_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
3035a0e240fSJohn Levon {"_raw_read_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
3045a0e240fSJohn Levon {"write_lock_irq", LOCK, irq, NO_ARG, ret_any},
3055a0e240fSJohn Levon {"write_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
3065a0e240fSJohn Levon {"_write_lock_irq", LOCK, irq, NO_ARG, ret_any},
3075a0e240fSJohn Levon {"_write_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
3085a0e240fSJohn Levon {"__write_lock_irq", LOCK, irq, NO_ARG, ret_any},
3095a0e240fSJohn Levon {"__write_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
3105a0e240fSJohn Levon {"_raw_write_lock_irq", LOCK, irq, NO_ARG, ret_any},
3115a0e240fSJohn Levon {"_raw_write_unlock_irq", UNLOCK, irq, NO_ARG, ret_any},
3125a0e240fSJohn Levon
3135a0e240fSJohn Levon {"arch_local_irq_save", LOCK, irq, RETURN_VAL, ret_any},
3145a0e240fSJohn Levon {"arch_local_irq_restore", RESTORE, irq, 0, ret_any},
3155a0e240fSJohn Levon {"__raw_local_irq_save", LOCK, irq, RETURN_VAL, ret_any},
3165a0e240fSJohn Levon {"raw_local_irq_restore", RESTORE, irq, 0, ret_any},
3175a0e240fSJohn Levon {"spin_lock_irqsave_nested", LOCK, irq, RETURN_VAL, ret_any},
3185a0e240fSJohn Levon {"spin_lock_irqsave", LOCK, irq, 1, ret_any},
3195a0e240fSJohn Levon {"spin_unlock_irqrestore", RESTORE, irq, 1, ret_any},
3205a0e240fSJohn Levon {"_spin_lock_irqsave_nested", LOCK, irq, RETURN_VAL, ret_any},
3215a0e240fSJohn Levon {"_spin_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
3225a0e240fSJohn Levon {"_spin_lock_irqsave", LOCK, irq, 1, ret_any},
3235a0e240fSJohn Levon {"_spin_unlock_irqrestore", RESTORE, irq, 1, ret_any},
3245a0e240fSJohn Levon {"__spin_lock_irqsave_nested", LOCK, irq, 1, ret_any},
3255a0e240fSJohn Levon {"__spin_lock_irqsave", LOCK, irq, 1, ret_any},
3265a0e240fSJohn Levon {"__spin_unlock_irqrestore", RESTORE, irq, 1, ret_any},
3275a0e240fSJohn Levon {"_raw_spin_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
3285a0e240fSJohn Levon {"_raw_spin_lock_irqsave", LOCK, irq, 1, ret_any},
3295a0e240fSJohn Levon {"_raw_spin_unlock_irqrestore", RESTORE, irq, 1, ret_any},
3305a0e240fSJohn Levon {"__raw_spin_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
3315a0e240fSJohn Levon {"__raw_spin_unlock_irqrestore", RESTORE, irq, 1, ret_any},
3325a0e240fSJohn Levon {"_raw_spin_lock_irqsave_nested", LOCK, irq, RETURN_VAL, ret_any},
3335a0e240fSJohn Levon {"spin_trylock_irqsave", LOCK, irq, 1, ret_one},
3345a0e240fSJohn Levon {"read_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
3355a0e240fSJohn Levon {"read_lock_irqsave", LOCK, irq, 1, ret_any},
3365a0e240fSJohn Levon {"read_unlock_irqrestore", RESTORE, irq, 1, ret_any},
3375a0e240fSJohn Levon {"_read_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
3385a0e240fSJohn Levon {"_read_lock_irqsave", LOCK, irq, 1, ret_any},
3395a0e240fSJohn Levon {"_read_unlock_irqrestore", RESTORE, irq, 1, ret_any},
3405a0e240fSJohn Levon {"__read_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
3415a0e240fSJohn Levon {"__read_unlock_irqrestore", RESTORE, irq, 1, ret_any},
3425a0e240fSJohn Levon {"write_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
3435a0e240fSJohn Levon {"write_lock_irqsave", LOCK, irq, 1, ret_any},
3445a0e240fSJohn Levon {"write_unlock_irqrestore", RESTORE, irq, 1, ret_any},
3455a0e240fSJohn Levon {"_write_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
3465a0e240fSJohn Levon {"_write_lock_irqsave", LOCK, irq, 1, ret_any},
3475a0e240fSJohn Levon {"_write_unlock_irqrestore", RESTORE, irq, 1, ret_any},
3485a0e240fSJohn Levon {"__write_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any},
3495a0e240fSJohn Levon {"__write_unlock_irqrestore", RESTORE, irq, 1, ret_any},
3505a0e240fSJohn Levon
3515a0e240fSJohn Levon {"local_bh_disable", LOCK, bottom_half, NO_ARG, ret_any},
3525a0e240fSJohn Levon {"_local_bh_disable", LOCK, bottom_half, NO_ARG, ret_any},
3535a0e240fSJohn Levon {"__local_bh_disable", LOCK, bottom_half, NO_ARG, ret_any},
3545a0e240fSJohn Levon {"local_bh_enable", UNLOCK, bottom_half, NO_ARG, ret_any},
3555a0e240fSJohn Levon {"_local_bh_enable", UNLOCK, bottom_half, NO_ARG, ret_any},
3565a0e240fSJohn Levon {"__local_bh_enable", UNLOCK, bottom_half, NO_ARG, ret_any},
3575a0e240fSJohn Levon {"spin_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
3585a0e240fSJohn Levon {"spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
3595a0e240fSJohn Levon {"_spin_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
3605a0e240fSJohn Levon {"_spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
3615a0e240fSJohn Levon {"__spin_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
3625a0e240fSJohn Levon {"__spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
3635a0e240fSJohn Levon {"read_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
3645a0e240fSJohn Levon {"read_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
3655a0e240fSJohn Levon {"_read_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
3665a0e240fSJohn Levon {"_read_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
3675a0e240fSJohn Levon {"__read_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
3685a0e240fSJohn Levon {"__read_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
3695a0e240fSJohn Levon {"_raw_read_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
3705a0e240fSJohn Levon {"_raw_read_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
3715a0e240fSJohn Levon {"write_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
3725a0e240fSJohn Levon {"write_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
3735a0e240fSJohn Levon {"_write_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
3745a0e240fSJohn Levon {"_write_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
3755a0e240fSJohn Levon {"__write_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
3765a0e240fSJohn Levon {"__write_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any},
3775a0e240fSJohn Levon {"_raw_write_lock_bh", LOCK, bottom_half, NO_ARG, ret_any},
3785a0e240fSJohn Levon {"_raw_write_unlock_bh",UNLOCK, bottom_half, NO_ARG, ret_any},
3795a0e240fSJohn Levon {"spin_trylock_bh", LOCK, bottom_half, NO_ARG, ret_one},
3805a0e240fSJohn Levon {"_spin_trylock_bh", LOCK, bottom_half, NO_ARG, ret_one},
3815a0e240fSJohn Levon {"__spin_trylock_bh", LOCK, bottom_half, NO_ARG, ret_one},
3825a0e240fSJohn Levon
3835a0e240fSJohn Levon {"ffs_mutex_lock", LOCK, mutex, 0, ret_zero},
3845a0e240fSJohn Levon
3855a0e240fSJohn Levon {"clk_prepare_lock", LOCK, prepare_lock, NO_ARG, ret_any},
3865a0e240fSJohn Levon {"clk_prepare_unlock", UNLOCK, prepare_lock, NO_ARG, ret_any},
3875a0e240fSJohn Levon {"clk_enable_lock", LOCK, enable_lock, -1, ret_any},
3885a0e240fSJohn Levon {"clk_enable_unlock", UNLOCK, enable_lock, 0, ret_any},
3895a0e240fSJohn Levon
3905a0e240fSJohn Levon {"dma_resv_lock", LOCK, mutex, 0, ret_zero},
3915a0e240fSJohn Levon {"dma_resv_trylock", LOCK, mutex, 0, ret_one},
3925a0e240fSJohn Levon {"dma_resv_lock_interruptible", LOCK, mutex, 0, ret_zero},
3935a0e240fSJohn Levon {"dma_resv_unlock", UNLOCK, mutex, 0, ret_any},
3945a0e240fSJohn Levon
3955a0e240fSJohn Levon {"modeset_lock", LOCK, mutex, 0, ret_zero},
3965a0e240fSJohn Levon {"drm_ modeset_lock", LOCK, mutex, 0, ret_zero},
3975a0e240fSJohn Levon {"drm_modeset_lock_single_interruptible", LOCK, mutex, 0, ret_zero},
3985a0e240fSJohn Levon {"modeset_unlock", UNLOCK, mutex, 0, ret_any},
3995a0e240fSJohn Levon
4005a0e240fSJohn Levon {"reiserfs_write_lock_nested", LOCK, mutex, 0, ret_any},
4015a0e240fSJohn Levon {"reiserfs_write_unlock_nested", UNLOCK, mutex, 0, ret_any},
4025a0e240fSJohn Levon
4035a0e240fSJohn Levon {"rw_lock", LOCK, write_lock, 1, ret_any},
4045a0e240fSJohn Levon {"rw_unlock", UNLOCK, write_lock, 1, ret_any},
4055a0e240fSJohn Levon
4065a0e240fSJohn Levon {"sem_lock", LOCK, mutex, 0, ret_any},
4075a0e240fSJohn Levon {"sem_unlock", UNLOCK, mutex, 0, ret_any},
4085a0e240fSJohn Levon
4095a0e240fSJohn Levon {},
4101f5207b7SJohn Levon };
4111f5207b7SJohn Levon
4125a0e240fSJohn Levon struct macro_info {
4135a0e240fSJohn Levon const char *macro;
4145a0e240fSJohn Levon enum action action;
4155a0e240fSJohn Levon int param;
4161f5207b7SJohn Levon };
4171f5207b7SJohn Levon
4185a0e240fSJohn Levon static struct macro_info macro_table[] = {
4195a0e240fSJohn Levon {"genpd_lock", LOCK, 0},
4205a0e240fSJohn Levon {"genpd_lock_nested", LOCK, 0},
4215a0e240fSJohn Levon {"genpd_lock_interruptible", LOCK, 0},
4225a0e240fSJohn Levon {"genpd_unlock", UNLOCK, 0},
4235a0e240fSJohn Levon };
4241f5207b7SJohn Levon
4255a0e240fSJohn Levon static const char *false_positives[][2] = {
4265a0e240fSJohn Levon {"fs/jffs2/", "->alloc_sem"},
4275a0e240fSJohn Levon {"fs/xfs/", "->b_sema"},
4285a0e240fSJohn Levon {"mm/", "pvmw->ptl"},
4291f5207b7SJohn Levon };
4301f5207b7SJohn Levon
4315a0e240fSJohn Levon static struct stree *start_states;
4325a0e240fSJohn Levon static struct stree_stack *saved_stack;
4331f5207b7SJohn Levon
4345a0e240fSJohn Levon static struct tracker_list *locks;
4355a0e240fSJohn Levon
reset(struct sm_state * sm,struct expression * mod_expr)4365a0e240fSJohn Levon static void reset(struct sm_state *sm, struct expression *mod_expr)
4375a0e240fSJohn Levon {
4385a0e240fSJohn Levon set_state(my_id, sm->name, sm->sym, &start_state);
4391f5207b7SJohn Levon }
4401f5207b7SJohn Levon
remove_spinlock_check(struct expression * expr)4411f5207b7SJohn Levon static struct expression *remove_spinlock_check(struct expression *expr)
4421f5207b7SJohn Levon {
4431f5207b7SJohn Levon if (expr->type != EXPR_CALL)
4441f5207b7SJohn Levon return expr;
4451f5207b7SJohn Levon if (expr->fn->type != EXPR_SYMBOL)
4461f5207b7SJohn Levon return expr;
4471f5207b7SJohn Levon if (strcmp(expr->fn->symbol_name->name, "spinlock_check"))
4481f5207b7SJohn Levon return expr;
4491f5207b7SJohn Levon expr = get_argument_from_call_expr(expr->args, 0);
4501f5207b7SJohn Levon return expr;
4511f5207b7SJohn Levon }
4521f5207b7SJohn Levon
filter_kernel_args(struct expression * arg)4535a0e240fSJohn Levon static struct expression *filter_kernel_args(struct expression *arg)
4545a0e240fSJohn Levon {
4555a0e240fSJohn Levon if (arg->type == EXPR_PREOP && arg->op == '&')
4565a0e240fSJohn Levon return strip_expr(arg->unop);
4575a0e240fSJohn Levon if (!is_pointer(arg))
4585a0e240fSJohn Levon return arg;
4595a0e240fSJohn Levon return deref_expression(strip_expr(arg));
4605a0e240fSJohn Levon }
4615a0e240fSJohn Levon
lock_to_name_sym(struct expression * expr,struct symbol ** sym)4625a0e240fSJohn Levon static char *lock_to_name_sym(struct expression *expr, struct symbol **sym)
4635a0e240fSJohn Levon {
4645a0e240fSJohn Levon expr = remove_spinlock_check(expr);
4655a0e240fSJohn Levon expr = filter_kernel_args(expr);
4665a0e240fSJohn Levon return expr_to_str_sym(expr, sym);
4675a0e240fSJohn Levon }
4685a0e240fSJohn Levon
get_full_name(struct expression * expr,int index,struct symbol ** sym)4695a0e240fSJohn Levon static char *get_full_name(struct expression *expr, int index, struct symbol **sym)
4701f5207b7SJohn Levon {
4711f5207b7SJohn Levon struct lock_info *lock = &lock_table[index];
4725a0e240fSJohn Levon struct expression *arg;
4731f5207b7SJohn Levon
4745a0e240fSJohn Levon *sym = NULL;
4751f5207b7SJohn Levon if (lock->arg == RETURN_VAL) {
4765a0e240fSJohn Levon return expr_to_var_sym(strip_expr(expr->left), sym);
4771f5207b7SJohn Levon } else if (lock->arg == NO_ARG) {
4785a0e240fSJohn Levon return alloc_string(get_lock_name(lock->type));
4791f5207b7SJohn Levon } else {
4801f5207b7SJohn Levon arg = get_argument_from_call_expr(expr->args, lock->arg);
4811f5207b7SJohn Levon if (!arg)
4825a0e240fSJohn Levon return NULL;
4835a0e240fSJohn Levon return lock_to_name_sym(arg, sym);
4841f5207b7SJohn Levon }
4851f5207b7SJohn Levon }
4861f5207b7SJohn Levon
unmatched_state(struct sm_state * sm)4875a0e240fSJohn Levon static struct smatch_state *unmatched_state(struct sm_state *sm)
4885a0e240fSJohn Levon {
4895a0e240fSJohn Levon return &start_state;
4905a0e240fSJohn Levon }
4915a0e240fSJohn Levon
pre_merge_hook(struct sm_state * cur,struct sm_state * other)4925a0e240fSJohn Levon static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
4931f5207b7SJohn Levon {
4945a0e240fSJohn Levon if (is_impossible_path())
4955a0e240fSJohn Levon set_state(my_id, cur->name, cur->sym, &impossible);
4965a0e240fSJohn Levon }
4971f5207b7SJohn Levon
merge_func(struct smatch_state * s1,struct smatch_state * s2)4985a0e240fSJohn Levon static struct smatch_state *merge_func(struct smatch_state *s1, struct smatch_state *s2)
4995a0e240fSJohn Levon {
5005a0e240fSJohn Levon if (s1 == &impossible)
5015a0e240fSJohn Levon return s2;
5025a0e240fSJohn Levon if (s2 == &impossible)
5035a0e240fSJohn Levon return s1;
5045a0e240fSJohn Levon return &merged;
5055a0e240fSJohn Levon }
5065a0e240fSJohn Levon
action_to_state(enum action lock_unlock)5075a0e240fSJohn Levon static struct smatch_state *action_to_state(enum action lock_unlock)
5085a0e240fSJohn Levon {
5095a0e240fSJohn Levon switch (lock_unlock) {
5105a0e240fSJohn Levon case LOCK:
5111f5207b7SJohn Levon return &locked;
5125a0e240fSJohn Levon case UNLOCK:
5131f5207b7SJohn Levon return &unlocked;
5145a0e240fSJohn Levon case RESTORE:
5155a0e240fSJohn Levon return &restore;
5165a0e240fSJohn Levon }
5175a0e240fSJohn Levon return NULL;
5181f5207b7SJohn Levon }
5191f5207b7SJohn Levon
get_best_match(const char * key,enum action lock_unlock)5205a0e240fSJohn Levon static struct sm_state *get_best_match(const char *key, enum action lock_unlock)
5211f5207b7SJohn Levon {
5225a0e240fSJohn Levon struct sm_state *sm;
5235a0e240fSJohn Levon struct sm_state *match;
5245a0e240fSJohn Levon int cnt = 0;
5255a0e240fSJohn Levon int start_pos, state_len, key_len, chunks, i;
5265a0e240fSJohn Levon
5275a0e240fSJohn Levon if (strncmp(key, "$->", 3) == 0)
5285a0e240fSJohn Levon key += 3;
5295a0e240fSJohn Levon
5305a0e240fSJohn Levon key_len = strlen(key);
5315a0e240fSJohn Levon chunks = 0;
5325a0e240fSJohn Levon for (i = key_len - 1; i > 0; i--) {
5335a0e240fSJohn Levon if (key[i] == '>' || key[i] == '.')
5345a0e240fSJohn Levon chunks++;
5355a0e240fSJohn Levon if (chunks == 2) {
5365a0e240fSJohn Levon key += (i + 1);
5375a0e240fSJohn Levon key_len = strlen(key);
5385a0e240fSJohn Levon break;
5395a0e240fSJohn Levon }
5405a0e240fSJohn Levon }
5415a0e240fSJohn Levon
5425a0e240fSJohn Levon FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
5435a0e240fSJohn Levon if (((lock_unlock == UNLOCK || lock_unlock == RESTORE) &&
5445a0e240fSJohn Levon sm->state != &locked) ||
5455a0e240fSJohn Levon (lock_unlock == LOCK && sm->state != &unlocked))
5465a0e240fSJohn Levon continue;
5475a0e240fSJohn Levon state_len = strlen(sm->name);
5485a0e240fSJohn Levon if (state_len < key_len)
5495a0e240fSJohn Levon continue;
5505a0e240fSJohn Levon start_pos = state_len - key_len;
5515a0e240fSJohn Levon if ((start_pos == 0 || !isalnum(sm->name[start_pos - 1])) &&
5525a0e240fSJohn Levon strcmp(sm->name + start_pos, key) == 0) {
5535a0e240fSJohn Levon cnt++;
5545a0e240fSJohn Levon match = sm;
5555a0e240fSJohn Levon }
5565a0e240fSJohn Levon } END_FOR_EACH_SM(sm);
5575a0e240fSJohn Levon
5585a0e240fSJohn Levon if (cnt == 1)
5595a0e240fSJohn Levon return match;
5605a0e240fSJohn Levon return NULL;
5611f5207b7SJohn Levon }
5621f5207b7SJohn Levon
use_best_match(char * key,enum action lock_unlock)5635a0e240fSJohn Levon static void use_best_match(char *key, enum action lock_unlock)
5641f5207b7SJohn Levon {
5655a0e240fSJohn Levon struct sm_state *match;
5665a0e240fSJohn Levon
5675a0e240fSJohn Levon match = get_best_match(key, lock_unlock);
5685a0e240fSJohn Levon if (match)
5695a0e240fSJohn Levon set_state(my_id, match->name, match->sym, action_to_state(lock_unlock));
5705a0e240fSJohn Levon else
5715a0e240fSJohn Levon set_state(my_id, key, NULL, action_to_state(lock_unlock));
5721f5207b7SJohn Levon }
5731f5207b7SJohn Levon
set_start_state(const char * name,struct symbol * sym,struct smatch_state * start)5745a0e240fSJohn Levon static void set_start_state(const char *name, struct symbol *sym, struct smatch_state *start)
575efe51d0cSJohn Levon {
5765a0e240fSJohn Levon struct smatch_state *orig;
5775a0e240fSJohn Levon
5785a0e240fSJohn Levon orig = get_state_stree(start_states, my_id, name, sym);
5795a0e240fSJohn Levon if (!orig)
5805a0e240fSJohn Levon set_state_stree(&start_states, my_id, name, sym, start);
5815a0e240fSJohn Levon else if (orig != start)
5825a0e240fSJohn Levon set_state_stree(&start_states, my_id, name, sym, &undefined);
5835a0e240fSJohn Levon }
5845a0e240fSJohn Levon
common_false_positive(const char * name)5855a0e240fSJohn Levon static bool common_false_positive(const char *name)
5865a0e240fSJohn Levon {
5875a0e240fSJohn Levon const char *path, *lname;
5885a0e240fSJohn Levon int i, len_total, len_path, len_name, skip;
5895a0e240fSJohn Levon
5905a0e240fSJohn Levon if (!get_filename())
5915a0e240fSJohn Levon return false;
5925a0e240fSJohn Levon
5935a0e240fSJohn Levon len_total = strlen(name);
5945a0e240fSJohn Levon for (i = 0; i < ARRAY_SIZE(false_positives); i++) {
5955a0e240fSJohn Levon path = false_positives[i][0];
5965a0e240fSJohn Levon lname = false_positives[i][1];
5975a0e240fSJohn Levon
5985a0e240fSJohn Levon len_path = strlen(path);
5995a0e240fSJohn Levon len_name = strlen(lname);
6005a0e240fSJohn Levon
6015a0e240fSJohn Levon if (len_name > len_total)
6025a0e240fSJohn Levon continue;
6035a0e240fSJohn Levon skip = len_total - len_name;
6045a0e240fSJohn Levon
6055a0e240fSJohn Levon if (strncmp(get_filename(), path, len_path) == 0 &&
6065a0e240fSJohn Levon strcmp(name + skip, lname) == 0)
6075a0e240fSJohn Levon return true;
6085a0e240fSJohn Levon }
6095a0e240fSJohn Levon
610efe51d0cSJohn Levon return false;
611efe51d0cSJohn Levon }
612efe51d0cSJohn Levon
warn_on_double(struct sm_state * sm,struct smatch_state * state)6135a0e240fSJohn Levon static void warn_on_double(struct sm_state *sm, struct smatch_state *state)
6141f5207b7SJohn Levon {
6155a0e240fSJohn Levon struct sm_state *tmp;
6161f5207b7SJohn Levon
6175a0e240fSJohn Levon if (!sm)
6181f5207b7SJohn Levon return;
6191f5207b7SJohn Levon
6205a0e240fSJohn Levon FOR_EACH_PTR(sm->possible, tmp) {
6215a0e240fSJohn Levon if (tmp->state == state)
6225a0e240fSJohn Levon goto found;
6235a0e240fSJohn Levon } END_FOR_EACH_PTR(tmp);
6245a0e240fSJohn Levon
6255a0e240fSJohn Levon return;
6265a0e240fSJohn Levon found:
6275a0e240fSJohn Levon if (strcmp(sm->name, "bottom_half") == 0)
6285a0e240fSJohn Levon return;
6295a0e240fSJohn Levon if (common_false_positive(sm->name))
6305a0e240fSJohn Levon return;
6315a0e240fSJohn Levon sm_msg("error: double %s '%s' (orig line %u)",
6325a0e240fSJohn Levon state->name, sm->name, tmp->line);
6331f5207b7SJohn Levon }
6341f5207b7SJohn Levon
handle_macro_lock_unlock(void)6355a0e240fSJohn Levon static bool handle_macro_lock_unlock(void)
6361f5207b7SJohn Levon {
6375a0e240fSJohn Levon struct expression *expr, *arg;
6385a0e240fSJohn Levon struct macro_info *info;
6391f5207b7SJohn Levon struct sm_state *sm;
6405a0e240fSJohn Levon struct symbol *sym;
6415a0e240fSJohn Levon const char *macro;
6425a0e240fSJohn Levon char *name;
6435a0e240fSJohn Levon bool ret = false;
6445a0e240fSJohn Levon int i;
6451f5207b7SJohn Levon
6465a0e240fSJohn Levon expr = last_ptr_list((struct ptr_list *)big_expression_stack);
6475a0e240fSJohn Levon while (expr && expr->type == EXPR_ASSIGNMENT)
6485a0e240fSJohn Levon expr = strip_expr(expr->right);
6495a0e240fSJohn Levon if (!expr || expr->type != EXPR_CALL)
6505a0e240fSJohn Levon return false;
6515a0e240fSJohn Levon
6525a0e240fSJohn Levon macro = get_macro_name(expr->pos);
6535a0e240fSJohn Levon if (!macro)
6545a0e240fSJohn Levon return false;
6555a0e240fSJohn Levon
6565a0e240fSJohn Levon for (i = 0; i < ARRAY_SIZE(macro_table); i++) {
6575a0e240fSJohn Levon info = ¯o_table[i];
6585a0e240fSJohn Levon
6595a0e240fSJohn Levon if (strcmp(macro, info->macro) != 0)
6605a0e240fSJohn Levon continue;
6615a0e240fSJohn Levon arg = get_argument_from_call_expr(expr->args, info->param);
6625a0e240fSJohn Levon name = expr_to_str_sym(arg, &sym);
6635a0e240fSJohn Levon if (!name || !sym)
6645a0e240fSJohn Levon goto free;
6655a0e240fSJohn Levon sm = get_sm_state(my_id, name, sym);
6665a0e240fSJohn Levon
6675a0e240fSJohn Levon if (info->action == LOCK) {
6685a0e240fSJohn Levon if (!sm)
6695a0e240fSJohn Levon set_start_state(name, sym, &unlocked);
6705a0e240fSJohn Levon if (sm && sm->line != expr->pos.line)
6715a0e240fSJohn Levon warn_on_double(sm, &locked);
6725a0e240fSJohn Levon set_state(my_id, name, sym, &locked);
6735a0e240fSJohn Levon } else {
6745a0e240fSJohn Levon if (!sm)
6755a0e240fSJohn Levon set_start_state(name, sym, &locked);
6765a0e240fSJohn Levon if (sm && sm->line != expr->pos.line)
6775a0e240fSJohn Levon warn_on_double(sm, &unlocked);
6785a0e240fSJohn Levon set_state(my_id, name, sym, &unlocked);
6795a0e240fSJohn Levon }
6805a0e240fSJohn Levon ret = true;
6815a0e240fSJohn Levon free:
6825a0e240fSJohn Levon free_string(name);
6835a0e240fSJohn Levon return ret;
6845a0e240fSJohn Levon }
6855a0e240fSJohn Levon return false;
6865a0e240fSJohn Levon }
6875a0e240fSJohn Levon
do_lock(const char * name,struct symbol * sym,struct lock_info * info)6885a0e240fSJohn Levon static void do_lock(const char *name, struct symbol *sym, struct lock_info *info)
6895a0e240fSJohn Levon {
6905a0e240fSJohn Levon struct sm_state *sm;
6915a0e240fSJohn Levon
6925a0e240fSJohn Levon if (handle_macro_lock_unlock())
6931f5207b7SJohn Levon return;
6941f5207b7SJohn Levon
6955a0e240fSJohn Levon add_tracker(&locks, my_id, name, sym);
6965a0e240fSJohn Levon
6975a0e240fSJohn Levon sm = get_sm_state(my_id, name, sym);
6981f5207b7SJohn Levon if (!sm)
6995a0e240fSJohn Levon set_start_state(name, sym, &unlocked);
7005a0e240fSJohn Levon warn_on_double(sm, &locked);
7015a0e240fSJohn Levon set_state(my_id, name, sym, &locked);
7025a0e240fSJohn Levon }
7035a0e240fSJohn Levon
do_lock_failed(const char * name,struct symbol * sym)7045a0e240fSJohn Levon static void do_lock_failed(const char *name, struct symbol *sym)
7055a0e240fSJohn Levon {
7065a0e240fSJohn Levon add_tracker(&locks, my_id, name, sym);
7075a0e240fSJohn Levon set_state(my_id, name, sym, &unlocked);
7081f5207b7SJohn Levon }
7091f5207b7SJohn Levon
do_unlock(const char * name,struct symbol * sym,struct lock_info * info)7105a0e240fSJohn Levon static void do_unlock(const char *name, struct symbol *sym, struct lock_info *info)
7111f5207b7SJohn Levon {
7121f5207b7SJohn Levon struct sm_state *sm;
7131f5207b7SJohn Levon
7141f5207b7SJohn Levon if (__path_is_null())
7151f5207b7SJohn Levon return;
7165a0e240fSJohn Levon
7175a0e240fSJohn Levon if (handle_macro_lock_unlock())
7185a0e240fSJohn Levon return;
7195a0e240fSJohn Levon
7205a0e240fSJohn Levon add_tracker(&locks, my_id, name, sym);
7215a0e240fSJohn Levon sm = get_sm_state(my_id, name, sym);
7225a0e240fSJohn Levon if (!sm) {
7235a0e240fSJohn Levon sm = get_best_match(name, UNLOCK);
7245a0e240fSJohn Levon if (sm) {
7255a0e240fSJohn Levon name = sm->name;
7265a0e240fSJohn Levon sym = sm->sym;
7275a0e240fSJohn Levon }
7285a0e240fSJohn Levon }
7291f5207b7SJohn Levon if (!sm)
7305a0e240fSJohn Levon set_start_state(name, sym, &locked);
7315a0e240fSJohn Levon warn_on_double(sm, &unlocked);
7325a0e240fSJohn Levon set_state(my_id, name, sym, &unlocked);
7335a0e240fSJohn Levon }
7345a0e240fSJohn Levon
do_restore(const char * name,struct symbol * sym,struct lock_info * info)7355a0e240fSJohn Levon static void do_restore(const char *name, struct symbol *sym, struct lock_info *info)
7365a0e240fSJohn Levon {
7375a0e240fSJohn Levon if (__path_is_null())
7385a0e240fSJohn Levon return;
7395a0e240fSJohn Levon
7405a0e240fSJohn Levon if (!get_state(my_id, name, sym))
7415a0e240fSJohn Levon set_start_state(name, sym, &locked);
7425a0e240fSJohn Levon
7435a0e240fSJohn Levon add_tracker(&locks, my_id, name, sym);
7445a0e240fSJohn Levon set_state(my_id, name, sym, &restore);
7451f5207b7SJohn Levon }
7461f5207b7SJohn Levon
match_lock_held(const char * fn,struct expression * call_expr,struct expression * assign_expr,void * _index)7471f5207b7SJohn Levon static void match_lock_held(const char *fn, struct expression *call_expr,
7485a0e240fSJohn Levon struct expression *assign_expr, void *_index)
7491f5207b7SJohn Levon {
7501f5207b7SJohn Levon int index = PTR_INT(_index);
7511f5207b7SJohn Levon struct lock_info *lock = &lock_table[index];
7525a0e240fSJohn Levon char *lock_name;
7535a0e240fSJohn Levon struct symbol *sym;
7541f5207b7SJohn Levon
7551f5207b7SJohn Levon if (lock->arg == NO_ARG) {
7565a0e240fSJohn Levon lock_name = get_full_name(NULL, index, &sym);
7571f5207b7SJohn Levon } else if (lock->arg == RETURN_VAL) {
7581f5207b7SJohn Levon if (!assign_expr)
7591f5207b7SJohn Levon return;
7605a0e240fSJohn Levon lock_name = get_full_name(assign_expr, index, &sym);
7611f5207b7SJohn Levon } else {
7625a0e240fSJohn Levon lock_name = get_full_name(call_expr, index, &sym);
7631f5207b7SJohn Levon }
7641f5207b7SJohn Levon if (!lock_name)
7651f5207b7SJohn Levon return;
7665a0e240fSJohn Levon do_lock(lock_name, sym, lock);
7671f5207b7SJohn Levon free_string(lock_name);
7681f5207b7SJohn Levon }
7691f5207b7SJohn Levon
match_lock_failed(const char * fn,struct expression * call_expr,struct expression * assign_expr,void * _index)7701f5207b7SJohn Levon static void match_lock_failed(const char *fn, struct expression *call_expr,
7711f5207b7SJohn Levon struct expression *assign_expr, void *_index)
7721f5207b7SJohn Levon {
7731f5207b7SJohn Levon int index = PTR_INT(_index);
7741f5207b7SJohn Levon struct lock_info *lock = &lock_table[index];
7755a0e240fSJohn Levon char *lock_name;
7765a0e240fSJohn Levon struct symbol *sym;
7771f5207b7SJohn Levon
7781f5207b7SJohn Levon if (lock->arg == NO_ARG) {
7795a0e240fSJohn Levon lock_name = get_full_name(NULL, index, &sym);
7801f5207b7SJohn Levon } else if (lock->arg == RETURN_VAL) {
7811f5207b7SJohn Levon if (!assign_expr)
7821f5207b7SJohn Levon return;
7835a0e240fSJohn Levon lock_name = get_full_name(assign_expr, index, &sym);
7841f5207b7SJohn Levon } else {
7855a0e240fSJohn Levon lock_name = get_full_name(call_expr, index, &sym);
7861f5207b7SJohn Levon }
7871f5207b7SJohn Levon if (!lock_name)
7881f5207b7SJohn Levon return;
7895a0e240fSJohn Levon do_lock_failed(lock_name, sym);
7901f5207b7SJohn Levon free_string(lock_name);
7911f5207b7SJohn Levon }
7921f5207b7SJohn Levon
match_returns_locked(const char * fn,struct expression * expr,void * _index)7931f5207b7SJohn Levon static void match_returns_locked(const char *fn, struct expression *expr,
7941f5207b7SJohn Levon void *_index)
7951f5207b7SJohn Levon {
7961f5207b7SJohn Levon int index = PTR_INT(_index);
7971f5207b7SJohn Levon struct lock_info *lock = &lock_table[index];
7985a0e240fSJohn Levon char *full_name;
7995a0e240fSJohn Levon struct symbol *sym;
8001f5207b7SJohn Levon
8011f5207b7SJohn Levon if (lock->arg != RETURN_VAL)
8021f5207b7SJohn Levon return;
8035a0e240fSJohn Levon full_name = get_full_name(expr, index, &sym);
8045a0e240fSJohn Levon if (!full_name)
8055a0e240fSJohn Levon return;
8065a0e240fSJohn Levon do_lock(full_name, sym, lock);
8071f5207b7SJohn Levon }
8081f5207b7SJohn Levon
match_lock_unlock(const char * fn,struct expression * expr,void * _index)8091f5207b7SJohn Levon static void match_lock_unlock(const char *fn, struct expression *expr, void *_index)
8101f5207b7SJohn Levon {
8111f5207b7SJohn Levon int index = PTR_INT(_index);
8121f5207b7SJohn Levon struct lock_info *lock = &lock_table[index];
8135a0e240fSJohn Levon char *full_name;
8145a0e240fSJohn Levon struct symbol *sym;
8151f5207b7SJohn Levon
8165a0e240fSJohn Levon full_name = get_full_name(expr, index, &sym);
8171f5207b7SJohn Levon if (!full_name)
8181f5207b7SJohn Levon return;
8195a0e240fSJohn Levon switch (lock->action) {
8205a0e240fSJohn Levon case LOCK:
8215a0e240fSJohn Levon do_lock(full_name, sym, lock);
8225a0e240fSJohn Levon break;
8235a0e240fSJohn Levon case UNLOCK:
8245a0e240fSJohn Levon do_unlock(full_name, sym, lock);
8255a0e240fSJohn Levon break;
8265a0e240fSJohn Levon case RESTORE:
8275a0e240fSJohn Levon do_restore(full_name, sym, lock);
8285a0e240fSJohn Levon break;
8295a0e240fSJohn Levon }
8301f5207b7SJohn Levon free_string(full_name);
8311f5207b7SJohn Levon }
8321f5207b7SJohn Levon
get_start_state(struct sm_state * sm)8335a0e240fSJohn Levon static struct smatch_state *get_start_state(struct sm_state *sm)
8341f5207b7SJohn Levon {
8355a0e240fSJohn Levon struct smatch_state *orig;
8361f5207b7SJohn Levon
8375a0e240fSJohn Levon orig = get_state_stree(start_states, my_id, sm->name, sm->sym);
8385a0e240fSJohn Levon if (orig)
8395a0e240fSJohn Levon return orig;
8405a0e240fSJohn Levon return &undefined;
8411f5207b7SJohn Levon }
8421f5207b7SJohn Levon
get_param_lock_name(struct sm_state * sm,struct expression * expr,const char ** name)8435a0e240fSJohn Levon static int get_param_lock_name(struct sm_state *sm, struct expression *expr,
8445a0e240fSJohn Levon const char **name)
8451f5207b7SJohn Levon {
8465a0e240fSJohn Levon char *other_name;
8475a0e240fSJohn Levon struct symbol *other_sym;
8485a0e240fSJohn Levon const char *param_name;
8495a0e240fSJohn Levon int param;
8505a0e240fSJohn Levon
8515a0e240fSJohn Levon *name = sm->name;
8525a0e240fSJohn Levon
8535a0e240fSJohn Levon param = get_param_num_from_sym(sm->sym);
8545a0e240fSJohn Levon if (param >= 0) {
8555a0e240fSJohn Levon param_name = get_param_name(sm);
8565a0e240fSJohn Levon if (param_name)
8575a0e240fSJohn Levon *name = param_name;
8585a0e240fSJohn Levon return param;
8595a0e240fSJohn Levon }
8601f5207b7SJohn Levon
8615a0e240fSJohn Levon if (expr) {
8625a0e240fSJohn Levon struct symbol *ret_sym;
8635a0e240fSJohn Levon char *ret_str;
8645a0e240fSJohn Levon
8655a0e240fSJohn Levon ret_str = expr_to_str_sym(expr, &ret_sym);
8665a0e240fSJohn Levon if (ret_str && ret_sym == sm->sym) {
8675a0e240fSJohn Levon param_name = state_name_to_param_name(sm->name, ret_str);
8685a0e240fSJohn Levon if (param_name) {
8695a0e240fSJohn Levon free_string(ret_str);
8705a0e240fSJohn Levon *name = param_name;
8715a0e240fSJohn Levon return -1;
8725a0e240fSJohn Levon }
8731f5207b7SJohn Levon }
8745a0e240fSJohn Levon free_string(ret_str);
8751f5207b7SJohn Levon }
8761f5207b7SJohn Levon
8775a0e240fSJohn Levon other_name = get_other_name_sym(sm->name, sm->sym, &other_sym);
8785a0e240fSJohn Levon if (!other_name)
8795a0e240fSJohn Levon return -2;
8805a0e240fSJohn Levon param = get_param_num_from_sym(other_sym);
8815a0e240fSJohn Levon if (param < 0)
8825a0e240fSJohn Levon return -2;
8835a0e240fSJohn Levon
8845a0e240fSJohn Levon param_name = get_param_name_var_sym(other_name, other_sym);
8855a0e240fSJohn Levon free_string(other_name);
8865a0e240fSJohn Levon if (param_name)
8875a0e240fSJohn Levon *name = param_name;
8885a0e240fSJohn Levon return param;
8895a0e240fSJohn Levon }
8901f5207b7SJohn Levon
get_db_type(struct sm_state * sm)8915a0e240fSJohn Levon static int get_db_type(struct sm_state *sm)
8921f5207b7SJohn Levon {
8935a0e240fSJohn Levon if (sm->state == get_start_state(sm)) {
8945a0e240fSJohn Levon if (sm->state == &locked)
8955a0e240fSJohn Levon return KNOWN_LOCKED;
8965a0e240fSJohn Levon if (sm->state == &unlocked)
8975a0e240fSJohn Levon return KNOWN_UNLOCKED;
8985a0e240fSJohn Levon }
8991f5207b7SJohn Levon
9005a0e240fSJohn Levon if (sm->state == &locked)
9015a0e240fSJohn Levon return LOCKED;
9025a0e240fSJohn Levon if (sm->state == &unlocked)
9035a0e240fSJohn Levon return UNLOCKED;
9045a0e240fSJohn Levon if (sm->state == &restore)
9055a0e240fSJohn Levon return LOCK_RESTORED;
9065a0e240fSJohn Levon return LOCKED;
9075a0e240fSJohn Levon }
9081f5207b7SJohn Levon
match_return_info(int return_id,char * return_ranges,struct expression * expr)9095a0e240fSJohn Levon static void match_return_info(int return_id, char *return_ranges, struct expression *expr)
9105a0e240fSJohn Levon {
9115a0e240fSJohn Levon struct sm_state *sm;
9125a0e240fSJohn Levon const char *param_name;
9135a0e240fSJohn Levon int param;
9141f5207b7SJohn Levon
9155a0e240fSJohn Levon FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
9165a0e240fSJohn Levon if (sm->state != &locked &&
9175a0e240fSJohn Levon sm->state != &unlocked &&
9185a0e240fSJohn Levon sm->state != &restore)
9195a0e240fSJohn Levon continue;
9201f5207b7SJohn Levon
9215a0e240fSJohn Levon param = get_param_lock_name(sm, expr, ¶m_name);
9225a0e240fSJohn Levon sql_insert_return_states(return_id, return_ranges,
9235a0e240fSJohn Levon get_db_type(sm),
9245a0e240fSJohn Levon param, param_name, "");
9255a0e240fSJohn Levon } END_FOR_EACH_SM(sm);
9261f5207b7SJohn Levon }
9271f5207b7SJohn Levon
9285a0e240fSJohn Levon enum {
9295a0e240fSJohn Levon ERR_PTR, VALID_PTR, NEGATIVE, ZERO, POSITIVE, NUM_BUCKETS,
9305a0e240fSJohn Levon };
9315a0e240fSJohn Levon
is_EINTR(struct range_list * rl)9325a0e240fSJohn Levon static bool is_EINTR(struct range_list *rl)
9331f5207b7SJohn Levon {
9345a0e240fSJohn Levon sval_t sval;
9351f5207b7SJohn Levon
9365a0e240fSJohn Levon if (!rl_to_sval(rl, &sval))
9375a0e240fSJohn Levon return false;
9385a0e240fSJohn Levon return sval.value == -4;
9391f5207b7SJohn Levon }
9401f5207b7SJohn Levon
success_fail_positive(struct range_list * rl)9415a0e240fSJohn Levon static int success_fail_positive(struct range_list *rl)
9421f5207b7SJohn Levon {
9435a0e240fSJohn Levon /* void returns are the same as success (zero in the kernel) */
9445a0e240fSJohn Levon if (!rl)
9455a0e240fSJohn Levon return ZERO;
9465a0e240fSJohn Levon
9475a0e240fSJohn Levon if (rl_type(rl)->type != SYM_PTR && sval_is_negative(rl_min(rl)))
9485a0e240fSJohn Levon return NEGATIVE;
9495a0e240fSJohn Levon
9505a0e240fSJohn Levon if (rl_min(rl).value == 0 && rl_max(rl).value == 0)
9515a0e240fSJohn Levon return ZERO;
9525a0e240fSJohn Levon
9535a0e240fSJohn Levon if (is_err_ptr(rl_min(rl)) &&
9545a0e240fSJohn Levon is_err_ptr(rl_max(rl)))
9555a0e240fSJohn Levon return ERR_PTR;
9565a0e240fSJohn Levon
9575a0e240fSJohn Levon /*
9585a0e240fSJohn Levon * Trying to match ERR_PTR(ret) but without the expression struct.
9595a0e240fSJohn Levon * Ugly...
9605a0e240fSJohn Levon */
9615a0e240fSJohn Levon if (type_bits(&long_ctype) == 64 &&
9625a0e240fSJohn Levon rl_type(rl)->type == SYM_PTR &&
9635a0e240fSJohn Levon rl_min(rl).value == INT_MIN)
9645a0e240fSJohn Levon return ERR_PTR;
9655a0e240fSJohn Levon
9665a0e240fSJohn Levon return POSITIVE;
9675a0e240fSJohn Levon }
9685a0e240fSJohn Levon
sym_in_lock_table(struct symbol * sym)9695a0e240fSJohn Levon static bool sym_in_lock_table(struct symbol *sym)
9705a0e240fSJohn Levon {
9715a0e240fSJohn Levon int i;
9721f5207b7SJohn Levon
9735a0e240fSJohn Levon if (!sym || !sym->ident)
9745a0e240fSJohn Levon return false;
9755a0e240fSJohn Levon
9765a0e240fSJohn Levon for (i = 0; lock_table[i].function != NULL; i++) {
9775a0e240fSJohn Levon if (strcmp(lock_table[i].function, sym->ident->name) == 0)
9785a0e240fSJohn Levon return true;
9795a0e240fSJohn Levon }
9805a0e240fSJohn Levon return false;
9811f5207b7SJohn Levon }
9821f5207b7SJohn Levon
func_in_lock_table(struct expression * expr)9835a0e240fSJohn Levon static bool func_in_lock_table(struct expression *expr)
9841f5207b7SJohn Levon {
9855a0e240fSJohn Levon if (expr->type != EXPR_SYMBOL)
9865a0e240fSJohn Levon return false;
9875a0e240fSJohn Levon return sym_in_lock_table(expr->symbol);
9885a0e240fSJohn Levon }
9895a0e240fSJohn Levon
check_lock(char * name,struct symbol * sym)9905a0e240fSJohn Levon static void check_lock(char *name, struct symbol *sym)
9915a0e240fSJohn Levon {
9925a0e240fSJohn Levon struct range_list *locked_lines = NULL;
9935a0e240fSJohn Levon struct range_list *unlocked_lines = NULL;
9945a0e240fSJohn Levon int locked_buckets[NUM_BUCKETS] = {};
9955a0e240fSJohn Levon int unlocked_buckets[NUM_BUCKETS] = {};
9965a0e240fSJohn Levon struct stree *stree, *orig;
9975a0e240fSJohn Levon struct sm_state *return_sm;
9985a0e240fSJohn Levon struct sm_state *sm;
9995a0e240fSJohn Levon sval_t line = sval_type_val(&int_ctype, 0);
10005a0e240fSJohn Levon int bucket;
10011f5207b7SJohn Levon int i;
10021f5207b7SJohn Levon
10035a0e240fSJohn Levon if (sym_in_lock_table(cur_func_sym))
10045a0e240fSJohn Levon return;
10051f5207b7SJohn Levon
10065a0e240fSJohn Levon FOR_EACH_PTR(get_all_return_strees(), stree) {
10075a0e240fSJohn Levon orig = __swap_cur_stree(stree);
10081f5207b7SJohn Levon
10095a0e240fSJohn Levon if (is_impossible_path())
10105a0e240fSJohn Levon goto swap_stree;
10115a0e240fSJohn Levon
10125a0e240fSJohn Levon return_sm = get_sm_state(RETURN_ID, "return_ranges", NULL);
10135a0e240fSJohn Levon if (!return_sm)
10145a0e240fSJohn Levon goto swap_stree;
10155a0e240fSJohn Levon line.value = return_sm->line;
10165a0e240fSJohn Levon
10175a0e240fSJohn Levon sm = get_sm_state(my_id, name, sym);
10185a0e240fSJohn Levon if (!sm)
10195a0e240fSJohn Levon goto swap_stree;
10205a0e240fSJohn Levon
10215a0e240fSJohn Levon if (parent_is_gone_var_sym(sm->name, sm->sym))
10225a0e240fSJohn Levon goto swap_stree;
10235a0e240fSJohn Levon
1024*31ad075eSJohn Levon if (sm->state != &locked &&
1025*31ad075eSJohn Levon sm->state != &unlocked &&
1026*31ad075eSJohn Levon sm->state != &restore)
10275a0e240fSJohn Levon goto swap_stree;
10285a0e240fSJohn Levon
1029*31ad075eSJohn Levon if ((sm->state == &unlocked || sm->state == &restore) &&
1030*31ad075eSJohn Levon is_EINTR(estate_rl(return_sm->state)))
10315a0e240fSJohn Levon goto swap_stree;
10325a0e240fSJohn Levon
10335a0e240fSJohn Levon bucket = success_fail_positive(estate_rl(return_sm->state));
10345a0e240fSJohn Levon if (sm->state == &locked) {
10355a0e240fSJohn Levon add_range(&locked_lines, line, line);
10365a0e240fSJohn Levon locked_buckets[bucket] = true;
10371f5207b7SJohn Levon }
1038*31ad075eSJohn Levon if (sm->state == &unlocked || sm->state == &restore) {
10395a0e240fSJohn Levon add_range(&unlocked_lines, line, line);
10405a0e240fSJohn Levon unlocked_buckets[bucket] = true;
10411f5207b7SJohn Levon }
10425a0e240fSJohn Levon swap_stree:
10435a0e240fSJohn Levon __swap_cur_stree(orig);
10445a0e240fSJohn Levon } END_FOR_EACH_PTR(stree);
10451f5207b7SJohn Levon
10461f5207b7SJohn Levon
10475a0e240fSJohn Levon if (!locked_lines || !unlocked_lines)
10485a0e240fSJohn Levon return;
10491f5207b7SJohn Levon
10505a0e240fSJohn Levon for (i = 0; i < NUM_BUCKETS; i++) {
10515a0e240fSJohn Levon if (locked_buckets[i] && unlocked_buckets[i])
10525a0e240fSJohn Levon goto complain;
10531f5207b7SJohn Levon }
10545a0e240fSJohn Levon if (locked_buckets[NEGATIVE] &&
10555a0e240fSJohn Levon (unlocked_buckets[ZERO] || unlocked_buckets[POSITIVE]))
10565a0e240fSJohn Levon goto complain;
10571f5207b7SJohn Levon
10585a0e240fSJohn Levon if (locked_buckets[ERR_PTR])
10595a0e240fSJohn Levon goto complain;
10601f5207b7SJohn Levon
10615a0e240fSJohn Levon return;
10625a0e240fSJohn Levon
10635a0e240fSJohn Levon complain:
10645a0e240fSJohn Levon sm_msg("warn: inconsistent returns '%s'.", name);
10655a0e240fSJohn Levon sm_printf(" Locked on : %s\n", show_rl(locked_lines));
10665a0e240fSJohn Levon sm_printf(" Unlocked on: %s\n", show_rl(unlocked_lines));
10671f5207b7SJohn Levon }
10681f5207b7SJohn Levon
match_func_end(struct symbol * sym)10695a0e240fSJohn Levon static void match_func_end(struct symbol *sym)
10701f5207b7SJohn Levon {
10715a0e240fSJohn Levon struct tracker *tracker;
10721f5207b7SJohn Levon
10735a0e240fSJohn Levon FOR_EACH_PTR(locks, tracker) {
10745a0e240fSJohn Levon check_lock(tracker->name, tracker->sym);
10755a0e240fSJohn Levon } END_FOR_EACH_PTR(tracker);
10761f5207b7SJohn Levon }
10771f5207b7SJohn Levon
register_lock(int index)10785a0e240fSJohn Levon static void register_lock(int index)
10791f5207b7SJohn Levon {
10805a0e240fSJohn Levon struct lock_info *lock = &lock_table[index];
10815a0e240fSJohn Levon void *idx = INT_PTR(index);
10821f5207b7SJohn Levon
10835a0e240fSJohn Levon if (lock->return_type == ret_one) {
10845a0e240fSJohn Levon return_implies_state(lock->function, 1, 1, &match_lock_held, idx);
10855a0e240fSJohn Levon return_implies_state(lock->function, 0, 0, &match_lock_failed, idx);
10865a0e240fSJohn Levon } else if (lock->return_type == ret_any && lock->arg == RETURN_VAL) {
10875a0e240fSJohn Levon add_function_assign_hook(lock->function, &match_returns_locked, idx);
10885a0e240fSJohn Levon } else if (lock->return_type == ret_any) {
10895a0e240fSJohn Levon add_function_hook(lock->function, &match_lock_unlock, idx);
10905a0e240fSJohn Levon } else if (lock->return_type == ret_zero) {
10915a0e240fSJohn Levon return_implies_state(lock->function, 0, 0, &match_lock_held, idx);
10925a0e240fSJohn Levon return_implies_state(lock->function, -4095, -1, &match_lock_failed, idx);
10935a0e240fSJohn Levon } else if (lock->return_type == ret_valid_ptr) {
10945a0e240fSJohn Levon return_implies_state_sval(lock->function, valid_ptr_min_sval, valid_ptr_max_sval, &match_lock_held, idx);
10955a0e240fSJohn Levon }
10961f5207b7SJohn Levon }
10971f5207b7SJohn Levon
load_table(struct lock_info * lock_table)10985a0e240fSJohn Levon static void load_table(struct lock_info *lock_table)
10991f5207b7SJohn Levon {
11005a0e240fSJohn Levon int i;
11011f5207b7SJohn Levon
11025a0e240fSJohn Levon for (i = 0; lock_table[i].function != NULL; i++) {
11035a0e240fSJohn Levon if (lock_table[i].action == LOCK)
11045a0e240fSJohn Levon register_lock(i);
11055a0e240fSJohn Levon else
11065a0e240fSJohn Levon add_function_hook(lock_table[i].function, &match_lock_unlock, INT_PTR(i));
11075a0e240fSJohn Levon }
11085a0e240fSJohn Levon }
11091f5207b7SJohn Levon
db_param_locked_unlocked(struct expression * expr,int param,char * key,char * value,enum action lock_unlock)11105a0e240fSJohn Levon static void db_param_locked_unlocked(struct expression *expr, int param, char *key, char *value, enum action lock_unlock)
11115a0e240fSJohn Levon {
11125a0e240fSJohn Levon struct expression *call, *arg;
11135a0e240fSJohn Levon char *name;
11145a0e240fSJohn Levon struct symbol *sym;
11155a0e240fSJohn Levon
11165a0e240fSJohn Levon call = expr;
11175a0e240fSJohn Levon while (call->type == EXPR_ASSIGNMENT)
11185a0e240fSJohn Levon call = strip_expr(call->right);
11195a0e240fSJohn Levon if (call->type != EXPR_CALL)
11201f5207b7SJohn Levon return;
11211f5207b7SJohn Levon
11225a0e240fSJohn Levon if (func_in_lock_table(call->fn))
11231f5207b7SJohn Levon return;
11241f5207b7SJohn Levon
11255a0e240fSJohn Levon if (param == -2) {
11265a0e240fSJohn Levon use_best_match(key, lock_unlock);
11271f5207b7SJohn Levon return;
11285a0e240fSJohn Levon }
11291f5207b7SJohn Levon
11305a0e240fSJohn Levon if (param == -1) {
11315a0e240fSJohn Levon if (expr->type != EXPR_ASSIGNMENT)
11325a0e240fSJohn Levon return;
11335a0e240fSJohn Levon name = get_variable_from_key(expr->left, key, &sym);
11345a0e240fSJohn Levon } else {
11355a0e240fSJohn Levon arg = get_argument_from_call_expr(call->args, param);
11365a0e240fSJohn Levon if (!arg)
11371f5207b7SJohn Levon return;
11385a0e240fSJohn Levon
11395a0e240fSJohn Levon name = get_variable_from_key(arg, key, &sym);
11401f5207b7SJohn Levon }
11415a0e240fSJohn Levon if (!name || !sym)
11425a0e240fSJohn Levon goto free;
11431f5207b7SJohn Levon
11445a0e240fSJohn Levon if (lock_unlock == LOCK)
11455a0e240fSJohn Levon do_lock(name, sym, NULL);
11465a0e240fSJohn Levon else if (lock_unlock == UNLOCK)
11475a0e240fSJohn Levon do_unlock(name, sym, NULL);
11485a0e240fSJohn Levon else if (lock_unlock == RESTORE)
11495a0e240fSJohn Levon do_restore(name, sym, NULL);
11505a0e240fSJohn Levon
11515a0e240fSJohn Levon free:
11525a0e240fSJohn Levon free_string(name);
11531f5207b7SJohn Levon }
11541f5207b7SJohn Levon
db_param_locked(struct expression * expr,int param,char * key,char * value)11555a0e240fSJohn Levon static void db_param_locked(struct expression *expr, int param, char *key, char *value)
11561f5207b7SJohn Levon {
11575a0e240fSJohn Levon db_param_locked_unlocked(expr, param, key, value, LOCK);
11581f5207b7SJohn Levon }
11591f5207b7SJohn Levon
db_param_unlocked(struct expression * expr,int param,char * key,char * value)11605a0e240fSJohn Levon static void db_param_unlocked(struct expression *expr, int param, char *key, char *value)
11611f5207b7SJohn Levon {
11625a0e240fSJohn Levon db_param_locked_unlocked(expr, param, key, value, UNLOCK);
11635a0e240fSJohn Levon }
11641f5207b7SJohn Levon
db_param_restore(struct expression * expr,int param,char * key,char * value)11655a0e240fSJohn Levon static void db_param_restore(struct expression *expr, int param, char *key, char *value)
11665a0e240fSJohn Levon {
11675a0e240fSJohn Levon db_param_locked_unlocked(expr, param, key, value, RESTORE);
11685a0e240fSJohn Levon }
11691f5207b7SJohn Levon
get_caller_param_lock_name(struct expression * call,struct sm_state * sm,const char ** name)11705a0e240fSJohn Levon static int get_caller_param_lock_name(struct expression *call, struct sm_state *sm, const char **name)
11715a0e240fSJohn Levon {
11725a0e240fSJohn Levon struct expression *arg;
11735a0e240fSJohn Levon char *arg_name;
11745a0e240fSJohn Levon int param;
11755a0e240fSJohn Levon
11765a0e240fSJohn Levon param = 0;
11775a0e240fSJohn Levon FOR_EACH_PTR(call->args, arg) {
11785a0e240fSJohn Levon arg_name = sm_to_arg_name(arg, sm);
11795a0e240fSJohn Levon if (arg_name) {
11805a0e240fSJohn Levon *name = arg_name;
11815a0e240fSJohn Levon return param;
11825a0e240fSJohn Levon }
11835a0e240fSJohn Levon param++;
11845a0e240fSJohn Levon } END_FOR_EACH_PTR(arg);
11851f5207b7SJohn Levon
11865a0e240fSJohn Levon *name = sm->name;
11875a0e240fSJohn Levon return -2;
11881f5207b7SJohn Levon }
11891f5207b7SJohn Levon
match_call_info(struct expression * expr)11905a0e240fSJohn Levon static void match_call_info(struct expression *expr)
11911f5207b7SJohn Levon {
11925a0e240fSJohn Levon struct sm_state *sm;
11935a0e240fSJohn Levon const char *param_name;
11945a0e240fSJohn Levon int locked_type;
11955a0e240fSJohn Levon int param;
11965a0e240fSJohn Levon
11975a0e240fSJohn Levon FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
11985a0e240fSJohn Levon param = get_caller_param_lock_name(expr, sm, ¶m_name);
11995a0e240fSJohn Levon if (sm->state == &locked)
12005a0e240fSJohn Levon locked_type = LOCKED;
12015a0e240fSJohn Levon else if (sm->state == &half_locked ||
12025a0e240fSJohn Levon slist_has_state(sm->possible, &locked))
12035a0e240fSJohn Levon locked_type = HALF_LOCKED;
12045a0e240fSJohn Levon else
12055a0e240fSJohn Levon continue;
12065a0e240fSJohn Levon sql_insert_caller_info(expr, locked_type, param, param_name, "xxx type");
12071f5207b7SJohn Levon
12085a0e240fSJohn Levon } END_FOR_EACH_SM(sm);
12091f5207b7SJohn Levon }
12101f5207b7SJohn Levon
match_save_states(struct expression * expr)12115a0e240fSJohn Levon static void match_save_states(struct expression *expr)
12121f5207b7SJohn Levon {
12135a0e240fSJohn Levon push_stree(&saved_stack, start_states);
12145a0e240fSJohn Levon start_states = NULL;
12151f5207b7SJohn Levon }
12161f5207b7SJohn Levon
match_restore_states(struct expression * expr)12175a0e240fSJohn Levon static void match_restore_states(struct expression *expr)
12181f5207b7SJohn Levon {
12195a0e240fSJohn Levon start_states = pop_stree(&saved_stack);
12205a0e240fSJohn Levon }
12211f5207b7SJohn Levon
match_after_func(struct symbol * sym)12225a0e240fSJohn Levon static void match_after_func(struct symbol *sym)
12235a0e240fSJohn Levon {
12245a0e240fSJohn Levon free_stree(&start_states);
12251f5207b7SJohn Levon }
12261f5207b7SJohn Levon
match_dma_resv_lock_NULL(const char * fn,struct expression * call_expr,struct expression * assign_expr,void * _index)12275a0e240fSJohn Levon static void match_dma_resv_lock_NULL(const char *fn, struct expression *call_expr,
12285a0e240fSJohn Levon struct expression *assign_expr, void *_index)
12291f5207b7SJohn Levon {
12305a0e240fSJohn Levon struct expression *lock, *ctx;
12315a0e240fSJohn Levon char *lock_name;
12325a0e240fSJohn Levon struct symbol *sym;
12331f5207b7SJohn Levon
12345a0e240fSJohn Levon lock = get_argument_from_call_expr(call_expr->args, 0);
12355a0e240fSJohn Levon ctx = get_argument_from_call_expr(call_expr->args, 1);
12365a0e240fSJohn Levon if (!expr_is_zero(ctx))
12375a0e240fSJohn Levon return;
12381f5207b7SJohn Levon
12395a0e240fSJohn Levon lock_name = lock_to_name_sym(lock, &sym);
12405a0e240fSJohn Levon if (!lock_name || !sym)
12415a0e240fSJohn Levon goto free;
12425a0e240fSJohn Levon do_lock(lock_name, sym, NULL);
12435a0e240fSJohn Levon free:
12445a0e240fSJohn Levon free_string(lock_name);
12451f5207b7SJohn Levon }
12461f5207b7SJohn Levon
12471f5207b7SJohn Levon /* print_held_locks() is used in check_call_tree.c */
print_held_locks(void)12481f5207b7SJohn Levon void print_held_locks(void)
12491f5207b7SJohn Levon {
12501f5207b7SJohn Levon struct stree *stree;
12511f5207b7SJohn Levon struct sm_state *sm;
12521f5207b7SJohn Levon int i = 0;
12531f5207b7SJohn Levon
12541f5207b7SJohn Levon stree = __get_cur_stree();
12551f5207b7SJohn Levon FOR_EACH_MY_SM(my_id, stree, sm) {
12561f5207b7SJohn Levon if (sm->state != &locked)
12571f5207b7SJohn Levon continue;
12581f5207b7SJohn Levon if (i++)
12591f5207b7SJohn Levon sm_printf(" ");
12601f5207b7SJohn Levon sm_printf("'%s'", sm->name);
12611f5207b7SJohn Levon } END_FOR_EACH_SM(sm);
12621f5207b7SJohn Levon }
12631f5207b7SJohn Levon
check_locking(int id)12641f5207b7SJohn Levon void check_locking(int id)
12651f5207b7SJohn Levon {
12661f5207b7SJohn Levon my_id = id;
12671f5207b7SJohn Levon
12685a0e240fSJohn Levon if (option_project != PROJ_KERNEL)
12691f5207b7SJohn Levon return;
12701f5207b7SJohn Levon
12715a0e240fSJohn Levon load_table(lock_table);
12725a0e240fSJohn Levon
12735a0e240fSJohn Levon set_dynamic_states(my_id);
12741f5207b7SJohn Levon add_unmatched_state_hook(my_id, &unmatched_state);
12751f5207b7SJohn Levon add_pre_merge_hook(my_id, &pre_merge_hook);
12765a0e240fSJohn Levon add_merge_hook(my_id, &merge_func);
12775a0e240fSJohn Levon add_modification_hook(my_id, &reset);
12785a0e240fSJohn Levon
12791f5207b7SJohn Levon add_hook(&match_func_end, END_FUNC_HOOK);
12805a0e240fSJohn Levon
12811f5207b7SJohn Levon add_hook(&match_after_func, AFTER_FUNC_HOOK);
12825a0e240fSJohn Levon add_hook(&match_save_states, INLINE_FN_START);
12835a0e240fSJohn Levon add_hook(&match_restore_states, INLINE_FN_END);
12845a0e240fSJohn Levon
12855a0e240fSJohn Levon add_hook(&match_call_info, FUNCTION_CALL_HOOK);
12865a0e240fSJohn Levon
12875a0e240fSJohn Levon add_split_return_callback(match_return_info);
12885a0e240fSJohn Levon select_return_states_hook(LOCKED, &db_param_locked);
12895a0e240fSJohn Levon select_return_states_hook(UNLOCKED, &db_param_unlocked);
12905a0e240fSJohn Levon select_return_states_hook(LOCK_RESTORED, &db_param_restore);
12911f5207b7SJohn Levon
12925a0e240fSJohn Levon return_implies_state("dma_resv_lock", -4095, -1, &match_dma_resv_lock_NULL, 0);
12931f5207b7SJohn Levon }
1294