1 /* 2 * Copyright (C) 2009 Dan Carpenter. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 2 7 * of the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt 16 */ 17 18 /* 19 * This test checks that locks are held the same across all returns. 20 * 21 * Of course, some functions are designed to only hold the locks on success. 22 * Oh well... We can rewrite it later if we want. 23 * 24 * The list of wine locking functions came from an earlier script written 25 * by Michael Stefaniuc. 26 * 27 */ 28 29 #include "parse.h" 30 #include "smatch.h" 31 #include "smatch_extra.h" 32 #include "smatch_slist.h" 33 34 static int my_id; 35 36 static int func_has_transition; 37 38 STATE(locked); 39 STATE(start_state); 40 STATE(unlocked); 41 STATE(impossible); 42 43 enum action { 44 LOCK, 45 UNLOCK, 46 }; 47 48 enum return_type { 49 ret_any, 50 ret_non_zero, 51 ret_zero, 52 ret_one, 53 ret_negative, 54 ret_positive, 55 }; 56 57 #define RETURN_VAL -1 58 #define NO_ARG -2 59 60 struct lock_info { 61 const char *function; 62 enum action action; 63 const char *name; 64 int arg; 65 enum return_type return_type; 66 }; 67 68 static struct lock_info wine_lock_table[] = { 69 {"create_window_handle", LOCK, "create_window_handle", RETURN_VAL, ret_non_zero}, 70 {"WIN_GetPtr", LOCK, "create_window_handle", RETURN_VAL, ret_non_zero}, 71 {"WIN_ReleasePtr", UNLOCK, "create_window_handle", 0, ret_any}, 72 {"EnterCriticalSection", LOCK, "CriticalSection", 0, ret_any}, 73 {"LeaveCriticalSection", UNLOCK, "CriticalSection", 0, ret_any}, 74 {"RtlEnterCriticalSection", LOCK, "RtlCriticalSection", 0, ret_any}, 75 {"RtlLeaveCriticalSection", UNLOCK, "RtlCriticalSection", 0, ret_any}, 76 {"GDI_GetObjPtr", LOCK, "GDI_Get", 0, ret_non_zero}, 77 {"GDI_ReleaseObj", UNLOCK, "GDI_Get", 0, ret_any}, 78 {"LdrLockLoaderLock", LOCK, "LdrLockLoaderLock", 2, ret_any}, 79 {"LdrUnlockLoaderLock", UNLOCK, "LdrLockLoaderLock", 1, ret_any}, 80 {"_lock", LOCK, "_lock", 0, ret_any}, 81 {"_unlock", UNLOCK, "_lock", 0, ret_any}, 82 {"msiobj_lock", LOCK, "msiobj_lock", 0, ret_any}, 83 {"msiobj_unlock", UNLOCK, "msiobj_lock", 0, ret_any}, 84 {"RtlAcquirePebLock", LOCK, "PebLock", NO_ARG, ret_any}, 85 {"RtlReleasePebLock", UNLOCK, "PebLock", NO_ARG, ret_any}, 86 {"server_enter_uninterrupted_section", LOCK, "server_uninterrupted_section", 0, ret_any}, 87 {"server_leave_uninterrupted_section", UNLOCK, "server_uninterrupted_section", 0, ret_any}, 88 {"RtlLockHeap", LOCK, "RtlLockHeap", 0, ret_any}, 89 {"RtlUnlockHeap", UNLOCK, "RtlLockHeap", 0, ret_any}, 90 {"_EnterSysLevel", LOCK, "SysLevel", 0, ret_any}, 91 {"_LeaveSysLevel", UNLOCK, "SysLevel", 0, ret_any}, 92 {"USER_Lock", LOCK, "USER_Lock", NO_ARG, ret_any}, 93 {"USER_Unlock", UNLOCK, "USER_Lock", NO_ARG, ret_any}, 94 {"wine_tsx11_lock", LOCK, "wine_tsx11_lock", NO_ARG, ret_any}, 95 {"wine_tsx11_unlock", UNLOCK, "wine_tsx11_lock", NO_ARG, ret_any}, 96 {"wine_tsx11_lock_ptr", LOCK, "wine_tsx11_lock_ptr", NO_ARG, ret_any}, 97 {"wine_tsx11_unlock_ptr", UNLOCK, "wine_tsx11_lock_ptr", NO_ARG, ret_any}, 98 {"wined3d_mutex_lock", LOCK, "wined3d_mutex_lock", NO_ARG, ret_any}, 99 {"wined3d_mutex_unlock", UNLOCK, "wined3d_mutex_lock", NO_ARG, ret_any}, 100 {"X11DRV_DIB_Lock", LOCK, "X11DRV_DIB_Lock", 0, ret_any}, 101 {"X11DRV_DIB_Unlock", UNLOCK, "X11DRV_DIB_Lock", 0, ret_any}, 102 }; 103 104 static struct lock_info kernel_lock_table[] = { 105 {"lock_kernel", LOCK, "BKL", NO_ARG, ret_any}, 106 {"unlock_kernel", UNLOCK, "BKL", NO_ARG, ret_any}, 107 108 {"spin_lock", LOCK, "spin_lock", 0, ret_any}, 109 {"spin_unlock", UNLOCK, "spin_lock", 0, ret_any}, 110 {"spin_lock_nested", LOCK, "spin_lock", 0, ret_any}, 111 {"_spin_lock", LOCK, "spin_lock", 0, ret_any}, 112 {"_spin_unlock", UNLOCK, "spin_lock", 0, ret_any}, 113 {"_spin_lock_nested", LOCK, "spin_lock", 0, ret_any}, 114 {"__spin_lock", LOCK, "spin_lock", 0, ret_any}, 115 {"__spin_unlock", UNLOCK, "spin_lock", 0, ret_any}, 116 {"__spin_lock_nested", LOCK, "spin_lock", 0, ret_any}, 117 {"raw_spin_lock", LOCK, "spin_lock", 0, ret_any}, 118 {"raw_spin_unlock", UNLOCK, "spin_lock", 0, ret_any}, 119 {"_raw_spin_lock", LOCK, "spin_lock", 0, ret_any}, 120 {"_raw_spin_lock_nested", LOCK, "spin_lock", 0, ret_any}, 121 {"_raw_spin_unlock", UNLOCK, "spin_lock", 0, ret_any}, 122 {"__raw_spin_lock", LOCK, "spin_lock", 0, ret_any}, 123 {"__raw_spin_unlock", UNLOCK, "spin_lock", 0, ret_any}, 124 125 {"spin_lock_irq", LOCK, "spin_lock", 0, ret_any}, 126 {"spin_unlock_irq", UNLOCK, "spin_lock", 0, ret_any}, 127 {"_spin_lock_irq", LOCK, "spin_lock", 0, ret_any}, 128 {"_spin_unlock_irq", UNLOCK, "spin_lock", 0, ret_any}, 129 {"__spin_lock_irq", LOCK, "spin_lock", 0, ret_any}, 130 {"__spin_unlock_irq", UNLOCK, "spin_lock", 0, ret_any}, 131 {"_raw_spin_lock_irq", LOCK, "spin_lock", 0, ret_any}, 132 {"_raw_spin_unlock_irq", UNLOCK, "spin_lock", 0, ret_any}, 133 {"__raw_spin_unlock_irq", UNLOCK, "spin_lock", 0, ret_any}, 134 {"spin_lock_irqsave", LOCK, "spin_lock", 0, ret_any}, 135 {"spin_unlock_irqrestore", UNLOCK, "spin_lock", 0, ret_any}, 136 {"_spin_lock_irqsave", LOCK, "spin_lock", 0, ret_any}, 137 {"_spin_unlock_irqrestore", UNLOCK, "spin_lock", 0, ret_any}, 138 {"__spin_lock_irqsave", LOCK, "spin_lock", 0, ret_any}, 139 {"__spin_unlock_irqrestore", UNLOCK, "spin_lock", 0, ret_any}, 140 {"_raw_spin_lock_irqsave", LOCK, "spin_lock", 0, ret_any}, 141 {"_raw_spin_unlock_irqrestore", UNLOCK, "spin_lock", 0, ret_any}, 142 {"__raw_spin_lock_irqsave", LOCK, "spin_lock", 0, ret_any}, 143 {"__raw_spin_unlock_irqrestore", UNLOCK, "spin_lock", 0, ret_any}, 144 {"spin_lock_irqsave_nested", LOCK, "spin_lock", 0, ret_any}, 145 {"_spin_lock_irqsave_nested", LOCK, "spin_lock", 0, ret_any}, 146 {"__spin_lock_irqsave_nested", LOCK, "spin_lock", 0, ret_any}, 147 {"_raw_spin_lock_irqsave_nested", LOCK, "spin_lock", 0, ret_any}, 148 {"spin_lock_bh", LOCK, "spin_lock", 0, ret_any}, 149 {"spin_unlock_bh", UNLOCK, "spin_lock", 0, ret_any}, 150 {"_spin_lock_bh", LOCK, "spin_lock", 0, ret_any}, 151 {"_spin_unlock_bh", UNLOCK, "spin_lock", 0, ret_any}, 152 {"__spin_lock_bh", LOCK, "spin_lock", 0, ret_any}, 153 {"__spin_unlock_bh", UNLOCK, "spin_lock", 0, ret_any}, 154 155 {"spin_trylock", LOCK, "spin_lock", 0, ret_one}, 156 {"_spin_trylock", LOCK, "spin_lock", 0, ret_one}, 157 {"__spin_trylock", LOCK, "spin_lock", 0, ret_one}, 158 {"raw_spin_trylock", LOCK, "spin_lock", 0, ret_one}, 159 {"_raw_spin_trylock", LOCK, "spin_lock", 0, ret_one}, 160 {"spin_trylock_irq", LOCK, "spin_lock", 0, ret_one}, 161 {"spin_trylock_irqsave", LOCK, "spin_lock", 0, ret_one}, 162 {"spin_trylock_bh", LOCK, "spin_lock", 0, ret_one}, 163 {"_spin_trylock_bh", LOCK, "spin_lock", 0, ret_one}, 164 {"__spin_trylock_bh", LOCK, "spin_lock", 0, ret_one}, 165 {"__raw_spin_trylock", LOCK, "spin_lock", 0, ret_one}, 166 {"_atomic_dec_and_lock", LOCK, "spin_lock", 1, ret_one}, 167 168 {"read_lock", LOCK, "read_lock", 0, ret_any}, 169 {"read_unlock", UNLOCK, "read_lock", 0, ret_any}, 170 {"_read_lock", LOCK, "read_lock", 0, ret_any}, 171 {"_read_unlock", UNLOCK, "read_lock", 0, ret_any}, 172 {"__read_lock", LOCK, "read_lock", 0, ret_any}, 173 {"__read_unlock", UNLOCK, "read_lock", 0, ret_any}, 174 {"_raw_read_lock", LOCK, "read_lock", 0, ret_any}, 175 {"_raw_read_unlock", UNLOCK, "read_lock", 0, ret_any}, 176 {"__raw_read_lock", LOCK, "read_lock", 0, ret_any}, 177 {"__raw_read_unlock", UNLOCK, "read_lock", 0, ret_any}, 178 {"read_lock_irq", LOCK, "read_lock", 0, ret_any}, 179 {"read_unlock_irq" , UNLOCK, "read_lock", 0, ret_any}, 180 {"_read_lock_irq", LOCK, "read_lock", 0, ret_any}, 181 {"_read_unlock_irq", UNLOCK, "read_lock", 0, ret_any}, 182 {"__read_lock_irq", LOCK, "read_lock", 0, ret_any}, 183 {"__read_unlock_irq", UNLOCK, "read_lock", 0, ret_any}, 184 {"read_lock_irqsave", LOCK, "read_lock", 0, ret_any}, 185 {"read_unlock_irqrestore", UNLOCK, "read_lock", 0, ret_any}, 186 {"_read_lock_irqsave", LOCK, "read_lock", 0, ret_any}, 187 {"_read_unlock_irqrestore", UNLOCK, "read_lock", 0, ret_any}, 188 {"__read_lock_irqsave", LOCK, "read_lock", 0, ret_any}, 189 {"__read_unlock_irqrestore", UNLOCK, "read_lock", 0, ret_any}, 190 {"read_lock_bh", LOCK, "read_lock", 0, ret_any}, 191 {"read_unlock_bh", UNLOCK, "read_lock", 0, ret_any}, 192 {"_read_lock_bh", LOCK, "read_lock", 0, ret_any}, 193 {"_read_unlock_bh", UNLOCK, "read_lock", 0, ret_any}, 194 {"__read_lock_bh", LOCK, "read_lock", 0, ret_any}, 195 {"__read_unlock_bh", UNLOCK, "read_lock", 0, ret_any}, 196 {"_raw_read_lock_bh", LOCK, "read_lock", 0, ret_any}, 197 {"_raw_read_unlock_bh", UNLOCK, "read_lock", 0, ret_any}, 198 {"__raw_read_lock_bh", LOCK, "read_lock", 0, ret_any}, 199 {"__raw_read_unlock_bh", UNLOCK, "read_lock", 0, ret_any}, 200 201 {"generic__raw_read_trylock", LOCK, "read_lock", 0, ret_one}, 202 {"read_trylock", LOCK, "read_lock", 0, ret_one}, 203 {"_read_trylock", LOCK, "read_lock", 0, ret_one}, 204 {"raw_read_trylock", LOCK, "read_lock", 0, ret_one}, 205 {"_raw_read_trylock", LOCK, "read_lock", 0, ret_one}, 206 {"__raw_read_trylock", LOCK, "read_lock", 0, ret_one}, 207 {"__read_trylock", LOCK, "read_lock", 0, ret_one}, 208 209 {"write_lock", LOCK, "write_lock", 0, ret_any}, 210 {"write_unlock", UNLOCK, "write_lock", 0, ret_any}, 211 {"_write_lock", LOCK, "write_lock", 0, ret_any}, 212 {"_write_unlock", UNLOCK, "write_lock", 0, ret_any}, 213 {"__write_lock", LOCK, "write_lock", 0, ret_any}, 214 {"__write_unlock", UNLOCK, "write_lock", 0, ret_any}, 215 {"write_lock_irq", LOCK, "write_lock", 0, ret_any}, 216 {"write_unlock_irq", UNLOCK, "write_lock", 0, ret_any}, 217 {"_write_lock_irq", LOCK, "write_lock", 0, ret_any}, 218 {"_write_unlock_irq", UNLOCK, "write_lock", 0, ret_any}, 219 {"__write_lock_irq", LOCK, "write_lock", 0, ret_any}, 220 {"__write_unlock_irq", UNLOCK, "write_lock", 0, ret_any}, 221 {"write_lock_irqsave", LOCK, "write_lock", 0, ret_any}, 222 {"write_unlock_irqrestore", UNLOCK, "write_lock", 0, ret_any}, 223 {"_write_lock_irqsave", LOCK, "write_lock", 0, ret_any}, 224 {"_write_unlock_irqrestore", UNLOCK, "write_lock", 0, ret_any}, 225 {"__write_lock_irqsave", LOCK, "write_lock", 0, ret_any}, 226 {"__write_unlock_irqrestore", UNLOCK, "write_lock", 0, ret_any}, 227 {"write_lock_bh", LOCK, "write_lock", 0, ret_any}, 228 {"write_unlock_bh", UNLOCK, "write_lock", 0, ret_any}, 229 {"_write_lock_bh", LOCK, "write_lock", 0, ret_any}, 230 {"_write_unlock_bh", UNLOCK, "write_lock", 0, ret_any}, 231 {"__write_lock_bh", LOCK, "write_lock", 0, ret_any}, 232 {"__write_unlock_bh", UNLOCK, "write_lock", 0, ret_any}, 233 {"_raw_write_lock", LOCK, "write_lock", 0, ret_any}, 234 {"__raw_write_lock", LOCK, "write_lock", 0, ret_any}, 235 {"_raw_write_unlock", UNLOCK, "write_lock", 0, ret_any}, 236 {"__raw_write_unlock", UNLOCK, "write_lock", 0, ret_any}, 237 238 {"write_trylock", LOCK, "write_lock", 0, ret_one}, 239 {"_write_trylock", LOCK, "write_lock", 0, ret_one}, 240 {"raw_write_trylock", LOCK, "write_lock", 0, ret_one}, 241 {"_raw_write_trylock", LOCK, "write_lock", 0, ret_one}, 242 {"__write_trylock", LOCK, "write_lock", 0, ret_one}, 243 {"__raw_write_trylock", LOCK, "write_lock", 0, ret_one}, 244 245 {"down", LOCK, "sem", 0, ret_any}, 246 {"up", UNLOCK, "sem", 0, ret_any}, 247 {"down_trylock", LOCK, "sem", 0, ret_zero}, 248 {"down_timeout", LOCK, "sem", 0, ret_zero}, 249 {"down_interruptible", LOCK, "sem", 0, ret_zero}, 250 251 252 {"down_write", LOCK, "rw_sem", 0, ret_any}, 253 {"downgrade_write", UNLOCK, "rw_sem", 0, ret_any}, 254 {"downgrade_write", LOCK, "read_sem", 0, ret_any}, 255 {"up_write", UNLOCK, "rw_sem", 0, ret_any}, 256 {"down_write_trylock", LOCK, "rw_sem", 0, ret_one}, 257 {"down_write_killable", LOCK, "rw_sem", 0, ret_zero}, 258 {"down_read", LOCK, "read_sem", 0, ret_any}, 259 {"down_read_trylock", LOCK, "read_sem", 0, ret_one}, 260 {"down_read_killable", LOCK, "read_sem", 0, ret_zero}, 261 {"up_read", UNLOCK, "read_sem", 0, ret_any}, 262 263 {"mutex_lock", LOCK, "mutex", 0, ret_any}, 264 {"mutex_lock_io", LOCK, "mutex", 0, ret_any}, 265 {"mutex_unlock", UNLOCK, "mutex", 0, ret_any}, 266 {"mutex_lock_nested", LOCK, "mutex", 0, ret_any}, 267 {"mutex_lock_io_nested", LOCK, "mutex", 0, ret_any}, 268 269 {"mutex_lock_interruptible", LOCK, "mutex", 0, ret_zero}, 270 {"mutex_lock_interruptible_nested", LOCK, "mutex", 0, ret_zero}, 271 {"mutex_lock_killable", LOCK, "mutex", 0, ret_zero}, 272 {"mutex_lock_killable_nested", LOCK, "mutex", 0, ret_zero}, 273 274 {"mutex_trylock", LOCK, "mutex", 0, ret_one}, 275 276 {"raw_local_irq_disable", LOCK, "irq", NO_ARG, ret_any}, 277 {"raw_local_irq_enable", UNLOCK, "irq", NO_ARG, ret_any}, 278 {"spin_lock_irq", LOCK, "irq", NO_ARG, ret_any}, 279 {"spin_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, 280 {"_spin_lock_irq", LOCK, "irq", NO_ARG, ret_any}, 281 {"_spin_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, 282 {"__spin_lock_irq", LOCK, "irq", NO_ARG, ret_any}, 283 {"__spin_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, 284 {"_raw_spin_lock_irq", LOCK, "irq", NO_ARG, ret_any}, 285 {"_raw_spin_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, 286 {"__raw_spin_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, 287 {"spin_trylock_irq", LOCK, "irq", NO_ARG, ret_one}, 288 {"read_lock_irq", LOCK, "irq", NO_ARG, ret_any}, 289 {"read_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, 290 {"_read_lock_irq", LOCK, "irq", NO_ARG, ret_any}, 291 {"_read_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, 292 {"__read_lock_irq", LOCK, "irq", NO_ARG, ret_any}, 293 {"__read_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, 294 {"write_lock_irq", LOCK, "irq", NO_ARG, ret_any}, 295 {"write_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, 296 {"_write_lock_irq", LOCK, "irq", NO_ARG, ret_any}, 297 {"_write_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, 298 {"__write_lock_irq", LOCK, "irq", NO_ARG, ret_any}, 299 {"__write_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any}, 300 301 {"arch_local_irq_save", LOCK, "irqsave", RETURN_VAL, ret_any}, 302 {"arch_local_irq_restore", UNLOCK, "irqsave", 0, ret_any}, 303 {"__raw_local_irq_save", LOCK, "irqsave", RETURN_VAL, ret_any}, 304 {"raw_local_irq_restore", UNLOCK, "irqsave", 0, ret_any}, 305 {"spin_lock_irqsave_nested", LOCK, "irqsave", RETURN_VAL, ret_any}, 306 {"spin_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, 307 {"spin_lock_irqsave", LOCK, "irqsave", 1, ret_any}, 308 {"spin_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any}, 309 {"_spin_lock_irqsave_nested", LOCK, "irqsave", RETURN_VAL, ret_any}, 310 {"_spin_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, 311 {"_spin_lock_irqsave", LOCK, "irqsave", 1, ret_any}, 312 {"_spin_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any}, 313 {"__spin_lock_irqsave_nested", LOCK, "irqsave", 1, ret_any}, 314 {"__spin_lock_irqsave", LOCK, "irqsave", 1, ret_any}, 315 {"__spin_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any}, 316 {"_raw_spin_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, 317 {"_raw_spin_lock_irqsave", LOCK, "irqsave", 1, ret_any}, 318 {"_raw_spin_unlock_irqrestore",UNLOCK, "irqsave", 1, ret_any}, 319 {"__raw_spin_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, 320 {"__raw_spin_unlock_irqrestore",UNLOCK, "irqsave", 1, ret_any}, 321 {"_raw_spin_lock_irqsave_nested", LOCK, "irqsave", RETURN_VAL, ret_any}, 322 {"spin_trylock_irqsave", LOCK, "irqsave", 1, ret_one}, 323 {"read_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, 324 {"read_lock_irqsave", LOCK, "irqsave", 1, ret_any}, 325 {"read_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any}, 326 {"_read_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, 327 {"_read_lock_irqsave", LOCK, "irqsave", 1, ret_any}, 328 {"_read_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any}, 329 {"__read_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, 330 {"__read_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any}, 331 {"write_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, 332 {"write_lock_irqsave", LOCK, "irqsave", 1, ret_any}, 333 {"write_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any}, 334 {"_write_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, 335 {"_write_lock_irqsave", LOCK, "irqsave", 1, ret_any}, 336 {"_write_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any}, 337 {"__write_lock_irqsave", LOCK, "irqsave", RETURN_VAL, ret_any}, 338 {"__write_unlock_irqrestore", UNLOCK, "irqsave", 1, ret_any}, 339 340 {"local_bh_disable", LOCK, "bottom_half", NO_ARG, ret_any}, 341 {"_local_bh_disable", LOCK, "bottom_half", NO_ARG, ret_any}, 342 {"__local_bh_disable", LOCK, "bottom_half", NO_ARG, ret_any}, 343 {"local_bh_enable", UNLOCK, "bottom_half", NO_ARG, ret_any}, 344 {"_local_bh_enable", UNLOCK, "bottom_half", NO_ARG, ret_any}, 345 {"__local_bh_enable", UNLOCK, "bottom_half", NO_ARG, ret_any}, 346 {"spin_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, 347 {"spin_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, 348 {"_spin_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, 349 {"_spin_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, 350 {"__spin_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, 351 {"__spin_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, 352 {"read_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, 353 {"read_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, 354 {"_read_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, 355 {"_read_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, 356 {"__read_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, 357 {"__read_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, 358 {"_raw_read_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, 359 {"_raw_read_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, 360 {"write_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, 361 {"write_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, 362 {"_write_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, 363 {"_write_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, 364 {"__write_lock_bh", LOCK, "bottom_half", NO_ARG, ret_any}, 365 {"__write_unlock_bh", UNLOCK, "bottom_half", NO_ARG, ret_any}, 366 {"spin_trylock_bh", LOCK, "bottom_half", NO_ARG, ret_one}, 367 {"_spin_trylock_bh", LOCK, "bottom_half", NO_ARG, ret_one}, 368 {"__spin_trylock_bh", LOCK, "bottom_half", NO_ARG, ret_one}, 369 370 {"ffs_mutex_lock", LOCK, "mutex", 0, ret_zero}, 371 }; 372 373 static struct lock_info *lock_table; 374 375 static struct tracker_list *starts_locked; 376 static struct tracker_list *starts_unlocked; 377 378 struct locks_on_return { 379 int line; 380 struct tracker_list *locked; 381 struct tracker_list *unlocked; 382 struct tracker_list *impossible; 383 struct range_list *return_values; 384 }; 385 DECLARE_PTR_LIST(return_list, struct locks_on_return); 386 static struct return_list *all_returns; 387 388 static char *make_full_name(const char *lock, const char *var) 389 { 390 static char tmp_buf[512]; 391 392 snprintf(tmp_buf, sizeof(tmp_buf), "%s:%s", lock, var); 393 remove_parens(tmp_buf); 394 return alloc_string(tmp_buf); 395 } 396 397 static struct expression *remove_spinlock_check(struct expression *expr) 398 { 399 if (expr->type != EXPR_CALL) 400 return expr; 401 if (expr->fn->type != EXPR_SYMBOL) 402 return expr; 403 if (strcmp(expr->fn->symbol_name->name, "spinlock_check")) 404 return expr; 405 expr = get_argument_from_call_expr(expr->args, 0); 406 return expr; 407 } 408 409 static char *get_full_name(struct expression *expr, int index) 410 { 411 struct expression *arg; 412 char *name = NULL; 413 char *full_name = NULL; 414 struct lock_info *lock = &lock_table[index]; 415 416 if (lock->arg == RETURN_VAL) { 417 name = expr_to_var(expr->left); 418 full_name = make_full_name(lock->name, name); 419 } else if (lock->arg == NO_ARG) { 420 full_name = make_full_name(lock->name, ""); 421 } else { 422 arg = get_argument_from_call_expr(expr->args, lock->arg); 423 if (!arg) 424 goto free; 425 arg = remove_spinlock_check(arg); 426 name = expr_to_str(arg); 427 if (!name) 428 goto free; 429 full_name = make_full_name(lock->name, name); 430 } 431 free: 432 free_string(name); 433 return full_name; 434 } 435 436 static struct smatch_state *get_start_state(struct sm_state *sm) 437 { 438 int is_locked = 0; 439 int is_unlocked = 0; 440 441 if (in_tracker_list(starts_locked, my_id, sm->name, sm->sym)) 442 is_locked = 1; 443 if (in_tracker_list(starts_unlocked, my_id, sm->name, sm->sym)) 444 is_unlocked = 1; 445 if (is_locked && is_unlocked) 446 return &undefined; 447 if (is_locked) 448 return &locked; 449 if (is_unlocked) 450 return &unlocked; 451 return &undefined; 452 } 453 454 static struct smatch_state *unmatched_state(struct sm_state *sm) 455 { 456 return &start_state; 457 } 458 459 static void pre_merge_hook(struct sm_state *sm) 460 { 461 if (is_impossible_path()) 462 set_state(my_id, sm->name, sm->sym, &impossible); 463 } 464 465 static bool nestable(const char *name) 466 { 467 if (strstr(name, "read_sem:")) 468 return true; 469 if (strcmp(name, "bottom_half:") == 0) 470 return true; 471 return false; 472 } 473 474 static void do_lock(const char *name) 475 { 476 struct sm_state *sm; 477 478 if (__inline_fn) 479 return; 480 481 sm = get_sm_state(my_id, name, NULL); 482 if (!sm) 483 add_tracker(&starts_unlocked, my_id, name, NULL); 484 if (sm && slist_has_state(sm->possible, &locked) && !nestable(name)) 485 sm_error("double lock '%s'", name); 486 if (sm) 487 func_has_transition = TRUE; 488 set_state(my_id, name, NULL, &locked); 489 } 490 491 static void do_lock_failed(const char *name) 492 { 493 struct sm_state *sm; 494 495 if (__inline_fn) 496 return; 497 498 sm = get_sm_state(my_id, name, NULL); 499 if (!sm) 500 add_tracker(&starts_unlocked, my_id, name, NULL); 501 set_state(my_id, name, NULL, &unlocked); 502 } 503 504 static void do_unlock(const char *name) 505 { 506 struct sm_state *sm; 507 508 if (__inline_fn) 509 return; 510 if (__path_is_null()) 511 return; 512 sm = get_sm_state(my_id, name, NULL); 513 if (!sm) 514 add_tracker(&starts_locked, my_id, name, NULL); 515 if (sm && slist_has_state(sm->possible, &unlocked) && 516 strcmp(name, "bottom_half:") != 0) 517 sm_error("double unlock '%s'", name); 518 if (sm) 519 func_has_transition = TRUE; 520 set_state(my_id, name, NULL, &unlocked); 521 } 522 523 static void match_lock_held(const char *fn, struct expression *call_expr, 524 struct expression *assign_expr, void *_index) 525 { 526 int index = PTR_INT(_index); 527 char *lock_name; 528 struct lock_info *lock = &lock_table[index]; 529 530 if (lock->arg == NO_ARG) { 531 lock_name = get_full_name(NULL, index); 532 } else if (lock->arg == RETURN_VAL) { 533 if (!assign_expr) 534 return; 535 lock_name = get_full_name(assign_expr, index); 536 } else { 537 lock_name = get_full_name(call_expr, index); 538 } 539 if (!lock_name) 540 return; 541 do_lock(lock_name); 542 free_string(lock_name); 543 } 544 545 static void match_lock_failed(const char *fn, struct expression *call_expr, 546 struct expression *assign_expr, void *_index) 547 { 548 int index = PTR_INT(_index); 549 char *lock_name; 550 struct lock_info *lock = &lock_table[index]; 551 552 if (lock->arg == NO_ARG) { 553 lock_name = get_full_name(NULL, index); 554 } else if (lock->arg == RETURN_VAL) { 555 if (!assign_expr) 556 return; 557 lock_name = get_full_name(assign_expr, index); 558 } else { 559 lock_name = get_full_name(call_expr, index); 560 } 561 if (!lock_name) 562 return; 563 do_lock_failed(lock_name); 564 free_string(lock_name); 565 } 566 567 static void match_returns_locked(const char *fn, struct expression *expr, 568 void *_index) 569 { 570 char *full_name = NULL; 571 int index = PTR_INT(_index); 572 struct lock_info *lock = &lock_table[index]; 573 574 if (lock->arg != RETURN_VAL) 575 return; 576 full_name = get_full_name(expr, index); 577 do_lock(full_name); 578 } 579 580 static void match_lock_unlock(const char *fn, struct expression *expr, void *_index) 581 { 582 char *full_name = NULL; 583 int index = PTR_INT(_index); 584 struct lock_info *lock = &lock_table[index]; 585 586 if (__inline_fn) 587 return; 588 589 full_name = get_full_name(expr, index); 590 if (!full_name) 591 return; 592 if (lock->action == LOCK) 593 do_lock(full_name); 594 else 595 do_unlock(full_name); 596 free_string(full_name); 597 } 598 599 static struct locks_on_return *alloc_return(struct expression *expr) 600 { 601 struct locks_on_return *ret; 602 603 ret = malloc(sizeof(*ret)); 604 if (!get_implied_rl(expr, &ret->return_values)) 605 ret->return_values = NULL; 606 ret->line = get_lineno(); 607 ret->locked = NULL; 608 ret->unlocked = NULL; 609 ret->impossible = NULL; 610 return ret; 611 } 612 613 static int check_possible(struct sm_state *sm) 614 { 615 struct sm_state *tmp; 616 int islocked = 0; 617 int isunlocked = 0; 618 int undef = 0; 619 620 if (!option_spammy) 621 return 0; 622 623 FOR_EACH_PTR(sm->possible, tmp) { 624 if (tmp->state == &locked) 625 islocked = 1; 626 if (tmp->state == &unlocked) 627 isunlocked = 1; 628 if (tmp->state == &start_state) { 629 struct smatch_state *s; 630 631 s = get_start_state(tmp); 632 if (s == &locked) 633 islocked = 1; 634 else if (s == &unlocked) 635 isunlocked = 1; 636 else 637 undef = 1; 638 } 639 if (tmp->state == &undefined) 640 undef = 1; // i don't think this is possible any more. 641 } END_FOR_EACH_PTR(tmp); 642 if ((islocked && isunlocked) || undef) { 643 sm_warning("'%s' is sometimes locked here and sometimes unlocked.", sm->name); 644 return 1; 645 } 646 return 0; 647 } 648 649 static struct position warned_pos; 650 651 static void match_return(int return_id, char *return_ranges, struct expression *expr) 652 { 653 struct locks_on_return *ret; 654 struct stree *stree; 655 struct sm_state *tmp; 656 657 if (!final_pass) 658 return; 659 if (__inline_fn) 660 return; 661 662 if (expr && cmp_pos(expr->pos, warned_pos) == 0) 663 return; 664 665 ret = alloc_return(expr); 666 667 stree = __get_cur_stree(); 668 FOR_EACH_MY_SM(my_id, stree, tmp) { 669 if (tmp->state == &locked) { 670 add_tracker(&ret->locked, tmp->owner, tmp->name, 671 tmp->sym); 672 } else if (tmp->state == &unlocked) { 673 add_tracker(&ret->unlocked, tmp->owner, tmp->name, 674 tmp->sym); 675 } else if (tmp->state == &start_state) { 676 struct smatch_state *s; 677 678 s = get_start_state(tmp); 679 if (s == &locked) 680 add_tracker(&ret->locked, tmp->owner, tmp->name, 681 tmp->sym); 682 if (s == &unlocked) 683 add_tracker(&ret->unlocked, tmp->owner,tmp->name, 684 tmp->sym); 685 } else if (tmp->state == &impossible) { 686 add_tracker(&ret->impossible, tmp->owner, tmp->name, 687 tmp->sym); 688 } else { 689 if (check_possible(tmp)) { 690 if (expr) 691 warned_pos = expr->pos; 692 } 693 } 694 } END_FOR_EACH_SM(tmp); 695 add_ptr_list(&all_returns, ret); 696 } 697 698 static void add_line(struct range_list **rl, int line) 699 { 700 sval_t sval = sval_type_val(&int_ctype, line); 701 702 add_range(rl, sval, sval); 703 } 704 705 static int line_printed(struct range_list *rl, int line) 706 { 707 sval_t sval = sval_type_val(&int_ctype, line); 708 709 return rl_has_sval(rl, sval); 710 } 711 712 static void print_inconsistent_returns(struct tracker *lock, 713 struct smatch_state *start) 714 { 715 struct locks_on_return *tmp; 716 struct range_list *printed = NULL; 717 int i; 718 719 sm_warning("inconsistent returns '%s'.", lock->name); 720 sm_printf(" Locked on: "); 721 722 i = 0; 723 FOR_EACH_PTR(all_returns, tmp) { 724 if (line_printed(printed, tmp->line)) 725 continue; 726 if (in_tracker_list(tmp->unlocked, lock->owner, lock->name, lock->sym)) 727 continue; 728 if (in_tracker_list(tmp->locked, lock->owner, lock->name, lock->sym)) { 729 if (i++) 730 sm_printf(" "); 731 sm_printf("line %d\n", tmp->line); 732 add_line(&printed, tmp->line); 733 continue; 734 } 735 if (start == &locked) { 736 if (i++) 737 sm_printf(" "); 738 sm_printf("line %d\n", tmp->line); 739 add_line(&printed, tmp->line); 740 } 741 } END_FOR_EACH_PTR(tmp); 742 743 sm_printf(" Unlocked on: "); 744 printed = NULL; 745 i = 0; 746 FOR_EACH_PTR(all_returns, tmp) { 747 if (line_printed(printed, tmp->line)) 748 continue; 749 if (in_tracker_list(tmp->unlocked, lock->owner, lock->name, lock->sym)) { 750 if (i++) 751 sm_printf(" "); 752 sm_printf("line %d\n", tmp->line); 753 add_line(&printed, tmp->line); 754 continue; 755 } 756 if (in_tracker_list(tmp->locked, lock->owner, lock->name, lock->sym)) 757 continue; 758 if (start == &unlocked) { 759 if (i++) 760 sm_printf(" "); 761 sm_printf("line %d\n", tmp->line); 762 add_line(&printed, tmp->line); 763 } 764 } END_FOR_EACH_PTR(tmp); 765 } 766 767 static int matches_return_type(struct range_list *rl, enum return_type type) 768 { 769 sval_t zero_sval = ll_to_sval(0); 770 sval_t one_sval = ll_to_sval(1); 771 772 /* All these double negatives are super ugly! */ 773 774 switch (type) { 775 case ret_zero: 776 return !possibly_true_rl(rl, SPECIAL_NOTEQUAL, alloc_rl(zero_sval, zero_sval)); 777 case ret_one: 778 return !possibly_true_rl(rl, SPECIAL_NOTEQUAL, alloc_rl(one_sval, one_sval)); 779 case ret_non_zero: 780 return !possibly_true_rl(rl, SPECIAL_EQUAL, alloc_rl(zero_sval, zero_sval)); 781 case ret_negative: 782 return !possibly_true_rl(rl, SPECIAL_GTE, alloc_rl(zero_sval, zero_sval)); 783 case ret_positive: 784 return !possibly_true_rl(rl, '<', alloc_rl(zero_sval, zero_sval)); 785 case ret_any: 786 default: 787 return 1; 788 } 789 } 790 791 static int match_held(struct tracker *lock, struct locks_on_return *this_return, struct smatch_state *start) 792 { 793 if (in_tracker_list(this_return->impossible, lock->owner, lock->name, lock->sym)) 794 return 0; 795 if (in_tracker_list(this_return->unlocked, lock->owner, lock->name, lock->sym)) 796 return 0; 797 if (in_tracker_list(this_return->locked, lock->owner, lock->name, lock->sym)) 798 return 1; 799 if (start == &unlocked) 800 return 0; 801 return 1; 802 } 803 804 static int match_released(struct tracker *lock, struct locks_on_return *this_return, struct smatch_state *start) 805 { 806 if (in_tracker_list(this_return->impossible, lock->owner, lock->name, lock->sym)) 807 return 0; 808 if (in_tracker_list(this_return->unlocked, lock->owner, lock->name, lock->sym)) 809 return 1; 810 if (in_tracker_list(this_return->locked, lock->owner, lock->name, lock->sym)) 811 return 0; 812 if (start == &unlocked) 813 return 1; 814 return 0; 815 } 816 817 static int held_on_return(struct tracker *lock, struct smatch_state *start, enum return_type type) 818 { 819 struct locks_on_return *tmp; 820 821 FOR_EACH_PTR(all_returns, tmp) { 822 if (!matches_return_type(tmp->return_values, type)) 823 continue; 824 if (match_held(lock, tmp, start)) 825 return 1; 826 } END_FOR_EACH_PTR(tmp); 827 return 0; 828 } 829 830 static int released_on_return(struct tracker *lock, struct smatch_state *start, enum return_type type) 831 { 832 struct locks_on_return *tmp; 833 834 FOR_EACH_PTR(all_returns, tmp) { 835 if (!matches_return_type(tmp->return_values, type)) 836 continue; 837 if (match_released(lock, tmp, start)) 838 return 1; 839 } END_FOR_EACH_PTR(tmp); 840 return 0; 841 } 842 843 static void check_returns_consistently(struct tracker *lock, 844 struct smatch_state *start) 845 { 846 struct symbol *type; 847 848 if (!held_on_return(lock, start, ret_any) || 849 !released_on_return(lock, start, ret_any)) 850 return; 851 852 if (held_on_return(lock, start, ret_zero) && 853 !held_on_return(lock, start, ret_non_zero)) 854 return; 855 856 if (held_on_return(lock, start, ret_positive) && 857 !held_on_return(lock, start, ret_zero)) 858 return; 859 860 if (held_on_return(lock, start, ret_positive) && 861 !held_on_return(lock, start, ret_negative)) 862 return; 863 864 type = cur_func_return_type(); 865 if (type && type->type == SYM_PTR) { 866 if (held_on_return(lock, start, ret_non_zero) && 867 !held_on_return(lock, start, ret_zero)) 868 return; 869 } 870 871 print_inconsistent_returns(lock, start); 872 } 873 874 static void check_consistency(struct symbol *sym) 875 { 876 struct tracker *tmp; 877 878 FOR_EACH_PTR(starts_locked, tmp) { 879 if (in_tracker_list(starts_unlocked, tmp->owner, tmp->name, 880 tmp->sym)) 881 sm_error("locking inconsistency. We assume " 882 "'%s' is both locked and unlocked at the " 883 "start.", 884 tmp->name); 885 } END_FOR_EACH_PTR(tmp); 886 887 FOR_EACH_PTR(starts_locked, tmp) { 888 check_returns_consistently(tmp, &locked); 889 } END_FOR_EACH_PTR(tmp); 890 891 FOR_EACH_PTR(starts_unlocked, tmp) { 892 check_returns_consistently(tmp, &unlocked); 893 } END_FOR_EACH_PTR(tmp); 894 } 895 896 static void clear_lists(void) 897 { 898 struct locks_on_return *tmp; 899 900 func_has_transition = FALSE; 901 902 free_trackers_and_list(&starts_locked); 903 free_trackers_and_list(&starts_unlocked); 904 905 FOR_EACH_PTR(all_returns, tmp) { 906 free_trackers_and_list(&tmp->locked); 907 free_trackers_and_list(&tmp->unlocked); 908 free(tmp); 909 } END_FOR_EACH_PTR(tmp); 910 __free_ptr_list((struct ptr_list **)&all_returns); 911 } 912 913 static void match_func_end(struct symbol *sym) 914 { 915 if (__inline_fn) 916 return; 917 918 if (func_has_transition) 919 check_consistency(sym); 920 } 921 922 static void match_after_func(struct symbol *sym) 923 { 924 if (__inline_fn) 925 return; 926 clear_lists(); 927 } 928 929 static void register_lock(int index) 930 { 931 struct lock_info *lock = &lock_table[index]; 932 void *idx = INT_PTR(index); 933 934 if (lock->return_type == ret_non_zero) { 935 return_implies_state(lock->function, 1, INT_MAX, &match_lock_held, idx); 936 return_implies_state(lock->function, 0, 0, &match_lock_failed, idx); 937 } else if (lock->return_type == ret_any && lock->arg == RETURN_VAL) { 938 add_function_assign_hook(lock->function, &match_returns_locked, idx); 939 } else if (lock->return_type == ret_any) { 940 add_function_hook(lock->function, &match_lock_unlock, idx); 941 } else if (lock->return_type == ret_zero) { 942 return_implies_state(lock->function, 0, 0, &match_lock_held, idx); 943 return_implies_state(lock->function, -4095, -1, &match_lock_failed, idx); 944 } else if (lock->return_type == ret_one) { 945 return_implies_state(lock->function, 1, 1, &match_lock_held, idx); 946 return_implies_state(lock->function, 0, 0, &match_lock_failed, idx); 947 } 948 } 949 950 static void load_table(struct lock_info *_lock_table, int size) 951 { 952 int i; 953 954 lock_table = _lock_table; 955 956 for (i = 0; i < size; i++) { 957 if (lock_table[i].action == LOCK) 958 register_lock(i); 959 else 960 add_function_hook(lock_table[i].function, &match_lock_unlock, INT_PTR(i)); 961 } 962 } 963 964 /* print_held_locks() is used in check_call_tree.c */ 965 void print_held_locks(void) 966 { 967 struct stree *stree; 968 struct sm_state *sm; 969 int i = 0; 970 971 stree = __get_cur_stree(); 972 FOR_EACH_MY_SM(my_id, stree, sm) { 973 if (sm->state != &locked) 974 continue; 975 if (i++) 976 sm_printf(" "); 977 sm_printf("'%s'", sm->name); 978 } END_FOR_EACH_SM(sm); 979 } 980 981 void check_locking(int id) 982 { 983 my_id = id; 984 985 if (option_project == PROJ_WINE) 986 load_table(wine_lock_table, ARRAY_SIZE(wine_lock_table)); 987 else if (option_project == PROJ_KERNEL) 988 load_table(kernel_lock_table, ARRAY_SIZE(kernel_lock_table)); 989 else 990 return; 991 992 add_unmatched_state_hook(my_id, &unmatched_state); 993 add_pre_merge_hook(my_id, &pre_merge_hook); 994 add_split_return_callback(match_return); 995 add_hook(&match_func_end, END_FUNC_HOOK); 996 add_hook(&match_after_func, AFTER_FUNC_HOOK); 997 998 }