diff options
author | Alexander Motin <[email protected]> | 2019-12-03 12:59:30 -0500 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2019-12-03 09:59:30 -0800 |
commit | 5ff2249fa579c20c12bce627e284415720fd4412 (patch) | |
tree | 2a84802e184e698ba07c49764ac722ebd4e84371 | |
parent | 624222ae31bc8ce0009c7da9b0877a3c93aa56fb (diff) |
Fix use-after-free in case of L2ARC prefetch failure
In case L2ARC read failed, l2arc_read_done() creates _different_ ZIO
to read data from the original storage device. Unfortunately pointer
to the failed ZIO remains in hdr->b_l1hdr.b_acb->acb_zio_head, and if
some other read try to bump the ZIO priority, it will crash.
The problem is reproducible by corrupting L2ARC content and reading
some data with prefetch if l2arc_noprefetch tunable is changed to 0.
With the default setting the issue is probably not reproducible now.
Reviewed-by: Tom Caputi <[email protected]>
Reviewed-by: Brian Behlendorf <[email protected]>
Signed-off-by: Alexander Motin <[email protected]>
Sponsored-By: iXsystems, Inc.
Closes #9648
-rw-r--r-- | module/zfs/arc.c | 19 |
1 files changed, 16 insertions, 3 deletions
diff --git a/module/zfs/arc.c b/module/zfs/arc.c index 87df07cc0..2ca4b6bef 100644 --- a/module/zfs/arc.c +++ b/module/zfs/arc.c @@ -7873,7 +7873,6 @@ l2arc_read_done(zio_t *zio) zio->io_private = hdr; arc_read_done(zio); } else { - mutex_exit(hash_lock); /* * Buffer didn't survive caching. Increment stats and * reissue to the original storage device. @@ -7898,10 +7897,24 @@ l2arc_read_done(zio_t *zio) ASSERT(!pio || pio->io_child_type == ZIO_CHILD_LOGICAL); - zio_nowait(zio_read(pio, zio->io_spa, zio->io_bp, + zio = zio_read(pio, zio->io_spa, zio->io_bp, abd, zio->io_size, arc_read_done, hdr, zio->io_priority, cb->l2rcb_flags, - &cb->l2rcb_zb)); + &cb->l2rcb_zb); + + /* + * Original ZIO will be freed, so we need to update + * ARC header with the new ZIO pointer to be used + * by zio_change_priority() in arc_read(). + */ + for (struct arc_callback *acb = hdr->b_l1hdr.b_acb; + acb != NULL; acb = acb->acb_next) + acb->acb_zio_head = zio; + + mutex_exit(hash_lock); + zio_nowait(zio); + } else { + mutex_exit(hash_lock); } } |