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