cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

060 (17151B)


      1#!/usr/bin/env bash
      2# group: rw auto quick
      3#
      4# Test case for image corruption (overlapping data structures) in qcow2
      5#
      6# Copyright (C) 2013 Red Hat, Inc.
      7#
      8# This program is free software; you can redistribute it and/or modify
      9# it under the terms of the GNU General Public License as published by
     10# the Free Software Foundation; either version 2 of the License, or
     11# (at your option) any later version.
     12#
     13# This program is distributed in the hope that it will be useful,
     14# but WITHOUT ANY WARRANTY; without even the implied warranty of
     15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16# GNU General Public License for more details.
     17#
     18# You should have received a copy of the GNU General Public License
     19# along with this program.  If not, see <http://www.gnu.org/licenses/>.
     20#
     21
     22# creator
     23owner=mreitz@redhat.com
     24
     25seq="$(basename $0)"
     26echo "QA output created by $seq"
     27
     28status=1	# failure is the default!
     29
     30_cleanup()
     31{
     32	_cleanup_test_img
     33}
     34trap "_cleanup; exit \$status" 0 1 2 3 15
     35
     36# Sometimes the error line might be dumped before/after an event
     37# randomly.  Mask it out for specific test that may trigger this
     38# uncertainty for current test for now.
     39_filter_io_error()
     40{
     41    sed '/Input\/output error/d'
     42}
     43
     44# get standard environment, filters and checks
     45. ./common.rc
     46. ./common.filter
     47
     48# This tests qcow2-specific low-level functionality
     49_supported_fmt qcow2
     50_supported_proto file fuse
     51_supported_os Linux
     52# These tests only work for compat=1.1 images without an external
     53# data file with refcount_bits=16
     54_unsupported_imgopts 'compat=0.10' data_file \
     55    'refcount_bits=\([^1]\|.\([^6]\|$\)\)'
     56
     57# The repair process will create a large file - so check for availability first
     58_require_large_file 64G
     59
     60rt_offset=65536  # 0x10000 (XXX: just an assumption)
     61rb_offset=131072 # 0x20000 (XXX: just an assumption)
     62l1_offset=196608 # 0x30000 (XXX: just an assumption)
     63l2_offset=262144 # 0x40000 (XXX: just an assumption)
     64l2_offset_after_snapshot=524288 # 0x80000 (XXX: just an assumption)
     65
     66OPEN_RW="open -o overlap-check=all $TEST_IMG"
     67# Overlap checks are done before write operations only, therefore opening an
     68# image read-only makes the overlap-check option irrelevant
     69OPEN_RO="open -r $TEST_IMG"
     70
     71echo
     72echo "=== Testing L2 reference into L1 ==="
     73echo
     74_make_test_img 64M
     75# Link first L1 entry (first L2 table) onto itself
     76# (Note the MSb in the L1 entry is set, ensuring the refcount is one - else any
     77# later write will result in a COW operation, effectively ruining this attempt
     78# on image corruption)
     79poke_file "$TEST_IMG" "$l1_offset" "\x80\x00\x00\x00\x00\x03\x00\x00"
     80_check_test_img
     81
     82# The corrupt bit should not be set anyway
     83$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
     84
     85# Try to write something, thereby forcing the corrupt bit to be set
     86$QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io
     87
     88# The corrupt bit must now be set
     89$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
     90
     91# This information should be available through qemu-img info
     92_img_info --format-specific
     93
     94# Try to open the image R/W (which should fail)
     95$QEMU_IO -c "$OPEN_RW" -c "read 0 512" 2>&1 | _filter_qemu_io \
     96                                            | _filter_testdir \
     97                                            | _filter_imgfmt
     98
     99# Try to open it RO (which should succeed)
    100$QEMU_IO -c "$OPEN_RO" -c "read 0 512" | _filter_qemu_io
    101
    102# We could now try to fix the image, but this would probably fail (how should an
    103# L2 table linked onto the L1 table be fixed?)
    104
    105echo
    106echo "=== Testing cluster data reference into refcount block ==="
    107echo
    108_make_test_img 64M
    109# Allocate L2 table
    110truncate -s "$(($l2_offset+65536))" "$TEST_IMG"
    111poke_file "$TEST_IMG" "$l1_offset" "\x80\x00\x00\x00\x00\x04\x00\x00"
    112# Mark cluster as used
    113poke_file "$TEST_IMG" "$(($rb_offset+8))" "\x00\x01"
    114# Redirect new data cluster onto refcount block
    115poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x02\x00\x00"
    116_check_test_img
    117$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
    118$QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io
    119$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
    120
    121# Try to fix it
    122_check_test_img -r all
    123
    124# The corrupt bit should be cleared
    125$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
    126
    127# Look if it's really really fixed
    128$QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io
    129$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
    130
    131echo
    132echo "=== Testing cluster data reference into inactive L2 table ==="
    133echo
    134_make_test_img 64M
    135$QEMU_IO -c "$OPEN_RW" -c "write -P 1 0 512" | _filter_qemu_io
    136$QEMU_IMG snapshot -c foo "$TEST_IMG"
    137$QEMU_IO -c "$OPEN_RW" -c "write -P 2 0 512" | _filter_qemu_io
    138# The inactive L2 table remains at its old offset
    139poke_file "$TEST_IMG" "$l2_offset_after_snapshot" \
    140                      "\x80\x00\x00\x00\x00\x04\x00\x00"
    141_check_test_img
    142$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
    143$QEMU_IO -c "$OPEN_RW" -c "write -P 3 0 512" | _filter_qemu_io
    144$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
    145_check_test_img -r all
    146$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
    147$QEMU_IO -c "$OPEN_RW" -c "write -P 4 0 512" | _filter_qemu_io
    148$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
    149
    150# Check data
    151$QEMU_IO -c "$OPEN_RO" -c "read -P 4 0 512" | _filter_qemu_io
    152$QEMU_IMG snapshot -a foo "$TEST_IMG"
    153_check_test_img
    154$QEMU_IO -c "$OPEN_RO" -c "read -P 1 0 512" | _filter_qemu_io
    155
    156echo
    157echo "=== Testing overlap while COW is in flight ==="
    158echo
    159BACKING_IMG=$TEST_IMG.base
    160TEST_IMG=$BACKING_IMG _make_test_img 1G
    161
    162$QEMU_IO -c 'write 0k 64k' "$BACKING_IMG" | _filter_qemu_io
    163
    164_make_test_img -b "$BACKING_IMG" -F $IMGFMT 1G
    165# Write two clusters, the second one enforces creation of an L2 table after
    166# the first data cluster.
    167$QEMU_IO -c 'write 0k 64k' -c 'write 512M 64k' "$TEST_IMG" | _filter_qemu_io
    168# Free the first cluster. This cluster will soon enough be reallocated and
    169# used for COW.
    170poke_file "$TEST_IMG" "$l2_offset" "\x00\x00\x00\x00\x00\x00\x00\x00"
    171poke_file "$TEST_IMG" "$(($rb_offset+10))" "\x00\x00"
    172# Now, corrupt the image by marking the second L2 table cluster as free.
    173poke_file "$TEST_IMG" "$(($rb_offset+12))" "\x00\x00"
    174# Start a write operation requiring COW on the image stopping it right before
    175# doing the read; then, trigger the corruption prevention by writing anything to
    176# any unallocated cluster, leading to an attempt to overwrite the second L2
    177# table. Finally, resume the COW write and see it fail (but not crash).
    178echo "open -o file.driver=blkdebug $TEST_IMG
    179break cow_read 0
    180aio_write 0k 1k
    181wait_break 0
    182write 64k 64k
    183resume 0" | $QEMU_IO | _filter_qemu_io
    184
    185echo
    186echo "=== Testing unallocated image header ==="
    187echo
    188_make_test_img 64M
    189# Create L1/L2
    190$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
    191poke_file "$TEST_IMG" "$rb_offset" "\x00\x00"
    192$QEMU_IO -c "write 64k 64k" "$TEST_IMG" | _filter_qemu_io
    193
    194echo
    195echo "=== Testing unaligned L1 entry ==="
    196echo
    197_make_test_img 64M
    198$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
    199# This will be masked with ~(512 - 1) = ~0x1ff, so whether the lower 9 bits are
    200# aligned or not does not matter
    201poke_file "$TEST_IMG" "$l1_offset" "\x80\x00\x00\x00\x00\x04\x2a\x00"
    202$QEMU_IO -c "read 0 64k" "$TEST_IMG" | _filter_qemu_io
    203
    204# Test how well zero cluster expansion can cope with this
    205_make_test_img 64M
    206$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
    207poke_file "$TEST_IMG" "$l1_offset" "\x80\x00\x00\x00\x00\x04\x2a\x00"
    208$QEMU_IMG amend -o compat=0.10 "$TEST_IMG"
    209
    210echo
    211echo "=== Testing unaligned L2 entry ==="
    212echo
    213_make_test_img 64M
    214$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
    215poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x05\x2a\x00"
    216$QEMU_IO -c "read 0 64k" "$TEST_IMG" | _filter_qemu_io
    217
    218echo
    219echo "=== Testing unaligned pre-allocated zero cluster ==="
    220echo
    221_make_test_img 64M
    222$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
    223poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x05\x2a\x01"
    224# zero cluster expansion
    225$QEMU_IMG amend -o compat=0.10 "$TEST_IMG"
    226
    227echo
    228echo "=== Testing unaligned reftable entry ==="
    229echo
    230_make_test_img 64M
    231poke_file "$TEST_IMG" "$rt_offset" "\x00\x00\x00\x00\x00\x02\x2a\x00"
    232$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
    233
    234echo
    235echo "=== Testing non-fatal corruption on freeing ==="
    236echo
    237_make_test_img 64M
    238$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
    239poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x05\x2a\x00"
    240$QEMU_IO -c "discard 0 64k" "$TEST_IMG" | _filter_qemu_io
    241
    242echo
    243echo "=== Testing read-only corruption report ==="
    244echo
    245_make_test_img 64M
    246$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
    247poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x05\x2a\x00"
    248# Should only emit a single error message
    249$QEMU_IO -c "$OPEN_RO" -c "read 0 64k" -c "read 0 64k" | _filter_qemu_io
    250
    251echo
    252echo "=== Testing non-fatal and then fatal corruption report ==="
    253echo
    254_make_test_img 64M
    255$QEMU_IO -c "write 0 128k" "$TEST_IMG" | _filter_qemu_io
    256poke_file "$TEST_IMG" "$l2_offset"        "\x80\x00\x00\x00\x00\x05\x2a\x00"
    257poke_file "$TEST_IMG" "$(($l2_offset+8))" "\x80\x00\x00\x00\x00\x06\x2a\x00"
    258# Should emit two error messages
    259$QEMU_IO -c "discard 0 64k" -c "read 64k 64k" "$TEST_IMG" | _filter_qemu_io
    260
    261echo
    262echo "=== Testing empty refcount table ==="
    263echo
    264_make_test_img 64M
    265poke_file "$TEST_IMG" "$rt_offset"        "\x00\x00\x00\x00\x00\x00\x00\x00"
    266$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
    267# Repair the image
    268_check_test_img -r all
    269
    270echo
    271echo "=== Testing empty refcount table with valid L1 and L2 tables ==="
    272echo
    273_make_test_img 64M
    274$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
    275poke_file "$TEST_IMG" "$rt_offset"        "\x00\x00\x00\x00\x00\x00\x00\x00"
    276# Since the first data cluster is already allocated this triggers an
    277# allocation with an explicit offset (using qcow2_alloc_clusters_at())
    278# causing a refcount block to be allocated at offset 0
    279$QEMU_IO -c "write 0 128k" "$TEST_IMG" | _filter_qemu_io
    280# Repair the image
    281_check_test_img -r all
    282
    283echo
    284echo "=== Testing empty refcount block ==="
    285echo
    286_make_test_img 64M
    287poke_file "$TEST_IMG" "$rb_offset"        "\x00\x00\x00\x00\x00\x00\x00\x00"
    288$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
    289# Repair the image
    290_check_test_img -r all
    291
    292echo
    293echo "=== Testing empty refcount block with compressed write ==="
    294echo
    295_make_test_img 64M
    296$QEMU_IO -c "write 64k 64k" "$TEST_IMG" | _filter_qemu_io
    297poke_file "$TEST_IMG" "$rb_offset"        "\x00\x00\x00\x00\x00\x00\x00\x00"
    298# The previous write already allocated an L2 table, so now this new
    299# write will try to allocate a compressed data cluster at offset 0.
    300$QEMU_IO -c "write -c 0k 64k" "$TEST_IMG" | _filter_qemu_io
    301# Repair the image
    302_check_test_img -r all
    303
    304echo
    305echo "=== Testing zero refcount table size ==="
    306echo
    307_make_test_img 64M
    308poke_file "$TEST_IMG" "56"                "\x00\x00\x00\x00"
    309$QEMU_IO -c "write 0 64k" "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt
    310# Repair the image
    311_check_test_img -r all
    312
    313echo
    314echo "=== Testing incorrect refcount table offset ==="
    315echo
    316_make_test_img 64M
    317poke_file "$TEST_IMG" "48"                "\x00\x00\x00\x00\x00\x00\x00\x00"
    318$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
    319
    320echo
    321echo "=== Testing dirty corrupt image ==="
    322echo
    323
    324_make_test_img 64M
    325
    326# Let the refblock appear unaligned
    327poke_file "$TEST_IMG" "$rt_offset"        "\x00\x00\x00\x00\xff\xff\x2a\x00"
    328# Mark the image dirty, thus forcing an automatic check when opening it
    329poke_file "$TEST_IMG" 72 "\x00\x00\x00\x00\x00\x00\x00\x01"
    330# Open the image (qemu should refuse to do so)
    331$QEMU_IO -c close "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt
    332
    333echo '--- Repairing ---'
    334
    335# The actual repair should have happened (because of the dirty bit),
    336# but some cleanup may have failed (like freeing the old reftable)
    337# because the image was already marked corrupt by that point
    338_check_test_img -r all
    339
    340echo
    341echo "=== Writing to an unaligned preallocated zero cluster ==="
    342echo
    343
    344_make_test_img 64M
    345
    346# Allocate the L2 table
    347$QEMU_IO -c "write 0 64k" -c "discard 0 64k" "$TEST_IMG" | _filter_qemu_io
    348# Pretend there is a preallocated zero cluster somewhere inside the
    349# image header
    350poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x00\x2a\x01"
    351# Let's write to it!
    352$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
    353
    354echo '--- Repairing ---'
    355_check_test_img -r all
    356
    357echo
    358echo '=== Discarding with an unaligned refblock ==='
    359echo
    360
    361_make_test_img 64M
    362
    363$QEMU_IO -c "write 0 128k" "$TEST_IMG" | _filter_qemu_io
    364# Make our refblock unaligned
    365poke_file "$TEST_IMG" "$(($rt_offset))" "\x00\x00\x00\x00\x00\x00\x2a\x00"
    366# Now try to discard something that will be submitted as two requests
    367# (main part + tail)
    368$QEMU_IO -c "discard 0 65537" "$TEST_IMG"
    369
    370echo '--- Repairing ---'
    371# Fails the first repair because the corruption prevents the check
    372# function from double-checking
    373# (Using -q for the first invocation, because otherwise the
    374#  double-check error message appears above the summary for some
    375#  reason -- so let's just hide the summary)
    376_check_test_img -q -r all
    377_check_test_img -r all
    378
    379echo
    380echo "=== Discarding an out-of-bounds refblock ==="
    381echo
    382
    383_make_test_img 64M
    384
    385# Pretend there's a refblock really up high
    386poke_file "$TEST_IMG" "$(($rt_offset+8))" "\x00\xff\xff\xff\x00\x00\x00\x00"
    387# Let's try to shrink the qcow2 image so that the block driver tries
    388# to discard that refblock (and see what happens!)
    389$QEMU_IMG resize --shrink "$TEST_IMG" 32M
    390
    391echo '--- Checking and retrying ---'
    392# Image should not be resized
    393_img_info | grep 'virtual size'
    394# But it should pass this check, because the "partial" resize has
    395# already overwritten refblocks past the end
    396_check_test_img -r all
    397# So let's try again
    398$QEMU_IMG resize --shrink "$TEST_IMG" 32M
    399_img_info | grep 'virtual size'
    400
    401echo
    402echo "=== Discarding a non-covered in-bounds refblock ==="
    403echo
    404
    405_make_test_img -o 'refcount_bits=1' 64M
    406
    407# Pretend there's a refblock somewhere where there is no refblock to
    408# cover it (but the covering refblock has a valid index in the
    409# reftable)
    410# Every refblock covers 65536 * 8 * 65536 = 32 GB, so we have to point
    411# to 0x10_0000_0000 (64G) to point to the third refblock
    412poke_file "$TEST_IMG" "$(($rt_offset+8))" "\x00\x00\x00\x10\x00\x00\x00\x00"
    413$QEMU_IMG resize --shrink "$TEST_IMG" 32M
    414
    415echo '--- Checking and retrying ---'
    416# Image should not be resized
    417_img_info | grep 'virtual size'
    418# But it should pass this check, because the "partial" resize has
    419# already overwritten refblocks past the end
    420_check_test_img -r all
    421# So let's try again
    422$QEMU_IMG resize --shrink "$TEST_IMG" 32M
    423_img_info | grep 'virtual size'
    424
    425echo
    426echo "=== Discarding a refblock covered by an unaligned refblock ==="
    427echo
    428
    429_make_test_img -o 'refcount_bits=1' 64M
    430
    431# Same as above
    432poke_file "$TEST_IMG" "$(($rt_offset+8))" "\x00\x00\x00\x10\x00\x00\x00\x00"
    433# But now we actually "create" an unaligned third refblock
    434poke_file "$TEST_IMG" "$(($rt_offset+16))" "\x00\x00\x00\x00\x00\x00\x02\x00"
    435$QEMU_IMG resize --shrink "$TEST_IMG" 32M
    436
    437echo '--- Repairing ---'
    438# Fails the first repair because the corruption prevents the check
    439# function from double-checking
    440# (Using -q for the first invocation, because otherwise the
    441#  double-check error message appears above the summary for some
    442#  reason -- so let's just hide the summary)
    443_check_test_img -q -r all
    444_check_test_img -r all
    445
    446echo
    447echo "=== Testing the QEMU shutdown with a corrupted image ==="
    448echo
    449_make_test_img 64M
    450poke_file "$TEST_IMG" "$rt_offset"        "\x00\x00\x00\x00\x00\x00\x00\x00"
    451echo "{'execute': 'qmp_capabilities'}
    452      {'execute': 'human-monitor-command',
    453       'arguments': {'command-line': 'qemu-io drive \"write 0 512\"'}}
    454      {'execute': 'quit'}" \
    455    | $QEMU -qmp stdio -nographic -nodefaults \
    456            -drive if=none,node-name=drive,file="$TEST_IMG",driver=qcow2 \
    457    | _filter_qmp | _filter_qemu_io
    458
    459echo
    460echo "=== Testing incoming inactive corrupted image ==="
    461echo
    462
    463_make_test_img 64M
    464# Create an unaligned L1 entry, so qemu will signal a corruption when
    465# reading from the covered area
    466poke_file "$TEST_IMG" "$l1_offset" "\x00\x00\x00\x00\x2a\x2a\x2a\x2a"
    467
    468# Inactive images are effectively read-only images, so this should be a
    469# non-fatal corruption (which does not modify the image)
    470echo "{'execute': 'qmp_capabilities'}
    471      {'execute': 'human-monitor-command',
    472       'arguments': {'command-line': 'qemu-io drive \"read 0 512\"'}}
    473      {'execute': 'quit'}" \
    474    | $QEMU -qmp stdio -nographic -nodefaults \
    475            -blockdev "{'node-name': 'drive',
    476                        'driver': 'qcow2',
    477                        'file': {
    478                            'driver': 'file',
    479                            'filename': '$TEST_IMG'
    480                        }}" \
    481            -incoming exec:'cat /dev/null' \
    482            2>&1 \
    483    | _filter_qmp | _filter_qemu_io | _filter_io_error
    484
    485echo
    486# Image should not have been marked corrupt
    487_img_info --format-specific | grep 'corrupt:'
    488
    489# success, all done
    490echo "*** done"
    491rm -f $seq.full
    492status=0