1// SPDX-License-Identifier: GPL-2.0 2// Author: Kirill Smelkov (kirr@nexedi.com) 3// 4// Search for stream-like files that are using nonseekable_open and convert 5// them to stream_open. A stream-like file is a file that does not use ppos in 6// its read and write. Rationale for the conversion is to avoid deadlock in 7// between read and write. 8 9virtual report 10virtual patch 11virtual explain // explain decisions in the patch (SPFLAGS="-D explain") 12 13// stream-like reader & writer - ones that do not depend on f_pos. 14@ stream_reader @ 15identifier readstream, ppos; 16identifier f, buf, len; 17type loff_t; 18@@ 19 ssize_t readstream(struct file *f, char *buf, size_t len, loff_t *ppos) 20 { 21 ... when != ppos 22 } 23 24@ stream_writer @ 25identifier writestream, ppos; 26identifier f, buf, len; 27type loff_t; 28@@ 29 ssize_t writestream(struct file *f, const char *buf, size_t len, loff_t *ppos) 30 { 31 ... when != ppos 32 } 33 34 35// a function that blocks 36@ blocks @ 37identifier block_f; 38identifier wait_event =~ "^wait_event_.*"; 39@@ 40 block_f(...) { 41 ... when exists 42 wait_event(...) 43 ... when exists 44 } 45 46// stream_reader that can block inside. 47// 48// XXX wait_* can be called not directly from current function (e.g. func -> f -> g -> wait()) 49// XXX currently reader_blocks supports only direct and 1-level indirect cases. 50@ reader_blocks_direct @ 51identifier stream_reader.readstream; 52identifier wait_event =~ "^wait_event_.*"; 53@@ 54 readstream(...) 55 { 56 ... when exists 57 wait_event(...) 58 ... when exists 59 } 60 61@ reader_blocks_1 @ 62identifier stream_reader.readstream; 63identifier blocks.block_f; 64@@ 65 readstream(...) 66 { 67 ... when exists 68 block_f(...) 69 ... when exists 70 } 71 72@ reader_blocks depends on reader_blocks_direct || reader_blocks_1 @ 73identifier stream_reader.readstream; 74@@ 75 readstream(...) { 76 ... 77 } 78 79 80// file_operations + whether they have _any_ .read, .write, .llseek ... at all. 81// 82// XXX add support for file_operations xxx[N] = ... (sound/core/pcm_native.c) 83@ fops0 @ 84identifier fops; 85@@ 86 struct file_operations fops = { 87 ... 88 }; 89 90@ has_read @ 91identifier fops0.fops; 92identifier read_f; 93@@ 94 struct file_operations fops = { 95 .read = read_f, 96 }; 97 98@ has_read_iter @ 99identifier fops0.fops; 100identifier read_iter_f; 101@@ 102 struct file_operations fops = { 103 .read_iter = read_iter_f, 104 }; 105 106@ has_write @ 107identifier fops0.fops; 108identifier write_f; 109@@ 110 struct file_operations fops = { 111 .write = write_f, 112 }; 113 114@ has_write_iter @ 115identifier fops0.fops; 116identifier write_iter_f; 117@@ 118 struct file_operations fops = { 119 .write_iter = write_iter_f, 120 }; 121 122@ has_llseek @ 123identifier fops0.fops; 124identifier llseek_f; 125@@ 126 struct file_operations fops = { 127 .llseek = llseek_f, 128 }; 129 130@ has_no_llseek @ 131identifier fops0.fops; 132@@ 133 struct file_operations fops = { 134 .llseek = no_llseek, 135 }; 136 137@ has_mmap @ 138identifier fops0.fops; 139identifier mmap_f; 140@@ 141 struct file_operations fops = { 142 .mmap = mmap_f, 143 }; 144 145@ has_copy_file_range @ 146identifier fops0.fops; 147identifier copy_file_range_f; 148@@ 149 struct file_operations fops = { 150 .copy_file_range = copy_file_range_f, 151 }; 152 153@ has_remap_file_range @ 154identifier fops0.fops; 155identifier remap_file_range_f; 156@@ 157 struct file_operations fops = { 158 .remap_file_range = remap_file_range_f, 159 }; 160 161@ has_splice_read @ 162identifier fops0.fops; 163identifier splice_read_f; 164@@ 165 struct file_operations fops = { 166 .splice_read = splice_read_f, 167 }; 168 169@ has_splice_write @ 170identifier fops0.fops; 171identifier splice_write_f; 172@@ 173 struct file_operations fops = { 174 .splice_write = splice_write_f, 175 }; 176 177 178// file_operations that is candidate for stream_open conversion - it does not 179// use mmap and other methods that assume @offset access to file. 180// 181// XXX for simplicity require no .{read/write}_iter and no .splice_{read/write} for now. 182// XXX maybe_steam.fops cannot be used in other rules - it gives "bad rule maybe_stream or bad variable fops". 183@ maybe_stream depends on (!has_llseek || has_no_llseek) && !has_mmap && !has_copy_file_range && !has_remap_file_range && !has_read_iter && !has_write_iter && !has_splice_read && !has_splice_write @ 184identifier fops0.fops; 185@@ 186 struct file_operations fops = { 187 }; 188 189 190// ---- conversions ---- 191 192// XXX .open = nonseekable_open -> .open = stream_open 193// XXX .open = func -> openfunc -> nonseekable_open 194 195// read & write 196// 197// if both are used in the same file_operations together with an opener - 198// under that conditions we can use stream_open instead of nonseekable_open. 199@ fops_rw depends on maybe_stream @ 200identifier fops0.fops, openfunc; 201identifier stream_reader.readstream; 202identifier stream_writer.writestream; 203@@ 204 struct file_operations fops = { 205 .open = openfunc, 206 .read = readstream, 207 .write = writestream, 208 }; 209 210@ report_rw depends on report @ 211identifier fops_rw.openfunc; 212position p1; 213@@ 214 openfunc(...) { 215 <... 216 nonseekable_open@p1 217 ...> 218 } 219 220@ script:python depends on report && reader_blocks @ 221fops << fops0.fops; 222p << report_rw.p1; 223@@ 224coccilib.report.print_report(p[0], 225 "ERROR: %s: .read() can deadlock .write(); change nonseekable_open -> stream_open to fix." % (fops,)) 226 227@ script:python depends on report && !reader_blocks @ 228fops << fops0.fops; 229p << report_rw.p1; 230@@ 231coccilib.report.print_report(p[0], 232 "WARNING: %s: .read() and .write() have stream semantic; safe to change nonseekable_open -> stream_open." % (fops,)) 233 234 235@ explain_rw_deadlocked depends on explain && reader_blocks @ 236identifier fops_rw.openfunc; 237@@ 238 openfunc(...) { 239 <... 240- nonseekable_open 241+ nonseekable_open /* read & write (was deadlock) */ 242 ...> 243 } 244 245 246@ explain_rw_nodeadlock depends on explain && !reader_blocks @ 247identifier fops_rw.openfunc; 248@@ 249 openfunc(...) { 250 <... 251- nonseekable_open 252+ nonseekable_open /* read & write (no direct deadlock) */ 253 ...> 254 } 255 256@ patch_rw depends on patch @ 257identifier fops_rw.openfunc; 258@@ 259 openfunc(...) { 260 <... 261- nonseekable_open 262+ stream_open 263 ...> 264 } 265 266 267// read, but not write 268@ fops_r depends on maybe_stream && !has_write @ 269identifier fops0.fops, openfunc; 270identifier stream_reader.readstream; 271@@ 272 struct file_operations fops = { 273 .open = openfunc, 274 .read = readstream, 275 }; 276 277@ report_r depends on report @ 278identifier fops_r.openfunc; 279position p1; 280@@ 281 openfunc(...) { 282 <... 283 nonseekable_open@p1 284 ...> 285 } 286 287@ script:python depends on report @ 288fops << fops0.fops; 289p << report_r.p1; 290@@ 291coccilib.report.print_report(p[0], 292 "WARNING: %s: .read() has stream semantic; safe to change nonseekable_open -> stream_open." % (fops,)) 293 294@ explain_r depends on explain @ 295identifier fops_r.openfunc; 296@@ 297 openfunc(...) { 298 <... 299- nonseekable_open 300+ nonseekable_open /* read only */ 301 ...> 302 } 303 304@ patch_r depends on patch @ 305identifier fops_r.openfunc; 306@@ 307 openfunc(...) { 308 <... 309- nonseekable_open 310+ stream_open 311 ...> 312 } 313 314 315// write, but not read 316@ fops_w depends on maybe_stream && !has_read @ 317identifier fops0.fops, openfunc; 318identifier stream_writer.writestream; 319@@ 320 struct file_operations fops = { 321 .open = openfunc, 322 .write = writestream, 323 }; 324 325@ report_w depends on report @ 326identifier fops_w.openfunc; 327position p1; 328@@ 329 openfunc(...) { 330 <... 331 nonseekable_open@p1 332 ...> 333 } 334 335@ script:python depends on report @ 336fops << fops0.fops; 337p << report_w.p1; 338@@ 339coccilib.report.print_report(p[0], 340 "WARNING: %s: .write() has stream semantic; safe to change nonseekable_open -> stream_open." % (fops,)) 341 342@ explain_w depends on explain @ 343identifier fops_w.openfunc; 344@@ 345 openfunc(...) { 346 <... 347- nonseekable_open 348+ nonseekable_open /* write only */ 349 ...> 350 } 351 352@ patch_w depends on patch @ 353identifier fops_w.openfunc; 354@@ 355 openfunc(...) { 356 <... 357- nonseekable_open 358+ stream_open 359 ...> 360 } 361 362 363// no read, no write - don't change anything 364