stream_open.cocci 7.7 KB

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