Print this page
12166 resync smatch to 0.6.1-rc1-il-3

*** 1,7 **** --- 1,8 ---- /* * Copyright (C) 2009 Dan Carpenter. + * Copyright (C) 2019 Oracle. * * 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.
*** 13,399 **** * * 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 */ ! /* ! * This test checks that locks are held the same across all returns. ! * ! * Of course, some functions are designed to only hold the locks on success. ! * Oh well... We can rewrite it later if we want. ! * ! * The list of wine locking functions came from an earlier script written ! * by Michael Stefaniuc. ! * ! */ ! #include "parse.h" #include "smatch.h" #include "smatch_extra.h" #include "smatch_slist.h" static int my_id; - static int func_has_transition; - STATE(locked); STATE(start_state); STATE(unlocked); STATE(impossible); enum action { LOCK, UNLOCK, }; enum return_type { ret_any, - ret_non_zero, ret_zero, ret_one, ret_negative, ret_positive, }; #define RETURN_VAL -1 #define NO_ARG -2 struct lock_info { const char *function; enum action action; ! const char *name; int arg; enum return_type return_type; }; ! static struct lock_info wine_lock_table[] = { ! {"create_window_handle", LOCK, "create_window_handle", RETURN_VAL, ret_non_zero}, ! {"WIN_GetPtr", LOCK, "create_window_handle", RETURN_VAL, ret_non_zero}, ! {"WIN_ReleasePtr", UNLOCK, "create_window_handle", 0, ret_any}, ! {"EnterCriticalSection", LOCK, "CriticalSection", 0, ret_any}, ! {"LeaveCriticalSection", UNLOCK, "CriticalSection", 0, ret_any}, ! {"RtlEnterCriticalSection", LOCK, "RtlCriticalSection", 0, ret_any}, ! {"RtlLeaveCriticalSection", UNLOCK, "RtlCriticalSection", 0, ret_any}, ! {"GDI_GetObjPtr", LOCK, "GDI_Get", 0, ret_non_zero}, ! {"GDI_ReleaseObj", UNLOCK, "GDI_Get", 0, ret_any}, ! {"LdrLockLoaderLock", LOCK, "LdrLockLoaderLock", 2, ret_any}, ! {"LdrUnlockLoaderLock", UNLOCK, "LdrLockLoaderLock", 1, ret_any}, ! {"_lock", LOCK, "_lock", 0, ret_any}, ! {"_unlock", UNLOCK, "_lock", 0, ret_any}, ! {"msiobj_lock", LOCK, "msiobj_lock", 0, ret_any}, ! {"msiobj_unlock", UNLOCK, "msiobj_lock", 0, ret_any}, ! {"RtlAcquirePebLock", LOCK, "PebLock", NO_ARG, ret_any}, ! {"RtlReleasePebLock", UNLOCK, "PebLock", NO_ARG, ret_any}, ! {"server_enter_uninterrupted_section", LOCK, "server_uninterrupted_section", 0, ret_any}, ! {"server_leave_uninterrupted_section", UNLOCK, "server_uninterrupted_section", 0, ret_any}, ! {"RtlLockHeap", LOCK, "RtlLockHeap", 0, ret_any}, ! {"RtlUnlockHeap", UNLOCK, "RtlLockHeap", 0, ret_any}, ! {"_EnterSysLevel", LOCK, "SysLevel", 0, ret_any}, ! {"_LeaveSysLevel", UNLOCK, "SysLevel", 0, ret_any}, ! {"USER_Lock", LOCK, "USER_Lock", NO_ARG, ret_any}, ! {"USER_Unlock", UNLOCK, "USER_Lock", NO_ARG, ret_any}, ! {"wine_tsx11_lock", LOCK, "wine_tsx11_lock", NO_ARG, ret_any}, ! {"wine_tsx11_unlock", UNLOCK, "wine_tsx11_lock", NO_ARG, ret_any}, ! {"wine_tsx11_lock_ptr", LOCK, "wine_tsx11_lock_ptr", NO_ARG, ret_any}, ! {"wine_tsx11_unlock_ptr", UNLOCK, "wine_tsx11_lock_ptr", NO_ARG, ret_any}, ! {"wined3d_mutex_lock", LOCK, "wined3d_mutex_lock", NO_ARG, ret_any}, ! {"wined3d_mutex_unlock", UNLOCK, "wined3d_mutex_lock", NO_ARG, ret_any}, ! {"X11DRV_DIB_Lock", LOCK, "X11DRV_DIB_Lock", 0, ret_any}, ! {"X11DRV_DIB_Unlock", UNLOCK, "X11DRV_DIB_Lock", 0, ret_any}, ! }; ! static struct lock_info kernel_lock_table[] = { ! {"lock_kernel", LOCK, "BKL", NO_ARG, ret_any}, ! {"unlock_kernel", UNLOCK, "BKL", NO_ARG, ret_any}, ! {"spin_lock", LOCK, "spin_lock", 0, ret_any}, ! {"spin_unlock", UNLOCK, "spin_lock", 0, ret_any}, ! {"spin_lock_nested", LOCK, "spin_lock", 0, ret_any}, ! {"_spin_lock", LOCK, "spin_lock", 0, ret_any}, ! {"_spin_unlock", UNLOCK, "spin_lock", 0, ret_any}, ! {"_spin_lock_nested", LOCK, "spin_lock", 0, ret_any}, ! {"__spin_lock", LOCK, "spin_lock", 0, ret_any}, ! {"__spin_unlock", UNLOCK, "spin_lock", 0, ret_any}, ! {"__spin_lock_nested", LOCK, "spin_lock", 0, ret_any}, ! {"raw_spin_lock", LOCK, "spin_lock", 0, ret_any}, ! {"raw_spin_unlock", UNLOCK, "spin_lock", 0, ret_any}, ! {"_raw_spin_lock", LOCK, "spin_lock", 0, ret_any}, ! {"_raw_spin_lock_nested", LOCK, "spin_lock", 0, ret_any}, ! {"_raw_spin_unlock", UNLOCK, "spin_lock", 0, ret_any}, ! {"__raw_spin_lock", LOCK, "spin_lock", 0, ret_any}, ! {"__raw_spin_unlock", UNLOCK, "spin_lock", 0, ret_any}, ! {"spin_lock_irq", LOCK, "spin_lock", 0, ret_any}, ! {"spin_unlock_irq", UNLOCK, "spin_lock", 0, ret_any}, ! {"_spin_lock_irq", LOCK, "spin_lock", 0, ret_any}, ! {"_spin_unlock_irq", UNLOCK, "spin_lock", 0, ret_any}, ! {"__spin_lock_irq", LOCK, "spin_lock", 0, ret_any}, ! {"__spin_unlock_irq", UNLOCK, "spin_lock", 0, ret_any}, ! {"_raw_spin_lock_irq", LOCK, "spin_lock", 0, ret_any}, ! {"_raw_spin_unlock_irq", UNLOCK, "spin_lock", 0, ret_any}, ! {"__raw_spin_unlock_irq", UNLOCK, "spin_lock", 0, ret_any}, ! {"spin_lock_irqsave", LOCK, "spin_lock", 0, ret_any}, ! {"spin_unlock_irqrestore", UNLOCK, "spin_lock", 0, ret_any}, ! {"_spin_lock_irqsave", LOCK, "spin_lock", 0, ret_any}, ! {"_spin_unlock_irqrestore", UNLOCK, "spin_lock", 0, ret_any}, ! {"__spin_lock_irqsave", LOCK, "spin_lock", 0, ret_any}, ! {"__spin_unlock_irqrestore", UNLOCK, "spin_lock", 0, ret_any}, ! {"_raw_spin_lock_irqsave", LOCK, "spin_lock", 0, ret_any}, ! {"_raw_spin_unlock_irqrestore", UNLOCK, "spin_lock", 0, ret_any}, ! {"__raw_spin_lock_irqsave", LOCK, "spin_lock", 0, ret_any}, ! {"__raw_spin_unlock_irqrestore", UNLOCK, "spin_lock", 0, ret_any}, ! {"spin_lock_irqsave_nested", LOCK, "spin_lock", 0, ret_any}, ! {"_spin_lock_irqsave_nested", LOCK, "spin_lock", 0, ret_any}, ! {"__spin_lock_irqsave_nested", LOCK, "spin_lock", 0, ret_any}, ! {"_raw_spin_lock_irqsave_nested", LOCK, "spin_lock", 0, ret_any}, ! {"spin_lock_bh", LOCK, "spin_lock", 0, ret_any}, ! {"spin_unlock_bh", UNLOCK, "spin_lock", 0, ret_any}, ! {"_spin_lock_bh", LOCK, "spin_lock", 0, ret_any}, ! {"_spin_unlock_bh", UNLOCK, "spin_lock", 0, ret_any}, ! {"__spin_lock_bh", LOCK, "spin_lock", 0, ret_any}, ! {"__spin_unlock_bh", UNLOCK, "spin_lock", 0, ret_any}, ! {"spin_trylock", LOCK, "spin_lock", 0, ret_one}, ! {"_spin_trylock", LOCK, "spin_lock", 0, ret_one}, ! {"__spin_trylock", LOCK, "spin_lock", 0, ret_one}, ! {"raw_spin_trylock", LOCK, "spin_lock", 0, ret_one}, ! {"_raw_spin_trylock", LOCK, "spin_lock", 0, ret_one}, ! {"spin_trylock_irq", LOCK, "spin_lock", 0, ret_one}, ! {"spin_trylock_irqsave", LOCK, "spin_lock", 0, ret_one}, ! {"spin_trylock_bh", LOCK, "spin_lock", 0, ret_one}, ! {"_spin_trylock_bh", LOCK, "spin_lock", 0, ret_one}, ! {"__spin_trylock_bh", LOCK, "spin_lock", 0, ret_one}, ! {"__raw_spin_trylock", LOCK, "spin_lock", 0, ret_one}, ! {"_atomic_dec_and_lock", LOCK, "spin_lock", 1, ret_one}, ! {"read_lock", LOCK, "read_lock", 0, ret_any}, ! {"read_unlock", UNLOCK, "read_lock", 0, ret_any}, ! {"_read_lock", LOCK, "read_lock", 0, ret_any}, ! {"_read_unlock", UNLOCK, "read_lock", 0, ret_any}, ! {"__read_lock", LOCK, "read_lock", 0, ret_any}, ! {"__read_unlock", UNLOCK, "read_lock", 0, ret_any}, ! {"_raw_read_lock", LOCK, "read_lock", 0, ret_any}, ! {"_raw_read_unlock", UNLOCK, "read_lock", 0, ret_any}, ! {"__raw_read_lock", LOCK, "read_lock", 0, ret_any}, ! {"__raw_read_unlock", UNLOCK, "read_lock", 0, ret_any}, ! {"read_lock_irq", LOCK, "read_lock", 0, ret_any}, ! {"read_unlock_irq" , UNLOCK, "read_lock", 0, ret_any}, ! {"_read_lock_irq", LOCK, "read_lock", 0, ret_any}, ! {"_read_unlock_irq", UNLOCK, "read_lock", 0, ret_any}, ! {"__read_lock_irq", LOCK, "read_lock", 0, ret_any}, ! {"__read_unlock_irq", UNLOCK, "read_lock", 0, ret_any}, ! {"read_lock_irqsave", LOCK, "read_lock", 0, ret_any}, ! {"read_unlock_irqrestore", UNLOCK, "read_lock", 0, ret_any}, ! {"_read_lock_irqsave", LOCK, "read_lock", 0, ret_any}, ! {"_read_unlock_irqrestore", UNLOCK, "read_lock", 0, ret_any}, ! {"__read_lock_irqsave", LOCK, "read_lock", 0, ret_any}, ! {"__read_unlock_irqrestore", UNLOCK, "read_lock", 0, ret_any}, ! {"read_lock_bh", LOCK, "read_lock", 0, ret_any}, ! {"read_unlock_bh", UNLOCK, "read_lock", 0, ret_any}, ! {"_read_lock_bh", LOCK, "read_lock", 0, ret_any}, ! {"_read_unlock_bh", UNLOCK, "read_lock", 0, ret_any}, ! {"__read_lock_bh", LOCK, "read_lock", 0, ret_any}, ! {"__read_unlock_bh", UNLOCK, "read_lock", 0, ret_any}, ! {"_raw_read_lock_bh", LOCK, "read_lock", 0, ret_any}, ! {"_raw_read_unlock_bh", UNLOCK, "read_lock", 0, ret_any}, ! {"__raw_read_lock_bh", LOCK, "read_lock", 0, ret_any}, ! {"__raw_read_unlock_bh", UNLOCK, "read_lock", 0, ret_any}, ! {"generic__raw_read_trylock", LOCK, "read_lock", 0, ret_one}, ! {"read_trylock", LOCK, "read_lock", 0, ret_one}, ! {"_read_trylock", LOCK, "read_lock", 0, ret_one}, ! {"raw_read_trylock", LOCK, "read_lock", 0, ret_one}, ! {"_raw_read_trylock", LOCK, "read_lock", 0, ret_one}, ! {"__raw_read_trylock", LOCK, "read_lock", 0, ret_one}, ! {"__read_trylock", LOCK, "read_lock", 0, ret_one}, ! {"write_lock", LOCK, "write_lock", 0, ret_any}, ! {"write_unlock", UNLOCK, "write_lock", 0, ret_any}, ! {"_write_lock", LOCK, "write_lock", 0, ret_any}, ! {"_write_unlock", UNLOCK, "write_lock", 0, ret_any}, ! {"__write_lock", LOCK, "write_lock", 0, ret_any}, ! {"__write_unlock", UNLOCK, "write_lock", 0, ret_any}, ! {"write_lock_irq", LOCK, "write_lock", 0, ret_any}, ! {"write_unlock_irq", UNLOCK, "write_lock", 0, ret_any}, ! {"_write_lock_irq", LOCK, "write_lock", 0, ret_any}, ! {"_write_unlock_irq", UNLOCK, "write_lock", 0, ret_any}, ! {"__write_lock_irq", LOCK, "write_lock", 0, ret_any}, ! {"__write_unlock_irq", UNLOCK, "write_lock", 0, ret_any}, ! {"write_lock_irqsave", LOCK, "write_lock", 0, ret_any}, ! {"write_unlock_irqrestore", UNLOCK, "write_lock", 0, ret_any}, ! {"_write_lock_irqsave", LOCK, "write_lock", 0, ret_any}, ! {"_write_unlock_irqrestore", UNLOCK, "write_lock", 0, ret_any}, ! {"__write_lock_irqsave", LOCK, "write_lock", 0, ret_any}, ! {"__write_unlock_irqrestore", UNLOCK, "write_lock", 0, ret_any}, ! {"write_lock_bh", LOCK, "write_lock", 0, ret_any}, ! {"write_unlock_bh", UNLOCK, "write_lock", 0, ret_any}, ! {"_write_lock_bh", LOCK, "write_lock", 0, ret_any}, ! {"_write_unlock_bh", UNLOCK, "write_lock", 0, ret_any}, ! {"__write_lock_bh", LOCK, "write_lock", 0, ret_any}, ! {"__write_unlock_bh", UNLOCK, "write_lock", 0, ret_any}, ! {"_raw_write_lock", LOCK, "write_lock", 0, ret_any}, ! {"__raw_write_lock", LOCK, "write_lock", 0, ret_any}, ! {"_raw_write_unlock", UNLOCK, "write_lock", 0, ret_any}, ! {"__raw_write_unlock", UNLOCK, "write_lock", 0, ret_any}, ! {"write_trylock", LOCK, "write_lock", 0, ret_one}, ! {"_write_trylock", LOCK, "write_lock", 0, ret_one}, ! {"raw_write_trylock", LOCK, "write_lock", 0, ret_one}, ! {"_raw_write_trylock", LOCK, "write_lock", 0, ret_one}, ! {"__write_trylock", LOCK, "write_lock", 0, ret_one}, ! {"__raw_write_trylock", LOCK, "write_lock", 0, ret_one}, - {"down", LOCK, "sem", 0, ret_any}, - {"up", UNLOCK, "sem", 0, ret_any}, - {"down_trylock", LOCK, "sem", 0, ret_zero}, - {"down_timeout", LOCK, "sem", 0, ret_zero}, - {"down_interruptible", LOCK, "sem", 0, ret_zero}, ! {"down_write", LOCK, "rw_sem", 0, ret_any}, ! {"downgrade_write", UNLOCK, "rw_sem", 0, ret_any}, ! {"downgrade_write", LOCK, "read_sem", 0, ret_any}, ! {"up_write", UNLOCK, "rw_sem", 0, ret_any}, ! {"down_write_trylock", LOCK, "rw_sem", 0, ret_one}, ! {"down_write_killable", LOCK, "rw_sem", 0, ret_zero}, ! {"down_read", LOCK, "read_sem", 0, ret_any}, ! {"down_read_trylock", LOCK, "read_sem", 0, ret_one}, ! {"down_read_killable", LOCK, "read_sem", 0, ret_zero}, ! {"up_read", UNLOCK, "read_sem", 0, ret_any}, ! {"mutex_lock", LOCK, "mutex", 0, ret_any}, ! {"mutex_lock_io", LOCK, "mutex", 0, ret_any}, ! {"mutex_unlock", UNLOCK, "mutex", 0, ret_any}, ! {"mutex_lock_nested", LOCK, "mutex", 0, ret_any}, ! {"mutex_lock_io_nested", LOCK, "mutex", 0, ret_any}, ! {"mutex_lock_interruptible", LOCK, "mutex", 0, ret_zero}, ! {"mutex_lock_interruptible_nested", LOCK, "mutex", 0, ret_zero}, ! {"mutex_lock_killable", LOCK, "mutex", 0, ret_zero}, ! {"mutex_lock_killable_nested", LOCK, "mutex", 0, ret_zero}, ! {"mutex_trylock", LOCK, "mutex", 0, ret_one}, ! {"raw_local_irq_disable", LOCK, "irq", NO_ARG, ret_any}, ! {"raw_local_irq_enable", UNLOCK, "irq", NO_ARG, ret_any}, ! {"spin_lock_irq", LOCK, "irq", NO_ARG, ret_any}, ! {"spin_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, ! {"_spin_lock_irq", LOCK, "irq", NO_ARG, ret_any}, ! {"_spin_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, ! {"__spin_lock_irq", LOCK, "irq", NO_ARG, ret_any}, ! {"__spin_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, ! {"_raw_spin_lock_irq", LOCK, "irq", NO_ARG, ret_any}, ! {"_raw_spin_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, ! {"__raw_spin_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, ! {"spin_trylock_irq", LOCK, "irq", NO_ARG, ret_one}, ! {"read_lock_irq", LOCK, "irq", NO_ARG, ret_any}, ! {"read_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, ! {"_read_lock_irq", LOCK, "irq", NO_ARG, ret_any}, ! {"_read_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, ! {"__read_lock_irq", LOCK, "irq", NO_ARG, ret_any}, ! {"__read_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, ! {"write_lock_irq", LOCK, "irq", NO_ARG, ret_any}, ! {"write_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, ! {"_write_lock_irq", LOCK, "irq", NO_ARG, ret_any}, ! {"_write_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, ! {"__write_lock_irq", LOCK, "irq", NO_ARG, ret_any}, ! {"__write_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, ! {"arch_local_irq_save", LOCK, "irqsave", RETURN_VAL, ret_any}, ! {"arch_local_irq_restore", UNLOCK, "irqsave", 0, ret_any}, ! {"__raw_local_irq_save", LOCK, "irqsave", RETURN_VAL, ret_any}, ! {"raw_local_irq_restore", UNLOCK, "irqsave", 0, ret_any}, ! {"spin_lock_irqsave_nested", LOCK, "irqsave", RETURN_VAL, ret_any}, ! {"spin_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, ! {"spin_lock_irqsave", LOCK, "irqsave", 1, ret_any}, ! {"spin_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any}, ! {"_spin_lock_irqsave_nested", LOCK, "irqsave", RETURN_VAL, ret_any}, ! {"_spin_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, ! {"_spin_lock_irqsave", LOCK, "irqsave", 1, ret_any}, ! {"_spin_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any}, ! {"__spin_lock_irqsave_nested", LOCK, "irqsave", 1, ret_any}, ! {"__spin_lock_irqsave", LOCK, "irqsave", 1, ret_any}, ! {"__spin_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any}, ! {"_raw_spin_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, ! {"_raw_spin_lock_irqsave", LOCK, "irqsave", 1, ret_any}, ! {"_raw_spin_unlock_irqrestore",UNLOCK, "irqsave", 1, ret_any}, ! {"__raw_spin_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, ! {"__raw_spin_unlock_irqrestore",UNLOCK, "irqsave", 1, ret_any}, ! {"_raw_spin_lock_irqsave_nested", LOCK, "irqsave", RETURN_VAL, ret_any}, ! {"spin_trylock_irqsave", LOCK, "irqsave", 1, ret_one}, ! {"read_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, ! {"read_lock_irqsave", LOCK, "irqsave", 1, ret_any}, ! {"read_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any}, ! {"_read_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, ! {"_read_lock_irqsave", LOCK, "irqsave", 1, ret_any}, ! {"_read_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any}, ! {"__read_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, ! {"__read_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any}, ! {"write_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, ! {"write_lock_irqsave", LOCK, "irqsave", 1, ret_any}, ! {"write_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any}, ! {"_write_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, ! {"_write_lock_irqsave", LOCK, "irqsave", 1, ret_any}, ! {"_write_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any}, ! {"__write_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, ! {"__write_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any}, ! {"local_bh_disable", LOCK, "bottom_half", NO_ARG, ret_any}, ! {"_local_bh_disable", LOCK, "bottom_half", NO_ARG, ret_any}, ! {"__local_bh_disable", LOCK, "bottom_half", NO_ARG, ret_any}, ! {"local_bh_enable", UNLOCK, "bottom_half", NO_ARG, ret_any}, ! {"_local_bh_enable", UNLOCK, "bottom_half", NO_ARG, ret_any}, ! {"__local_bh_enable", UNLOCK, "bottom_half", NO_ARG, ret_any}, ! {"spin_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, ! {"spin_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, ! {"_spin_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, ! {"_spin_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, ! {"__spin_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, ! {"__spin_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, ! {"read_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, ! {"read_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, ! {"_read_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, ! {"_read_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, ! {"__read_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, ! {"__read_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, ! {"_raw_read_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, ! {"_raw_read_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, ! {"write_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, ! {"write_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, ! {"_write_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, ! {"_write_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, ! {"__write_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, ! {"__write_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, ! {"spin_trylock_bh", LOCK, "bottom_half", NO_ARG, ret_one}, ! {"_spin_trylock_bh", LOCK, "bottom_half", NO_ARG, ret_one}, ! {"__spin_trylock_bh", LOCK, "bottom_half", NO_ARG, ret_one}, ! {"ffs_mutex_lock", LOCK, "mutex", 0, ret_zero}, }; ! static struct lock_info *lock_table; ! static struct tracker_list *starts_locked; ! static struct tracker_list *starts_unlocked; ! struct locks_on_return { ! int line; ! struct tracker_list *locked; ! struct tracker_list *unlocked; ! struct tracker_list *impossible; ! struct range_list *return_values; }; - DECLARE_PTR_LIST(return_list, struct locks_on_return); - static struct return_list *all_returns; ! static char *make_full_name(const char *lock, const char *var) ! { ! static char tmp_buf[512]; ! snprintf(tmp_buf, sizeof(tmp_buf), "%s:%s", lock, var); ! remove_parens(tmp_buf); ! return alloc_string(tmp_buf); } static struct expression *remove_spinlock_check(struct expression *expr) { if (expr->type != EXPR_CALL) --- 14,443 ---- * * 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(locked); + STATE(half_locked); STATE(start_state); STATE(unlocked); STATE(impossible); + STATE(restore); enum action { LOCK, UNLOCK, + RESTORE, }; + enum lock_type { + spin_lock, + read_lock, + write_lock, + mutex, + bottom_half, + irq, + sem, + prepare_lock, + enable_lock, + }; + + const char *get_lock_name(enum lock_type type) + { + static const char *names[] = { + [spin_lock] = "spin_lock", + [read_lock] = "read_lock", + [write_lock] = "write_lock", + [mutex] = "mutex", + [bottom_half] = "bottom_half", + [irq] = "irq", + [sem] = "sem", + [prepare_lock] = "prepare_lock", + [enable_lock] = "enable_lock", + }; + + return names[type]; + } + enum return_type { ret_any, ret_zero, ret_one, ret_negative, ret_positive, + ret_valid_ptr, }; #define RETURN_VAL -1 #define NO_ARG -2 struct lock_info { const char *function; enum action action; ! enum lock_type type; int arg; enum return_type return_type; }; ! static struct lock_info lock_table[] = { ! {"spin_lock", LOCK, spin_lock, 0, ret_any}, ! {"spin_unlock", UNLOCK, spin_lock, 0, ret_any}, ! {"spin_lock_nested", LOCK, spin_lock, 0, ret_any}, ! {"_spin_lock", LOCK, spin_lock, 0, ret_any}, ! {"_spin_unlock", UNLOCK, spin_lock, 0, ret_any}, ! {"_spin_lock_nested", LOCK, spin_lock, 0, ret_any}, ! {"__spin_lock", LOCK, spin_lock, 0, ret_any}, ! {"__spin_unlock", UNLOCK, spin_lock, 0, ret_any}, ! {"__spin_lock_nested", LOCK, spin_lock, 0, ret_any}, ! {"raw_spin_lock", LOCK, spin_lock, 0, ret_any}, ! {"raw_spin_unlock", UNLOCK, spin_lock, 0, ret_any}, ! {"_raw_spin_lock", LOCK, spin_lock, 0, ret_any}, ! {"_raw_spin_lock_nested", LOCK, spin_lock, 0, ret_any}, ! {"_raw_spin_unlock", UNLOCK, spin_lock, 0, ret_any}, ! {"__raw_spin_lock", LOCK, spin_lock, 0, ret_any}, ! {"__raw_spin_unlock", UNLOCK, spin_lock, 0, ret_any}, ! {"spin_lock_irq", LOCK, spin_lock, 0, ret_any}, ! {"spin_unlock_irq", UNLOCK, spin_lock, 0, ret_any}, ! {"_spin_lock_irq", LOCK, spin_lock, 0, ret_any}, ! {"_spin_unlock_irq", UNLOCK, spin_lock, 0, ret_any}, ! {"__spin_lock_irq", LOCK, spin_lock, 0, ret_any}, ! {"__spin_unlock_irq", UNLOCK, spin_lock, 0, ret_any}, ! {"_raw_spin_lock_irq", LOCK, spin_lock, 0, ret_any}, ! {"_raw_spin_unlock_irq", UNLOCK, spin_lock, 0, ret_any}, ! {"__raw_spin_unlock_irq", UNLOCK, spin_lock, 0, ret_any}, ! {"spin_lock_irqsave", LOCK, spin_lock, 0, ret_any}, ! {"spin_unlock_irqrestore", UNLOCK, spin_lock, 0, ret_any}, ! {"_spin_lock_irqsave", LOCK, spin_lock, 0, ret_any}, ! {"_spin_unlock_irqrestore", UNLOCK, spin_lock, 0, ret_any}, ! {"__spin_lock_irqsave", LOCK, spin_lock, 0, ret_any}, ! {"__spin_unlock_irqrestore", UNLOCK, spin_lock, 0, ret_any}, ! {"_raw_spin_lock_irqsave", LOCK, spin_lock, 0, ret_any}, ! {"_raw_spin_unlock_irqrestore", UNLOCK, spin_lock, 0, ret_any}, ! {"__raw_spin_lock_irqsave", LOCK, spin_lock, 0, ret_any}, ! {"__raw_spin_unlock_irqrestore", UNLOCK, spin_lock, 0, ret_any}, ! {"spin_lock_irqsave_nested", LOCK, spin_lock, 0, ret_any}, ! {"_spin_lock_irqsave_nested", LOCK, spin_lock, 0, ret_any}, ! {"__spin_lock_irqsave_nested", LOCK, spin_lock, 0, ret_any}, ! {"_raw_spin_lock_irqsave_nested", LOCK, spin_lock, 0, ret_any}, ! {"spin_lock_bh", LOCK, spin_lock, 0, ret_any}, ! {"spin_unlock_bh", UNLOCK, spin_lock, 0, ret_any}, ! {"_spin_lock_bh", LOCK, spin_lock, 0, ret_any}, ! {"_spin_unlock_bh", UNLOCK, spin_lock, 0, ret_any}, ! {"__spin_lock_bh", LOCK, spin_lock, 0, ret_any}, ! {"__spin_unlock_bh", UNLOCK, spin_lock, 0, ret_any}, ! {"spin_trylock", LOCK, spin_lock, 0, ret_one}, ! {"_spin_trylock", LOCK, spin_lock, 0, ret_one}, ! {"__spin_trylock", LOCK, spin_lock, 0, ret_one}, ! {"raw_spin_trylock", LOCK, spin_lock, 0, ret_one}, ! {"_raw_spin_trylock", LOCK, spin_lock, 0, ret_one}, ! {"spin_trylock_irq", LOCK, spin_lock, 0, ret_one}, ! {"spin_trylock_irqsave", LOCK, spin_lock, 0, ret_one}, ! {"spin_trylock_bh", LOCK, spin_lock, 0, ret_one}, ! {"_spin_trylock_bh", LOCK, spin_lock, 0, ret_one}, ! {"__spin_trylock_bh", LOCK, spin_lock, 0, ret_one}, ! {"__raw_spin_trylock", LOCK, spin_lock, 0, ret_one}, ! {"_atomic_dec_and_lock", LOCK, spin_lock, 1, ret_one}, ! {"read_lock", LOCK, read_lock, 0, ret_any}, ! {"down_read", LOCK, read_lock, 0, ret_any}, ! {"down_read_nested", LOCK, read_lock, 0, ret_any}, ! {"down_read_trylock", LOCK, read_lock, 0, ret_one}, ! {"up_read", UNLOCK, read_lock, 0, ret_any}, ! {"read_unlock", UNLOCK, read_lock, 0, ret_any}, ! {"_read_lock", LOCK, read_lock, 0, ret_any}, ! {"_read_unlock", UNLOCK, read_lock, 0, ret_any}, ! {"__read_lock", LOCK, read_lock, 0, ret_any}, ! {"__read_unlock", UNLOCK, read_lock, 0, ret_any}, ! {"_raw_read_lock", LOCK, read_lock, 0, ret_any}, ! {"_raw_read_unlock", UNLOCK, read_lock, 0, ret_any}, ! {"__raw_read_lock", LOCK, read_lock, 0, ret_any}, ! {"__raw_read_unlock", UNLOCK, read_lock, 0, ret_any}, ! {"read_lock_irq", LOCK, read_lock, 0, ret_any}, ! {"read_unlock_irq" , UNLOCK, read_lock, 0, ret_any}, ! {"_read_lock_irq", LOCK, read_lock, 0, ret_any}, ! {"_read_unlock_irq", UNLOCK, read_lock, 0, ret_any}, ! {"__read_lock_irq", LOCK, read_lock, 0, ret_any}, ! {"__read_unlock_irq", UNLOCK, read_lock, 0, ret_any}, ! {"_raw_read_unlock_irq", UNLOCK, read_lock, 0, ret_any}, ! {"_raw_read_lock_irq", LOCK, read_lock, 0, ret_any}, ! {"_raw_read_lock_bh", LOCK, read_lock, 0, ret_any}, ! {"_raw_read_unlock_bh", UNLOCK, read_lock, 0, ret_any}, ! {"read_lock_irqsave", LOCK, read_lock, 0, ret_any}, ! {"read_unlock_irqrestore", UNLOCK, read_lock, 0, ret_any}, ! {"_read_lock_irqsave", LOCK, read_lock, 0, ret_any}, ! {"_read_unlock_irqrestore", UNLOCK, read_lock, 0, ret_any}, ! {"__read_lock_irqsave", LOCK, read_lock, 0, ret_any}, ! {"__read_unlock_irqrestore", UNLOCK, read_lock, 0, ret_any}, ! {"read_lock_bh", LOCK, read_lock, 0, ret_any}, ! {"read_unlock_bh", UNLOCK, read_lock, 0, ret_any}, ! {"_read_lock_bh", LOCK, read_lock, 0, ret_any}, ! {"_read_unlock_bh", UNLOCK, read_lock, 0, ret_any}, ! {"__read_lock_bh", LOCK, read_lock, 0, ret_any}, ! {"__read_unlock_bh", UNLOCK, read_lock, 0, ret_any}, ! {"__raw_read_lock_bh", LOCK, read_lock, 0, ret_any}, ! {"__raw_read_unlock_bh", UNLOCK, read_lock, 0, ret_any}, ! {"_raw_read_lock_irqsave", LOCK, read_lock, 0, ret_any}, ! {"_raw_read_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any}, ! {"_raw_read_unlock_irqrestore", UNLOCK, read_lock, 0, ret_any}, ! {"_raw_read_unlock_irqrestore", RESTORE, irq, 1, ret_any}, ! {"_raw_spin_lock_bh", LOCK, read_lock, 0, ret_any}, ! {"_raw_spin_lock_bh", LOCK, bottom_half, NO_ARG, ret_any}, ! {"_raw_spin_lock_nest_lock", LOCK, read_lock, 0, ret_any}, ! {"_raw_spin_unlock_bh", UNLOCK, read_lock, 0, ret_any}, ! {"_raw_spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any}, ! {"_raw_write_lock_irqsave", LOCK, write_lock, 0, ret_any}, ! {"_raw_write_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any}, ! {"_raw_write_unlock_irqrestore", UNLOCK, write_lock, 0, ret_any}, ! {"_raw_write_unlock_irqrestore", RESTORE, irq, 1, ret_any}, ! {"__raw_write_unlock_irqrestore", UNLOCK, write_lock, 0, ret_any}, ! {"__raw_write_unlock_irqrestore", RESTORE, irq, 1, ret_any}, ! {"generic__raw_read_trylock", LOCK, read_lock, 0, ret_one}, ! {"read_trylock", LOCK, read_lock, 0, ret_one}, ! {"_read_trylock", LOCK, read_lock, 0, ret_one}, ! {"raw_read_trylock", LOCK, read_lock, 0, ret_one}, ! {"_raw_read_trylock", LOCK, read_lock, 0, ret_one}, ! {"__raw_read_trylock", LOCK, read_lock, 0, ret_one}, ! {"__read_trylock", LOCK, read_lock, 0, ret_one}, ! {"write_lock", LOCK, write_lock, 0, ret_any}, ! {"down_write", LOCK, write_lock, 0, ret_any}, ! {"down_write_nested", LOCK, write_lock, 0, ret_any}, ! {"up_write", UNLOCK, write_lock, 0, ret_any}, ! {"write_unlock", UNLOCK, write_lock, 0, ret_any}, ! {"_write_lock", LOCK, write_lock, 0, ret_any}, ! {"_write_unlock", UNLOCK, write_lock, 0, ret_any}, ! {"__write_lock", LOCK, write_lock, 0, ret_any}, ! {"__write_unlock", UNLOCK, write_lock, 0, ret_any}, ! {"write_lock_irq", LOCK, write_lock, 0, ret_any}, ! {"write_unlock_irq", UNLOCK, write_lock, 0, ret_any}, ! {"_write_lock_irq", LOCK, write_lock, 0, ret_any}, ! {"_write_unlock_irq", UNLOCK, write_lock, 0, ret_any}, ! {"__write_lock_irq", LOCK, write_lock, 0, ret_any}, ! {"__write_unlock_irq", UNLOCK, write_lock, 0, ret_any}, ! {"_raw_write_unlock_irq", UNLOCK, write_lock, 0, ret_any}, ! {"write_lock_irqsave", LOCK, write_lock, 0, ret_any}, ! {"write_unlock_irqrestore", UNLOCK, write_lock, 0, ret_any}, ! {"_write_lock_irqsave", LOCK, write_lock, 0, ret_any}, ! {"_write_unlock_irqrestore", UNLOCK, write_lock, 0, ret_any}, ! {"__write_lock_irqsave", LOCK, write_lock, 0, ret_any}, ! {"__write_unlock_irqrestore", UNLOCK, write_lock, 0, ret_any}, ! {"write_lock_bh", LOCK, write_lock, 0, ret_any}, ! {"write_unlock_bh", UNLOCK, write_lock, 0, ret_any}, ! {"_write_lock_bh", LOCK, write_lock, 0, ret_any}, ! {"_write_unlock_bh", UNLOCK, write_lock, 0, ret_any}, ! {"__write_lock_bh", LOCK, write_lock, 0, ret_any}, ! {"__write_unlock_bh", UNLOCK, write_lock, 0, ret_any}, ! {"_raw_write_lock", LOCK, write_lock, 0, ret_any}, ! {"__raw_write_lock", LOCK, write_lock, 0, ret_any}, ! {"_raw_write_unlock", UNLOCK, write_lock, 0, ret_any}, ! {"__raw_write_unlock", UNLOCK, write_lock, 0, ret_any}, ! {"_raw_write_lock_bh", LOCK, write_lock, 0, ret_any}, ! {"_raw_write_unlock_bh", UNLOCK, write_lock, 0, ret_any}, ! {"_raw_write_lock_irq", LOCK, write_lock, 0, ret_any}, ! {"write_trylock", LOCK, write_lock, 0, ret_one}, ! {"_write_trylock", LOCK, write_lock, 0, ret_one}, ! {"raw_write_trylock", LOCK, write_lock, 0, ret_one}, ! {"_raw_write_trylock", LOCK, write_lock, 0, ret_one}, ! {"__write_trylock", LOCK, write_lock, 0, ret_one}, ! {"__raw_write_trylock", LOCK, write_lock, 0, ret_one}, ! {"down_write_trylock", LOCK, write_lock, 0, ret_one}, ! {"down_write_killable", LOCK, write_lock, 0, ret_zero}, ! {"down", LOCK, sem, 0, ret_any}, ! {"up", UNLOCK, sem, 0, ret_any}, ! {"down_trylock", LOCK, sem, 0, ret_zero}, ! {"down_timeout", LOCK, sem, 0, ret_zero}, ! {"down_interruptible", LOCK, sem, 0, ret_zero}, ! {"down_killable", LOCK, sem, 0, ret_zero}, + {"mutex_lock", LOCK, mutex, 0, ret_any}, + {"mutex_unlock", UNLOCK, mutex, 0, ret_any}, + {"mutex_lock_nested", LOCK, mutex, 0, ret_any}, + {"mutex_lock_io", LOCK, mutex, 0, ret_any}, + {"mutex_lock_io_nested", LOCK, mutex, 0, ret_any}, ! {"mutex_lock_interruptible", LOCK, mutex, 0, ret_zero}, ! {"mutex_lock_interruptible_nested", LOCK, mutex, 0, ret_zero}, ! {"mutex_lock_killable", LOCK, mutex, 0, ret_zero}, ! {"mutex_lock_killable_nested", LOCK, mutex, 0, ret_zero}, ! {"mutex_trylock", LOCK, mutex, 0, ret_one}, ! {"ww_mutex_lock", LOCK, mutex, 0, ret_any}, ! {"__ww_mutex_lock", LOCK, mutex, 0, ret_any}, ! {"ww_mutex_lock_interruptible", LOCK, mutex, 0, ret_zero}, ! {"ww_mutex_unlock", UNLOCK, mutex, 0, ret_any}, ! {"raw_local_irq_disable", LOCK, irq, NO_ARG, ret_any}, ! {"raw_local_irq_enable", UNLOCK, irq, NO_ARG, ret_any}, ! {"spin_lock_irq", LOCK, irq, NO_ARG, ret_any}, ! {"spin_unlock_irq", UNLOCK, irq, NO_ARG, ret_any}, ! {"_spin_lock_irq", LOCK, irq, NO_ARG, ret_any}, ! {"_spin_unlock_irq", UNLOCK, irq, NO_ARG, ret_any}, ! {"__spin_lock_irq", LOCK, irq, NO_ARG, ret_any}, ! {"__spin_unlock_irq", UNLOCK, irq, NO_ARG, ret_any}, ! {"_raw_spin_lock_irq", LOCK, irq, NO_ARG, ret_any}, ! {"_raw_spin_unlock_irq", UNLOCK, irq, NO_ARG, ret_any}, ! {"__raw_spin_unlock_irq", UNLOCK, irq, NO_ARG, ret_any}, ! {"spin_trylock_irq", LOCK, irq, NO_ARG, ret_one}, ! {"read_lock_irq", LOCK, irq, NO_ARG, ret_any}, ! {"read_unlock_irq", UNLOCK, irq, NO_ARG, ret_any}, ! {"_read_lock_irq", LOCK, irq, NO_ARG, ret_any}, ! {"_read_unlock_irq", UNLOCK, irq, NO_ARG, ret_any}, ! {"__read_lock_irq", LOCK, irq, NO_ARG, ret_any}, ! {"_raw_read_lock_irq", LOCK, irq, NO_ARG, ret_any}, ! {"__read_unlock_irq", UNLOCK, irq, NO_ARG, ret_any}, ! {"_raw_read_unlock_irq", UNLOCK, irq, NO_ARG, ret_any}, ! {"write_lock_irq", LOCK, irq, NO_ARG, ret_any}, ! {"write_unlock_irq", UNLOCK, irq, NO_ARG, ret_any}, ! {"_write_lock_irq", LOCK, irq, NO_ARG, ret_any}, ! {"_write_unlock_irq", UNLOCK, irq, NO_ARG, ret_any}, ! {"__write_lock_irq", LOCK, irq, NO_ARG, ret_any}, ! {"__write_unlock_irq", UNLOCK, irq, NO_ARG, ret_any}, ! {"_raw_write_lock_irq", LOCK, irq, NO_ARG, ret_any}, ! {"_raw_write_unlock_irq", UNLOCK, irq, NO_ARG, ret_any}, ! {"arch_local_irq_save", LOCK, irq, RETURN_VAL, ret_any}, ! {"arch_local_irq_restore", RESTORE, irq, 0, ret_any}, ! {"__raw_local_irq_save", LOCK, irq, RETURN_VAL, ret_any}, ! {"raw_local_irq_restore", RESTORE, irq, 0, ret_any}, ! {"spin_lock_irqsave_nested", LOCK, irq, RETURN_VAL, ret_any}, ! {"spin_lock_irqsave", LOCK, irq, 1, ret_any}, ! {"spin_unlock_irqrestore", RESTORE, irq, 1, ret_any}, ! {"_spin_lock_irqsave_nested", LOCK, irq, RETURN_VAL, ret_any}, ! {"_spin_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any}, ! {"_spin_lock_irqsave", LOCK, irq, 1, ret_any}, ! {"_spin_unlock_irqrestore", RESTORE, irq, 1, ret_any}, ! {"__spin_lock_irqsave_nested", LOCK, irq, 1, ret_any}, ! {"__spin_lock_irqsave", LOCK, irq, 1, ret_any}, ! {"__spin_unlock_irqrestore", RESTORE, irq, 1, ret_any}, ! {"_raw_spin_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any}, ! {"_raw_spin_lock_irqsave", LOCK, irq, 1, ret_any}, ! {"_raw_spin_unlock_irqrestore", RESTORE, irq, 1, ret_any}, ! {"__raw_spin_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any}, ! {"__raw_spin_unlock_irqrestore", RESTORE, irq, 1, ret_any}, ! {"_raw_spin_lock_irqsave_nested", LOCK, irq, RETURN_VAL, ret_any}, ! {"spin_trylock_irqsave", LOCK, irq, 1, ret_one}, ! {"read_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any}, ! {"read_lock_irqsave", LOCK, irq, 1, ret_any}, ! {"read_unlock_irqrestore", RESTORE, irq, 1, ret_any}, ! {"_read_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any}, ! {"_read_lock_irqsave", LOCK, irq, 1, ret_any}, ! {"_read_unlock_irqrestore", RESTORE, irq, 1, ret_any}, ! {"__read_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any}, ! {"__read_unlock_irqrestore", RESTORE, irq, 1, ret_any}, ! {"write_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any}, ! {"write_lock_irqsave", LOCK, irq, 1, ret_any}, ! {"write_unlock_irqrestore", RESTORE, irq, 1, ret_any}, ! {"_write_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any}, ! {"_write_lock_irqsave", LOCK, irq, 1, ret_any}, ! {"_write_unlock_irqrestore", RESTORE, irq, 1, ret_any}, ! {"__write_lock_irqsave", LOCK, irq, RETURN_VAL, ret_any}, ! {"__write_unlock_irqrestore", RESTORE, irq, 1, ret_any}, ! {"local_bh_disable", LOCK, bottom_half, NO_ARG, ret_any}, ! {"_local_bh_disable", LOCK, bottom_half, NO_ARG, ret_any}, ! {"__local_bh_disable", LOCK, bottom_half, NO_ARG, ret_any}, ! {"local_bh_enable", UNLOCK, bottom_half, NO_ARG, ret_any}, ! {"_local_bh_enable", UNLOCK, bottom_half, NO_ARG, ret_any}, ! {"__local_bh_enable", UNLOCK, bottom_half, NO_ARG, ret_any}, ! {"spin_lock_bh", LOCK, bottom_half, NO_ARG, ret_any}, ! {"spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any}, ! {"_spin_lock_bh", LOCK, bottom_half, NO_ARG, ret_any}, ! {"_spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any}, ! {"__spin_lock_bh", LOCK, bottom_half, NO_ARG, ret_any}, ! {"__spin_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any}, ! {"read_lock_bh", LOCK, bottom_half, NO_ARG, ret_any}, ! {"read_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any}, ! {"_read_lock_bh", LOCK, bottom_half, NO_ARG, ret_any}, ! {"_read_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any}, ! {"__read_lock_bh", LOCK, bottom_half, NO_ARG, ret_any}, ! {"__read_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any}, ! {"_raw_read_lock_bh", LOCK, bottom_half, NO_ARG, ret_any}, ! {"_raw_read_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any}, ! {"write_lock_bh", LOCK, bottom_half, NO_ARG, ret_any}, ! {"write_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any}, ! {"_write_lock_bh", LOCK, bottom_half, NO_ARG, ret_any}, ! {"_write_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any}, ! {"__write_lock_bh", LOCK, bottom_half, NO_ARG, ret_any}, ! {"__write_unlock_bh", UNLOCK, bottom_half, NO_ARG, ret_any}, ! {"_raw_write_lock_bh", LOCK, bottom_half, NO_ARG, ret_any}, ! {"_raw_write_unlock_bh",UNLOCK, bottom_half, NO_ARG, ret_any}, ! {"spin_trylock_bh", LOCK, bottom_half, NO_ARG, ret_one}, ! {"_spin_trylock_bh", LOCK, bottom_half, NO_ARG, ret_one}, ! {"__spin_trylock_bh", LOCK, bottom_half, NO_ARG, ret_one}, ! {"ffs_mutex_lock", LOCK, mutex, 0, ret_zero}, ! {"clk_prepare_lock", LOCK, prepare_lock, NO_ARG, ret_any}, ! {"clk_prepare_unlock", UNLOCK, prepare_lock, NO_ARG, ret_any}, ! {"clk_enable_lock", LOCK, enable_lock, -1, ret_any}, ! {"clk_enable_unlock", UNLOCK, enable_lock, 0, ret_any}, ! ! {"dma_resv_lock", LOCK, mutex, 0, ret_zero}, ! {"dma_resv_trylock", LOCK, mutex, 0, ret_one}, ! {"dma_resv_lock_interruptible", LOCK, mutex, 0, ret_zero}, ! {"dma_resv_unlock", UNLOCK, mutex, 0, ret_any}, ! ! {"modeset_lock", LOCK, mutex, 0, ret_zero}, ! {"drm_ modeset_lock", LOCK, mutex, 0, ret_zero}, ! {"drm_modeset_lock_single_interruptible", LOCK, mutex, 0, ret_zero}, ! {"modeset_unlock", UNLOCK, mutex, 0, ret_any}, ! ! {"reiserfs_write_lock_nested", LOCK, mutex, 0, ret_any}, ! {"reiserfs_write_unlock_nested", UNLOCK, mutex, 0, ret_any}, ! ! {"rw_lock", LOCK, write_lock, 1, ret_any}, ! {"rw_unlock", UNLOCK, write_lock, 1, ret_any}, ! ! {"sem_lock", LOCK, mutex, 0, ret_any}, ! {"sem_unlock", UNLOCK, mutex, 0, ret_any}, ! ! {}, }; ! struct macro_info { ! const char *macro; ! enum action action; ! int param; ! }; ! static struct macro_info macro_table[] = { ! {"genpd_lock", LOCK, 0}, ! {"genpd_lock_nested", LOCK, 0}, ! {"genpd_lock_interruptible", LOCK, 0}, ! {"genpd_unlock", UNLOCK, 0}, ! }; ! static const char *false_positives[][2] = { ! {"fs/jffs2/", "->alloc_sem"}, ! {"fs/xfs/", "->b_sema"}, ! {"mm/", "pvmw->ptl"}, }; ! static struct stree *start_states; ! static struct stree_stack *saved_stack; ! static struct tracker_list *locks; ! ! static void reset(struct sm_state *sm, struct expression *mod_expr) ! { ! set_state(my_id, sm->name, sm->sym, &start_state); } static struct expression *remove_spinlock_check(struct expression *expr) { if (expr->type != EXPR_CALL)
*** 404,959 **** return expr; expr = get_argument_from_call_expr(expr->args, 0); return expr; } ! static char *get_full_name(struct expression *expr, int index) { ! struct expression *arg; ! char *name = NULL; ! char *full_name = NULL; struct lock_info *lock = &lock_table[index]; if (lock->arg == RETURN_VAL) { ! name = expr_to_var(expr->left); ! full_name = make_full_name(lock->name, name); } else if (lock->arg == NO_ARG) { ! full_name = make_full_name(lock->name, ""); } else { arg = get_argument_from_call_expr(expr->args, lock->arg); if (!arg) ! goto free; ! arg = remove_spinlock_check(arg); ! name = expr_to_str(arg); ! if (!name) ! goto free; ! full_name = make_full_name(lock->name, name); } - free: - free_string(name); - return full_name; } ! static struct smatch_state *get_start_state(struct sm_state *sm) { ! int is_locked = 0; ! int is_unlocked = 0; ! if (in_tracker_list(starts_locked, my_id, sm->name, sm->sym)) ! is_locked = 1; ! if (in_tracker_list(starts_unlocked, my_id, sm->name, sm->sym)) ! is_unlocked = 1; ! if (is_locked && is_unlocked) ! return &undefined; ! if (is_locked) return &locked; ! if (is_unlocked) return &unlocked; ! return &undefined; } ! static struct smatch_state *unmatched_state(struct sm_state *sm) { ! return &start_state; } ! static void pre_merge_hook(struct sm_state *cur, struct sm_state *other) { ! if (is_impossible_path()) ! set_state(my_id, cur->name, cur->sym, &impossible); } ! static bool nestable(const char *name) { ! if (strstr(name, "read_sem:")) return true; ! if (strcmp(name, "bottom_half:") == 0) ! return true; return false; } ! static void do_lock(const char *name) { ! struct sm_state *sm; ! if (__inline_fn) return; ! sm = get_sm_state(my_id, name, NULL); if (!sm) ! add_tracker(&starts_unlocked, my_id, name, NULL); ! if (sm && slist_has_state(sm->possible, &locked) && !nestable(name)) ! sm_error("double lock '%s'", name); ! if (sm) ! func_has_transition = TRUE; ! set_state(my_id, name, NULL, &locked); } ! static void do_lock_failed(const char *name) { struct sm_state *sm; ! if (__inline_fn) return; ! sm = get_sm_state(my_id, name, NULL); if (!sm) ! add_tracker(&starts_unlocked, my_id, name, NULL); ! set_state(my_id, name, NULL, &unlocked); } ! static void do_unlock(const char *name) { struct sm_state *sm; - if (__inline_fn) - return; if (__path_is_null()) return; ! sm = get_sm_state(my_id, name, NULL); if (!sm) ! add_tracker(&starts_locked, my_id, name, NULL); ! if (sm && slist_has_state(sm->possible, &unlocked) && ! strcmp(name, "bottom_half:") != 0) ! sm_error("double unlock '%s'", name); ! if (sm) ! func_has_transition = TRUE; ! set_state(my_id, name, NULL, &unlocked); } static void match_lock_held(const char *fn, struct expression *call_expr, struct expression *assign_expr, void *_index) { int index = PTR_INT(_index); - char *lock_name; struct lock_info *lock = &lock_table[index]; if (lock->arg == NO_ARG) { ! lock_name = get_full_name(NULL, index); } else if (lock->arg == RETURN_VAL) { if (!assign_expr) return; ! lock_name = get_full_name(assign_expr, index); } else { ! lock_name = get_full_name(call_expr, index); } if (!lock_name) return; ! do_lock(lock_name); free_string(lock_name); } static void match_lock_failed(const char *fn, struct expression *call_expr, struct expression *assign_expr, void *_index) { int index = PTR_INT(_index); - char *lock_name; struct lock_info *lock = &lock_table[index]; if (lock->arg == NO_ARG) { ! lock_name = get_full_name(NULL, index); } else if (lock->arg == RETURN_VAL) { if (!assign_expr) return; ! lock_name = get_full_name(assign_expr, index); } else { ! lock_name = get_full_name(call_expr, index); } if (!lock_name) return; ! do_lock_failed(lock_name); free_string(lock_name); } static void match_returns_locked(const char *fn, struct expression *expr, void *_index) { - char *full_name = NULL; int index = PTR_INT(_index); struct lock_info *lock = &lock_table[index]; if (lock->arg != RETURN_VAL) return; ! full_name = get_full_name(expr, index); ! do_lock(full_name); } static void match_lock_unlock(const char *fn, struct expression *expr, void *_index) { - char *full_name = NULL; int index = PTR_INT(_index); struct lock_info *lock = &lock_table[index]; ! if (__inline_fn) ! return; ! ! full_name = get_full_name(expr, index); if (!full_name) return; ! if (lock->action == LOCK) ! do_lock(full_name); ! else ! do_unlock(full_name); free_string(full_name); } ! static struct locks_on_return *alloc_return(struct expression *expr) { ! struct locks_on_return *ret; ! ret = malloc(sizeof(*ret)); ! if (!get_implied_rl(expr, &ret->return_values)) ! ret->return_values = NULL; ! ret->line = get_lineno(); ! ret->locked = NULL; ! ret->unlocked = NULL; ! ret->impossible = NULL; ! return ret; } ! static int check_possible(struct sm_state *sm) { ! struct sm_state *tmp; ! int islocked = 0; ! int isunlocked = 0; ! int undef = 0; ! if (!option_spammy) ! return 0; ! FOR_EACH_PTR(sm->possible, tmp) { ! if (tmp->state == &locked) ! islocked = 1; ! if (tmp->state == &unlocked) ! isunlocked = 1; ! if (tmp->state == &start_state) { ! struct smatch_state *s; ! s = get_start_state(tmp); ! if (s == &locked) ! islocked = 1; ! else if (s == &unlocked) ! isunlocked = 1; ! else ! undef = 1; } - if (tmp->state == &undefined) - undef = 1; // i don't think this is possible any more. - } END_FOR_EACH_PTR(tmp); - if ((islocked && isunlocked) || undef) { - sm_warning("'%s' is sometimes locked here and sometimes unlocked.", sm->name); - return 1; } ! return 0; } ! static struct position warned_pos; ! static void match_return(int return_id, char *return_ranges, struct expression *expr) { ! struct locks_on_return *ret; ! struct stree *stree; ! struct sm_state *tmp; ! if (!final_pass) ! return; ! if (__inline_fn) ! return; ! if (expr && cmp_pos(expr->pos, warned_pos) == 0) ! return; ! ret = alloc_return(expr); ! stree = __get_cur_stree(); ! FOR_EACH_MY_SM(my_id, stree, tmp) { ! if (tmp->state == &locked) { ! add_tracker(&ret->locked, tmp->owner, tmp->name, ! tmp->sym); ! } else if (tmp->state == &unlocked) { ! add_tracker(&ret->unlocked, tmp->owner, tmp->name, ! tmp->sym); ! } else if (tmp->state == &start_state) { ! struct smatch_state *s; ! s = get_start_state(tmp); ! if (s == &locked) ! add_tracker(&ret->locked, tmp->owner, tmp->name, ! tmp->sym); ! if (s == &unlocked) ! add_tracker(&ret->unlocked, tmp->owner,tmp->name, ! tmp->sym); ! } else if (tmp->state == &impossible) { ! add_tracker(&ret->impossible, tmp->owner, tmp->name, ! tmp->sym); ! } else { ! if (check_possible(tmp)) { ! if (expr) ! warned_pos = expr->pos; ! } ! } ! } END_FOR_EACH_SM(tmp); ! add_ptr_list(&all_returns, ret); } ! static void add_line(struct range_list **rl, int line) { ! sval_t sval = sval_type_val(&int_ctype, line); ! add_range(rl, sval, sval); } ! static int line_printed(struct range_list *rl, int line) { ! sval_t sval = sval_type_val(&int_ctype, line); ! return rl_has_sval(rl, sval); } ! static void print_inconsistent_returns(struct tracker *lock, ! struct smatch_state *start) { ! struct locks_on_return *tmp; ! struct range_list *printed = NULL; int i; ! sm_warning("inconsistent returns '%s'.", lock->name); ! sm_printf(" Locked on: "); ! i = 0; ! FOR_EACH_PTR(all_returns, tmp) { ! if (line_printed(printed, tmp->line)) ! continue; ! if (in_tracker_list(tmp->unlocked, lock->owner, lock->name, lock->sym)) ! continue; ! if (in_tracker_list(tmp->locked, lock->owner, lock->name, lock->sym)) { ! if (i++) ! sm_printf(" "); ! sm_printf("line %d\n", tmp->line); ! add_line(&printed, tmp->line); ! continue; ! } ! if (start == &locked) { ! if (i++) ! sm_printf(" "); ! sm_printf("line %d\n", tmp->line); ! add_line(&printed, tmp->line); ! } ! } END_FOR_EACH_PTR(tmp); ! sm_printf(" Unlocked on: "); ! printed = NULL; ! i = 0; ! FOR_EACH_PTR(all_returns, tmp) { ! if (line_printed(printed, tmp->line)) ! continue; ! if (in_tracker_list(tmp->unlocked, lock->owner, lock->name, lock->sym)) { ! if (i++) ! sm_printf(" "); ! sm_printf("line %d\n", tmp->line); ! add_line(&printed, tmp->line); ! continue; } ! if (in_tracker_list(tmp->locked, lock->owner, lock->name, lock->sym)) ! continue; ! if (start == &unlocked) { ! if (i++) ! sm_printf(" "); ! sm_printf("line %d\n", tmp->line); ! add_line(&printed, tmp->line); } ! } END_FOR_EACH_PTR(tmp); ! } - static int matches_return_type(struct range_list *rl, enum return_type type) - { - sval_t zero_sval = ll_to_sval(0); - sval_t one_sval = ll_to_sval(1); ! /* All these double negatives are super ugly! */ ! switch (type) { ! case ret_zero: ! return !possibly_true_rl(rl, SPECIAL_NOTEQUAL, alloc_rl(zero_sval, zero_sval)); ! case ret_one: ! return !possibly_true_rl(rl, SPECIAL_NOTEQUAL, alloc_rl(one_sval, one_sval)); ! case ret_non_zero: ! return !possibly_true_rl(rl, SPECIAL_EQUAL, alloc_rl(zero_sval, zero_sval)); ! case ret_negative: ! return !possibly_true_rl(rl, SPECIAL_GTE, alloc_rl(zero_sval, zero_sval)); ! case ret_positive: ! return !possibly_true_rl(rl, '<', alloc_rl(zero_sval, zero_sval)); ! case ret_any: ! default: ! return 1; } ! } ! static int match_held(struct tracker *lock, struct locks_on_return *this_return, struct smatch_state *start) ! { ! if (in_tracker_list(this_return->impossible, lock->owner, lock->name, lock->sym)) ! return 0; ! if (in_tracker_list(this_return->unlocked, lock->owner, lock->name, lock->sym)) ! return 0; ! if (in_tracker_list(this_return->locked, lock->owner, lock->name, lock->sym)) ! return 1; ! if (start == &unlocked) ! return 0; ! return 1; } ! static int match_released(struct tracker *lock, struct locks_on_return *this_return, struct smatch_state *start) { ! if (in_tracker_list(this_return->impossible, lock->owner, lock->name, lock->sym)) ! return 0; ! if (in_tracker_list(this_return->unlocked, lock->owner, lock->name, lock->sym)) ! return 1; ! if (in_tracker_list(this_return->locked, lock->owner, lock->name, lock->sym)) ! return 0; ! if (start == &unlocked) ! return 1; ! return 0; } ! static int held_on_return(struct tracker *lock, struct smatch_state *start, enum return_type type) { ! struct locks_on_return *tmp; ! FOR_EACH_PTR(all_returns, tmp) { ! if (!matches_return_type(tmp->return_values, type)) ! continue; ! if (match_held(lock, tmp, start)) ! return 1; ! } END_FOR_EACH_PTR(tmp); ! return 0; } ! static int released_on_return(struct tracker *lock, struct smatch_state *start, enum return_type type) { ! struct locks_on_return *tmp; ! FOR_EACH_PTR(all_returns, tmp) { ! if (!matches_return_type(tmp->return_values, type)) ! continue; ! if (match_released(lock, tmp, start)) ! return 1; ! } END_FOR_EACH_PTR(tmp); ! return 0; } ! static void check_returns_consistently(struct tracker *lock, ! struct smatch_state *start) { ! struct symbol *type; ! if (!held_on_return(lock, start, ret_any) || ! !released_on_return(lock, start, ret_any)) return; ! if (held_on_return(lock, start, ret_zero) && ! !held_on_return(lock, start, ret_non_zero)) return; ! if (held_on_return(lock, start, ret_positive) && ! !held_on_return(lock, start, ret_zero)) return; ! if (held_on_return(lock, start, ret_positive) && ! !held_on_return(lock, start, ret_negative)) return; ! ! type = cur_func_return_type(); ! if (type && type->type == SYM_PTR) { ! if (held_on_return(lock, start, ret_non_zero) && ! !held_on_return(lock, start, ret_zero)) return; } ! print_inconsistent_returns(lock, start); } ! static void check_consistency(struct symbol *sym) { ! struct tracker *tmp; ! FOR_EACH_PTR(starts_locked, tmp) { ! if (in_tracker_list(starts_unlocked, tmp->owner, tmp->name, ! tmp->sym)) ! sm_error("locking inconsistency. We assume " ! "'%s' is both locked and unlocked at the " ! "start.", ! tmp->name); ! } END_FOR_EACH_PTR(tmp); ! FOR_EACH_PTR(starts_locked, tmp) { ! check_returns_consistently(tmp, &locked); ! } END_FOR_EACH_PTR(tmp); ! ! FOR_EACH_PTR(starts_unlocked, tmp) { ! check_returns_consistently(tmp, &unlocked); ! } END_FOR_EACH_PTR(tmp); } ! static void clear_lists(void) { ! struct locks_on_return *tmp; ! func_has_transition = FALSE; ! free_trackers_and_list(&starts_locked); ! free_trackers_and_list(&starts_unlocked); ! ! FOR_EACH_PTR(all_returns, tmp) { ! free_trackers_and_list(&tmp->locked); ! free_trackers_and_list(&tmp->unlocked); ! free(tmp); ! } END_FOR_EACH_PTR(tmp); ! __free_ptr_list((struct ptr_list **)&all_returns); } ! static void match_func_end(struct symbol *sym) { ! if (__inline_fn) ! return; ! if (func_has_transition) ! check_consistency(sym); } ! static void match_after_func(struct symbol *sym) { ! if (__inline_fn) ! return; ! clear_lists(); } ! static void register_lock(int index) { ! struct lock_info *lock = &lock_table[index]; ! void *idx = INT_PTR(index); ! if (lock->return_type == ret_non_zero) { ! return_implies_state(lock->function, 1, INT_MAX, &match_lock_held, idx); ! return_implies_state(lock->function, 0, 0, &match_lock_failed, idx); ! } else if (lock->return_type == ret_any && lock->arg == RETURN_VAL) { ! add_function_assign_hook(lock->function, &match_returns_locked, idx); ! } else if (lock->return_type == ret_any) { ! add_function_hook(lock->function, &match_lock_unlock, idx); ! } else if (lock->return_type == ret_zero) { ! return_implies_state(lock->function, 0, 0, &match_lock_held, idx); ! return_implies_state(lock->function, -4095, -1, &match_lock_failed, idx); ! } else if (lock->return_type == ret_one) { ! return_implies_state(lock->function, 1, 1, &match_lock_held, idx); ! return_implies_state(lock->function, 0, 0, &match_lock_failed, idx); ! } } ! static void load_table(struct lock_info *_lock_table, int size) { ! int i; ! lock_table = _lock_table; for (i = 0; i < size; i++) { if (lock_table[i].action == LOCK) register_lock(i); else --- 448,1239 ---- 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 char *lock_to_name_sym(struct expression *expr, struct symbol **sym) ! { ! expr = remove_spinlock_check(expr); ! expr = filter_kernel_args(expr); ! return expr_to_str_sym(expr, sym); ! } ! ! static char *get_full_name(struct expression *expr, int index, struct symbol **sym) ! { struct lock_info *lock = &lock_table[index]; + struct expression *arg; + *sym = NULL; if (lock->arg == RETURN_VAL) { ! return expr_to_var_sym(strip_expr(expr->left), sym); } else if (lock->arg == NO_ARG) { ! return alloc_string(get_lock_name(lock->type)); } else { arg = get_argument_from_call_expr(expr->args, lock->arg); if (!arg) ! return NULL; ! return lock_to_name_sym(arg, sym); } } ! static struct smatch_state *unmatched_state(struct sm_state *sm) { ! return &start_state; ! } ! static void pre_merge_hook(struct sm_state *cur, struct sm_state *other) ! { ! if (is_impossible_path()) ! set_state(my_id, cur->name, cur->sym, &impossible); ! } ! ! static struct smatch_state *merge_func(struct smatch_state *s1, struct smatch_state *s2) ! { ! if (s1 == &impossible) ! return s2; ! if (s2 == &impossible) ! return s1; ! return &merged; ! } ! ! static struct smatch_state *action_to_state(enum action lock_unlock) ! { ! switch (lock_unlock) { ! case LOCK: return &locked; ! case UNLOCK: return &unlocked; ! case RESTORE: ! return &restore; ! } ! return NULL; } ! static struct sm_state *get_best_match(const char *key, enum action lock_unlock) { ! struct sm_state *sm; ! struct sm_state *match; ! int cnt = 0; ! int start_pos, state_len, key_len, chunks, i; ! ! if (strncmp(key, "$->", 3) == 0) ! key += 3; ! ! key_len = strlen(key); ! chunks = 0; ! for (i = key_len - 1; i > 0; i--) { ! if (key[i] == '>' || key[i] == '.') ! chunks++; ! if (chunks == 2) { ! key += (i + 1); ! key_len = strlen(key); ! break; ! } ! } ! ! FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { ! if (((lock_unlock == UNLOCK || lock_unlock == RESTORE) && ! sm->state != &locked) || ! (lock_unlock == LOCK && sm->state != &unlocked)) ! continue; ! state_len = strlen(sm->name); ! if (state_len < key_len) ! continue; ! start_pos = state_len - key_len; ! if ((start_pos == 0 || !isalnum(sm->name[start_pos - 1])) && ! strcmp(sm->name + start_pos, key) == 0) { ! cnt++; ! match = sm; ! } ! } END_FOR_EACH_SM(sm); ! ! if (cnt == 1) ! return match; ! return NULL; } ! static void use_best_match(char *key, enum action lock_unlock) { ! struct sm_state *match; ! ! match = get_best_match(key, lock_unlock); ! if (match) ! set_state(my_id, match->name, match->sym, action_to_state(lock_unlock)); ! else ! set_state(my_id, key, NULL, action_to_state(lock_unlock)); } ! static void set_start_state(const char *name, struct symbol *sym, struct smatch_state *start) { ! struct smatch_state *orig; ! ! orig = get_state_stree(start_states, my_id, name, sym); ! if (!orig) ! set_state_stree(&start_states, my_id, name, sym, start); ! else if (orig != start) ! set_state_stree(&start_states, my_id, name, sym, &undefined); ! } ! ! static bool common_false_positive(const char *name) ! { ! const char *path, *lname; ! int i, len_total, len_path, len_name, skip; ! ! if (!get_filename()) ! return false; ! ! len_total = strlen(name); ! for (i = 0; i < ARRAY_SIZE(false_positives); i++) { ! path = false_positives[i][0]; ! lname = false_positives[i][1]; ! ! len_path = strlen(path); ! len_name = strlen(lname); ! ! if (len_name > len_total) ! continue; ! skip = len_total - len_name; ! ! if (strncmp(get_filename(), path, len_path) == 0 && ! strcmp(name + skip, lname) == 0) return true; ! } ! return false; } ! static void warn_on_double(struct sm_state *sm, struct smatch_state *state) { ! struct sm_state *tmp; ! if (!sm) return; ! FOR_EACH_PTR(sm->possible, tmp) { ! if (tmp->state == state) ! goto found; ! } END_FOR_EACH_PTR(tmp); ! ! return; ! found: ! if (strcmp(sm->name, "bottom_half") == 0) ! return; ! if (common_false_positive(sm->name)) ! return; ! sm_msg("error: double %s '%s' (orig line %u)", ! state->name, sm->name, tmp->line); ! } ! ! static bool handle_macro_lock_unlock(void) ! { ! struct expression *expr, *arg; ! struct macro_info *info; ! struct sm_state *sm; ! struct symbol *sym; ! const char *macro; ! char *name; ! bool ret = false; ! int i; ! ! expr = last_ptr_list((struct ptr_list *)big_expression_stack); ! while (expr && expr->type == EXPR_ASSIGNMENT) ! expr = strip_expr(expr->right); ! if (!expr || expr->type != EXPR_CALL) ! return false; ! ! macro = get_macro_name(expr->pos); ! if (!macro) ! return false; ! ! for (i = 0; i < ARRAY_SIZE(macro_table); i++) { ! info = &macro_table[i]; ! ! if (strcmp(macro, info->macro) != 0) ! continue; ! arg = get_argument_from_call_expr(expr->args, info->param); ! name = expr_to_str_sym(arg, &sym); ! if (!name || !sym) ! goto free; ! sm = get_sm_state(my_id, name, sym); ! ! if (info->action == LOCK) { if (!sm) ! set_start_state(name, sym, &unlocked); ! if (sm && sm->line != expr->pos.line) ! warn_on_double(sm, &locked); ! set_state(my_id, name, sym, &locked); ! } else { ! if (!sm) ! set_start_state(name, sym, &locked); ! if (sm && sm->line != expr->pos.line) ! warn_on_double(sm, &unlocked); ! set_state(my_id, name, sym, &unlocked); ! } ! ret = true; ! free: ! free_string(name); ! return ret; ! } ! return false; } ! static void do_lock(const char *name, struct symbol *sym, struct lock_info *info) { struct sm_state *sm; ! if (handle_macro_lock_unlock()) return; ! add_tracker(&locks, my_id, name, sym); ! ! sm = get_sm_state(my_id, name, sym); if (!sm) ! set_start_state(name, sym, &unlocked); ! warn_on_double(sm, &locked); ! set_state(my_id, name, sym, &locked); } ! static void do_lock_failed(const char *name, struct symbol *sym) { + add_tracker(&locks, my_id, name, sym); + set_state(my_id, name, sym, &unlocked); + } + + static void do_unlock(const char *name, struct symbol *sym, struct lock_info *info) + { struct sm_state *sm; if (__path_is_null()) return; ! ! if (handle_macro_lock_unlock()) ! return; ! ! add_tracker(&locks, my_id, name, sym); ! sm = get_sm_state(my_id, name, sym); ! if (!sm) { ! sm = get_best_match(name, UNLOCK); ! if (sm) { ! name = sm->name; ! sym = sm->sym; ! } ! } if (!sm) ! set_start_state(name, sym, &locked); ! warn_on_double(sm, &unlocked); ! set_state(my_id, name, sym, &unlocked); } + static void do_restore(const char *name, struct symbol *sym, struct lock_info *info) + { + if (__path_is_null()) + return; + + if (!get_state(my_id, name, sym)) + set_start_state(name, sym, &locked); + + add_tracker(&locks, my_id, name, sym); + set_state(my_id, name, sym, &restore); + } + static void match_lock_held(const char *fn, struct expression *call_expr, struct expression *assign_expr, void *_index) { int index = PTR_INT(_index); struct lock_info *lock = &lock_table[index]; + char *lock_name; + struct symbol *sym; if (lock->arg == NO_ARG) { ! lock_name = get_full_name(NULL, index, &sym); } else if (lock->arg == RETURN_VAL) { if (!assign_expr) return; ! lock_name = get_full_name(assign_expr, index, &sym); } else { ! lock_name = get_full_name(call_expr, index, &sym); } if (!lock_name) return; ! do_lock(lock_name, sym, lock); free_string(lock_name); } static void match_lock_failed(const char *fn, struct expression *call_expr, struct expression *assign_expr, void *_index) { int index = PTR_INT(_index); struct lock_info *lock = &lock_table[index]; + char *lock_name; + struct symbol *sym; if (lock->arg == NO_ARG) { ! lock_name = get_full_name(NULL, index, &sym); } else if (lock->arg == RETURN_VAL) { if (!assign_expr) return; ! lock_name = get_full_name(assign_expr, index, &sym); } else { ! lock_name = get_full_name(call_expr, index, &sym); } if (!lock_name) return; ! do_lock_failed(lock_name, sym); free_string(lock_name); } static void match_returns_locked(const char *fn, struct expression *expr, void *_index) { int index = PTR_INT(_index); struct lock_info *lock = &lock_table[index]; + char *full_name; + struct symbol *sym; if (lock->arg != RETURN_VAL) return; ! full_name = get_full_name(expr, index, &sym); ! if (!full_name) ! return; ! do_lock(full_name, sym, lock); } static void match_lock_unlock(const char *fn, struct expression *expr, void *_index) { int index = PTR_INT(_index); struct lock_info *lock = &lock_table[index]; + char *full_name; + struct symbol *sym; ! full_name = get_full_name(expr, index, &sym); if (!full_name) return; ! switch (lock->action) { ! case LOCK: ! do_lock(full_name, sym, lock); ! break; ! case UNLOCK: ! do_unlock(full_name, sym, lock); ! break; ! case RESTORE: ! do_restore(full_name, sym, lock); ! break; ! } free_string(full_name); } ! static struct smatch_state *get_start_state(struct sm_state *sm) { ! struct smatch_state *orig; ! orig = get_state_stree(start_states, my_id, sm->name, sm->sym); ! if (orig) ! return orig; ! return &undefined; } ! static int get_param_lock_name(struct sm_state *sm, struct expression *expr, ! const char **name) { ! char *other_name; ! struct symbol *other_sym; ! const char *param_name; ! int param; ! *name = sm->name; ! param = get_param_num_from_sym(sm->sym); ! if (param >= 0) { ! param_name = get_param_name(sm); ! if (param_name) ! *name = param_name; ! return param; ! } ! if (expr) { ! struct symbol *ret_sym; ! char *ret_str; ! ! ret_str = expr_to_str_sym(expr, &ret_sym); ! if (ret_str && ret_sym == sm->sym) { ! param_name = state_name_to_param_name(sm->name, ret_str); ! if (param_name) { ! free_string(ret_str); ! *name = param_name; ! return -1; } } ! free_string(ret_str); ! } ! ! other_name = get_other_name_sym(sm->name, sm->sym, &other_sym); ! if (!other_name) ! return -2; ! param = get_param_num_from_sym(other_sym); ! if (param < 0) ! return -2; ! ! param_name = get_param_name_var_sym(other_name, other_sym); ! free_string(other_name); ! if (param_name) ! *name = param_name; ! return param; } ! static int get_db_type(struct sm_state *sm) ! { ! if (sm->state == get_start_state(sm)) { ! if (sm->state == &locked) ! return KNOWN_LOCKED; ! if (sm->state == &unlocked) ! return KNOWN_UNLOCKED; ! } ! if (sm->state == &locked) ! return LOCKED; ! if (sm->state == &unlocked) ! return UNLOCKED; ! if (sm->state == &restore) ! return LOCK_RESTORED; ! return LOCKED; ! } ! ! static void match_return_info(int return_id, char *return_ranges, struct expression *expr) { ! struct sm_state *sm; ! const char *param_name; ! int param; ! FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { ! if (sm->state != &locked && ! sm->state != &unlocked && ! sm->state != &restore) ! continue; ! param = get_param_lock_name(sm, expr, &param_name); ! sql_insert_return_states(return_id, return_ranges, ! get_db_type(sm), ! param, param_name, ""); ! } END_FOR_EACH_SM(sm); ! } ! enum { ! ERR_PTR, VALID_PTR, NEGATIVE, ZERO, POSITIVE, NUM_BUCKETS, ! }; ! static bool is_EINTR(struct range_list *rl) ! { ! sval_t sval; ! if (!rl_to_sval(rl, &sval)) ! return false; ! return sval.value == -4; } ! static int success_fail_positive(struct range_list *rl) { ! /* void returns are the same as success (zero in the kernel) */ ! if (!rl) ! return ZERO; ! if (rl_type(rl)->type != SYM_PTR && sval_is_negative(rl_min(rl))) ! return NEGATIVE; ! ! if (rl_min(rl).value == 0 && rl_max(rl).value == 0) ! return ZERO; ! ! if (is_err_ptr(rl_min(rl)) && ! is_err_ptr(rl_max(rl))) ! return ERR_PTR; ! ! /* ! * Trying to match ERR_PTR(ret) but without the expression struct. ! * Ugly... ! */ ! if (type_bits(&long_ctype) == 64 && ! rl_type(rl)->type == SYM_PTR && ! rl_min(rl).value == INT_MIN) ! return ERR_PTR; ! ! return POSITIVE; } ! static bool sym_in_lock_table(struct symbol *sym) { ! int i; ! 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 bool func_in_lock_table(struct expression *expr) { ! if (expr->type != EXPR_SYMBOL) ! return false; ! return sym_in_lock_table(expr->symbol); ! } ! ! static void check_lock(char *name, struct symbol *sym) ! { ! struct range_list *locked_lines = NULL; ! struct range_list *unlocked_lines = NULL; ! int locked_buckets[NUM_BUCKETS] = {}; ! int unlocked_buckets[NUM_BUCKETS] = {}; ! struct stree *stree, *orig; ! struct sm_state *return_sm; ! struct sm_state *sm; ! sval_t line = sval_type_val(&int_ctype, 0); ! int bucket; int i; ! if (sym_in_lock_table(cur_func_sym)) ! return; ! FOR_EACH_PTR(get_all_return_strees(), stree) { ! orig = __swap_cur_stree(stree); ! if (is_impossible_path()) ! goto swap_stree; ! ! return_sm = get_sm_state(RETURN_ID, "return_ranges", NULL); ! if (!return_sm) ! goto swap_stree; ! line.value = return_sm->line; ! ! sm = get_sm_state(my_id, name, sym); ! if (!sm) ! goto swap_stree; ! ! if (parent_is_gone_var_sym(sm->name, sm->sym)) ! goto swap_stree; ! ! if (sm->state != &locked && sm->state != &unlocked) ! goto swap_stree; ! ! if (sm->state == &unlocked && is_EINTR(estate_rl(return_sm->state))) ! goto swap_stree; ! ! bucket = success_fail_positive(estate_rl(return_sm->state)); ! if (sm->state == &locked) { ! add_range(&locked_lines, line, line); ! locked_buckets[bucket] = true; } ! if (sm->state == &unlocked) { ! add_range(&unlocked_lines, line, line); ! unlocked_buckets[bucket] = true; } ! swap_stree: ! __swap_cur_stree(orig); ! } END_FOR_EACH_PTR(stree); ! if (!locked_lines || !unlocked_lines) ! return; ! for (i = 0; i < NUM_BUCKETS; i++) { ! if (locked_buckets[i] && unlocked_buckets[i]) ! goto complain; } ! if (locked_buckets[NEGATIVE] && ! (unlocked_buckets[ZERO] || unlocked_buckets[POSITIVE])) ! goto complain; ! if (locked_buckets[ERR_PTR]) ! goto complain; ! ! return; ! ! complain: ! sm_msg("warn: inconsistent returns '%s'.", name); ! sm_printf(" Locked on : %s\n", show_rl(locked_lines)); ! sm_printf(" Unlocked on: %s\n", show_rl(unlocked_lines)); } ! static void match_func_end(struct symbol *sym) { ! struct tracker *tracker; ! ! FOR_EACH_PTR(locks, tracker) { ! check_lock(tracker->name, tracker->sym); ! } END_FOR_EACH_PTR(tracker); } ! static void register_lock(int index) { ! struct lock_info *lock = &lock_table[index]; ! void *idx = INT_PTR(index); ! if (lock->return_type == ret_one) { ! return_implies_state(lock->function, 1, 1, &match_lock_held, idx); ! return_implies_state(lock->function, 0, 0, &match_lock_failed, idx); ! } else if (lock->return_type == ret_any && lock->arg == RETURN_VAL) { ! add_function_assign_hook(lock->function, &match_returns_locked, idx); ! } else if (lock->return_type == ret_any) { ! add_function_hook(lock->function, &match_lock_unlock, idx); ! } else if (lock->return_type == ret_zero) { ! return_implies_state(lock->function, 0, 0, &match_lock_held, idx); ! return_implies_state(lock->function, -4095, -1, &match_lock_failed, idx); ! } else if (lock->return_type == ret_valid_ptr) { ! return_implies_state_sval(lock->function, valid_ptr_min_sval, valid_ptr_max_sval, &match_lock_held, idx); ! } } ! static void load_table(struct lock_info *lock_table) { ! int i; ! for (i = 0; lock_table[i].function != NULL; i++) { ! if (lock_table[i].action == LOCK) ! register_lock(i); ! else ! add_function_hook(lock_table[i].function, &match_lock_unlock, INT_PTR(i)); ! } } ! static void db_param_locked_unlocked(struct expression *expr, int param, char *key, char *value, enum action lock_unlock) { ! struct expression *call, *arg; ! char *name; ! struct symbol *sym; ! call = expr; ! while (call->type == EXPR_ASSIGNMENT) ! call = strip_expr(call->right); ! if (call->type != EXPR_CALL) return; ! if (func_in_lock_table(call->fn)) return; ! if (param == -2) { ! use_best_match(key, lock_unlock); return; + } ! if (param == -1) { ! if (expr->type != EXPR_ASSIGNMENT) return; ! name = get_variable_from_key(expr->left, key, &sym); ! } else { ! arg = get_argument_from_call_expr(call->args, param); ! if (!arg) return; + + name = get_variable_from_key(arg, key, &sym); } + if (!name || !sym) + goto free; ! if (lock_unlock == LOCK) ! do_lock(name, sym, NULL); ! else if (lock_unlock == UNLOCK) ! do_unlock(name, sym, NULL); ! else if (lock_unlock == RESTORE) ! do_restore(name, sym, NULL); ! ! free: ! free_string(name); } ! static void db_param_locked(struct expression *expr, int param, char *key, char *value) { ! db_param_locked_unlocked(expr, param, key, value, LOCK); ! } ! static void db_param_unlocked(struct expression *expr, int param, char *key, char *value) ! { ! db_param_locked_unlocked(expr, param, key, value, UNLOCK); ! } ! static void db_param_restore(struct expression *expr, int param, char *key, char *value) ! { ! db_param_locked_unlocked(expr, param, key, value, RESTORE); } ! static int get_caller_param_lock_name(struct expression *call, struct sm_state *sm, const char **name) { ! struct expression *arg; ! char *arg_name; ! int param; ! param = 0; ! FOR_EACH_PTR(call->args, arg) { ! arg_name = sm_to_arg_name(arg, sm); ! if (arg_name) { ! *name = arg_name; ! return param; ! } ! param++; ! } END_FOR_EACH_PTR(arg); ! *name = sm->name; ! return -2; } ! static void match_call_info(struct expression *expr) { ! struct sm_state *sm; ! const char *param_name; ! int locked_type; ! int param; ! FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { ! param = get_caller_param_lock_name(expr, sm, &param_name); ! if (sm->state == &locked) ! locked_type = LOCKED; ! else if (sm->state == &half_locked || ! slist_has_state(sm->possible, &locked)) ! locked_type = HALF_LOCKED; ! else ! continue; ! sql_insert_caller_info(expr, locked_type, param, param_name, "xxx type"); ! ! } END_FOR_EACH_SM(sm); } ! static void match_save_states(struct expression *expr) { ! push_stree(&saved_stack, start_states); ! start_states = NULL; } ! static void match_restore_states(struct expression *expr) { ! start_states = pop_stree(&saved_stack); ! } ! static void match_after_func(struct symbol *sym) ! { ! free_stree(&start_states); } ! static void match_dma_resv_lock_NULL(const char *fn, struct expression *call_expr, ! struct expression *assign_expr, void *_index) { ! struct expression *lock, *ctx; ! char *lock_name; ! struct symbol *sym; ! 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_name = lock_to_name_sym(lock, &sym); if (!lock_name || !sym) goto free; do_lock(lock_name, sym, NULL);
*** 951,966 **** { int i; lock_table = _lock_table; ! for (i = 0; i < size; i++) { ! if (lock_table[i].action == LOCK) ! register_lock(i); ! else ! add_function_hook(lock_table[i].function, &match_lock_unlock, INT_PTR(i)); ! } } /* print_held_locks() is used in check_call_tree.c */ void print_held_locks(void) { --- 1231,1246 ---- 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_name = lock_to_name_sym(lock, &sym); ! if (!lock_name || !sym) ! goto free; ! do_lock(lock_name, sym, NULL); ! free: ! free_string(lock_name); } /* print_held_locks() is used in check_call_tree.c */ void print_held_locks(void) {
*** 980,998 **** void check_locking(int id) { my_id = id; ! if (option_project == PROJ_WINE) ! load_table(wine_lock_table, ARRAY_SIZE(wine_lock_table)); ! else if (option_project == PROJ_KERNEL) ! load_table(kernel_lock_table, ARRAY_SIZE(kernel_lock_table)); ! else return; add_unmatched_state_hook(my_id, &unmatched_state); add_pre_merge_hook(my_id, &pre_merge_hook); ! add_split_return_callback(match_return); add_hook(&match_func_end, END_FUNC_HOOK); add_hook(&match_after_func, AFTER_FUNC_HOOK); } --- 1260,1290 ---- void check_locking(int id) { my_id = id; ! if (option_project != PROJ_KERNEL) return; + load_table(lock_table); + + set_dynamic_states(my_id); add_unmatched_state_hook(my_id, &unmatched_state); add_pre_merge_hook(my_id, &pre_merge_hook); ! add_merge_hook(my_id, &merge_func); ! add_modification_hook(my_id, &reset); ! add_hook(&match_func_end, END_FUNC_HOOK); + add_hook(&match_after_func, AFTER_FUNC_HOOK); + add_hook(&match_save_states, INLINE_FN_START); + add_hook(&match_restore_states, INLINE_FN_END); + add_hook(&match_call_info, FUNCTION_CALL_HOOK); + + add_split_return_callback(match_return_info); + select_return_states_hook(LOCKED, &db_param_locked); + select_return_states_hook(UNLOCKED, &db_param_unlocked); + select_return_states_hook(LOCK_RESTORED, &db_param_restore); + + return_implies_state("dma_resv_lock", -4095, -1, &match_dma_resv_lock_NULL, 0); }