cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

stream_open.cocci (7880B)


      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 =~ "^wait_.*";
     39@@
     40  block_f(...) {
     41    ... when exists
     42    wait(...)
     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 =~ "^wait_.*";
     53@@
     54  readstream(...)
     55  {
     56    ... when exists
     57    wait(...)
     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_noop_llseek @
    138identifier fops0.fops;
    139@@
    140  struct file_operations fops = {
    141    .llseek = noop_llseek,
    142  };
    143
    144@ has_mmap @
    145identifier fops0.fops;
    146identifier mmap_f;
    147@@
    148  struct file_operations fops = {
    149    .mmap = mmap_f,
    150  };
    151
    152@ has_copy_file_range @
    153identifier fops0.fops;
    154identifier copy_file_range_f;
    155@@
    156  struct file_operations fops = {
    157    .copy_file_range = copy_file_range_f,
    158  };
    159
    160@ has_remap_file_range @
    161identifier fops0.fops;
    162identifier remap_file_range_f;
    163@@
    164  struct file_operations fops = {
    165    .remap_file_range = remap_file_range_f,
    166  };
    167
    168@ has_splice_read @
    169identifier fops0.fops;
    170identifier splice_read_f;
    171@@
    172  struct file_operations fops = {
    173    .splice_read = splice_read_f,
    174  };
    175
    176@ has_splice_write @
    177identifier fops0.fops;
    178identifier splice_write_f;
    179@@
    180  struct file_operations fops = {
    181    .splice_write = splice_write_f,
    182  };
    183
    184
    185// file_operations that is candidate for stream_open conversion - it does not
    186// use mmap and other methods that assume @offset access to file.
    187//
    188// XXX for simplicity require no .{read/write}_iter and no .splice_{read/write} for now.
    189// XXX maybe_steam.fops cannot be used in other rules - it gives "bad rule maybe_stream or bad variable fops".
    190@ 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 @
    191identifier fops0.fops;
    192@@
    193  struct file_operations fops = {
    194  };
    195
    196
    197// ---- conversions ----
    198
    199// XXX .open = nonseekable_open -> .open = stream_open
    200// XXX .open = func -> openfunc -> nonseekable_open
    201
    202// read & write
    203//
    204// if both are used in the same file_operations together with an opener -
    205// under that conditions we can use stream_open instead of nonseekable_open.
    206@ fops_rw depends on maybe_stream @
    207identifier fops0.fops, openfunc;
    208identifier stream_reader.readstream;
    209identifier stream_writer.writestream;
    210@@
    211  struct file_operations fops = {
    212      .open  = openfunc,
    213      .read  = readstream,
    214      .write = writestream,
    215  };
    216
    217@ report_rw depends on report @
    218identifier fops_rw.openfunc;
    219position p1;
    220@@
    221  openfunc(...) {
    222    <...
    223     nonseekable_open@p1
    224    ...>
    225  }
    226
    227@ script:python depends on report && reader_blocks @
    228fops << fops0.fops;
    229p << report_rw.p1;
    230@@
    231coccilib.report.print_report(p[0],
    232  "ERROR: %s: .read() can deadlock .write(); change nonseekable_open -> stream_open to fix." % (fops,))
    233
    234@ script:python depends on report && !reader_blocks @
    235fops << fops0.fops;
    236p << report_rw.p1;
    237@@
    238coccilib.report.print_report(p[0],
    239  "WARNING: %s: .read() and .write() have stream semantic; safe to change nonseekable_open -> stream_open." % (fops,))
    240
    241
    242@ explain_rw_deadlocked depends on explain && reader_blocks @
    243identifier fops_rw.openfunc;
    244@@
    245  openfunc(...) {
    246    <...
    247-    nonseekable_open
    248+    nonseekable_open /* read & write (was deadlock) */
    249    ...>
    250  }
    251
    252
    253@ explain_rw_nodeadlock depends on explain && !reader_blocks @
    254identifier fops_rw.openfunc;
    255@@
    256  openfunc(...) {
    257    <...
    258-    nonseekable_open
    259+    nonseekable_open /* read & write (no direct deadlock) */
    260    ...>
    261  }
    262
    263@ patch_rw depends on patch @
    264identifier fops_rw.openfunc;
    265@@
    266  openfunc(...) {
    267    <...
    268-   nonseekable_open
    269+   stream_open
    270    ...>
    271  }
    272
    273
    274// read, but not write
    275@ fops_r depends on maybe_stream && !has_write @
    276identifier fops0.fops, openfunc;
    277identifier stream_reader.readstream;
    278@@
    279  struct file_operations fops = {
    280      .open  = openfunc,
    281      .read  = readstream,
    282  };
    283
    284@ report_r depends on report @
    285identifier fops_r.openfunc;
    286position p1;
    287@@
    288  openfunc(...) {
    289    <...
    290    nonseekable_open@p1
    291    ...>
    292  }
    293
    294@ script:python depends on report @
    295fops << fops0.fops;
    296p << report_r.p1;
    297@@
    298coccilib.report.print_report(p[0],
    299  "WARNING: %s: .read() has stream semantic; safe to change nonseekable_open -> stream_open." % (fops,))
    300
    301@ explain_r depends on explain @
    302identifier fops_r.openfunc;
    303@@
    304  openfunc(...) {
    305    <...
    306-   nonseekable_open
    307+   nonseekable_open /* read only */
    308    ...>
    309  }
    310
    311@ patch_r depends on patch @
    312identifier fops_r.openfunc;
    313@@
    314  openfunc(...) {
    315    <...
    316-   nonseekable_open
    317+   stream_open
    318    ...>
    319  }
    320
    321
    322// write, but not read
    323@ fops_w depends on maybe_stream && !has_read @
    324identifier fops0.fops, openfunc;
    325identifier stream_writer.writestream;
    326@@
    327  struct file_operations fops = {
    328      .open  = openfunc,
    329      .write = writestream,
    330  };
    331
    332@ report_w depends on report @
    333identifier fops_w.openfunc;
    334position p1;
    335@@
    336  openfunc(...) {
    337    <...
    338    nonseekable_open@p1
    339    ...>
    340  }
    341
    342@ script:python depends on report @
    343fops << fops0.fops;
    344p << report_w.p1;
    345@@
    346coccilib.report.print_report(p[0],
    347  "WARNING: %s: .write() has stream semantic; safe to change nonseekable_open -> stream_open." % (fops,))
    348
    349@ explain_w depends on explain @
    350identifier fops_w.openfunc;
    351@@
    352  openfunc(...) {
    353    <...
    354-   nonseekable_open
    355+   nonseekable_open /* write only */
    356    ...>
    357  }
    358
    359@ patch_w depends on patch @
    360identifier fops_w.openfunc;
    361@@
    362  openfunc(...) {
    363    <...
    364-   nonseekable_open
    365+   stream_open
    366    ...>
    367  }
    368
    369
    370// no read, no write - don't change anything