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

308 (9902B)


      1#!/usr/bin/env bash
      2# group: rw
      3#
      4# Test FUSE exports (in ways that are not captured by the generic
      5# tests)
      6#
      7# Copyright (C) 2020 Red Hat, Inc.
      8#
      9# This program is free software; you can redistribute it and/or modify
     10# it under the terms of the GNU General Public License as published by
     11# the Free Software Foundation; either version 2 of the License, or
     12# (at your option) any later version.
     13#
     14# This program is distributed in the hope that it will be useful,
     15# but WITHOUT ANY WARRANTY; without even the implied warranty of
     16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17# GNU General Public License for more details.
     18#
     19# You should have received a copy of the GNU General Public License
     20# along with this program.  If not, see <http://www.gnu.org/licenses/>.
     21#
     22
     23seq=$(basename "$0")
     24echo "QA output created by $seq"
     25
     26status=1	# failure is the default!
     27
     28_cleanup()
     29{
     30    _cleanup_qemu
     31    _cleanup_test_img
     32    rmdir "$EXT_MP" 2>/dev/null
     33    rm -f "$EXT_MP"
     34    rm -f "$COPIED_IMG"
     35}
     36trap "_cleanup; exit \$status" 0 1 2 3 15
     37
     38# get standard environment, filters and checks
     39. ./common.rc
     40. ./common.filter
     41. ./common.qemu
     42
     43# Generic format, but needs a plain filename
     44_supported_fmt generic
     45if [ "$IMGOPTSSYNTAX" = "true" ]; then
     46    _unsupported_fmt $IMGFMT
     47fi
     48# We need the image to have exactly the specified size, and VPC does
     49# not allow that by default
     50_unsupported_fmt vpc
     51
     52_supported_proto file # We create the FUSE export manually
     53_supported_os Linux # We need /dev/urandom
     54
     55# $1: Export ID
     56# $2: Options (beyond the node-name and ID)
     57# $3: Expected return value (defaults to 'return')
     58# $4: Node to export (defaults to 'node-format')
     59fuse_export_add()
     60{
     61    # The grep -v is a filter for errors when /etc/fuse.conf does not contain
     62    # user_allow_other.  (The error is benign, but it is printed by fusermount
     63    # on the first mount attempt, so our export code cannot hide it.)
     64    _send_qemu_cmd $QEMU_HANDLE \
     65        "{'execute': 'block-export-add',
     66          'arguments': {
     67              'type': 'fuse',
     68              'id': '$1',
     69              'node-name': '${4:-node-format}',
     70              $2
     71          } }" \
     72        "${3:-return}" \
     73        | _filter_imgfmt \
     74        | grep -v 'option allow_other only allowed if'
     75}
     76
     77# $1: Export ID
     78fuse_export_del()
     79{
     80    _send_qemu_cmd $QEMU_HANDLE \
     81        "{'execute': 'block-export-del',
     82          'arguments': {
     83              'id': '$1'
     84          } }" \
     85        'return'
     86
     87    _send_qemu_cmd $QEMU_HANDLE \
     88        '' \
     89        'BLOCK_EXPORT_DELETED'
     90}
     91
     92# Return the length of the protocol file
     93# $1: Protocol node export mount point
     94# $2: Original file (to compare)
     95get_proto_len()
     96{
     97    len1=$(stat -c '%s' "$1")
     98    len2=$(stat -c '%s' "$2")
     99
    100    if [ "$len1" != "$len2" ]; then
    101        echo 'ERROR: Length of export and original differ:' >&2
    102        echo "$len1 != $len2" >&2
    103    else
    104        echo '(OK: Lengths of export and original are the same)' >&2
    105    fi
    106
    107    echo "$len1"
    108}
    109
    110COPIED_IMG="$TEST_IMG.copy"
    111EXT_MP="$TEST_IMG.fuse"
    112
    113echo '=== Set up ==='
    114
    115# Create image with random data
    116_make_test_img 64M
    117$QEMU_IO -c 'write -s /dev/urandom 0 64M' "$TEST_IMG" | _filter_qemu_io
    118
    119_launch_qemu
    120_send_qemu_cmd $QEMU_HANDLE \
    121    "{'execute': 'qmp_capabilities'}" \
    122    'return'
    123
    124# Separate blockdev-add calls for format and protocol so we can remove
    125# the format layer later on
    126_send_qemu_cmd $QEMU_HANDLE \
    127    "{'execute': 'blockdev-add',
    128      'arguments': {
    129          'driver': 'file',
    130          'node-name': 'node-protocol',
    131          'filename': '$TEST_IMG'
    132      } }" \
    133    'return'
    134
    135_send_qemu_cmd $QEMU_HANDLE \
    136    "{'execute': 'blockdev-add',
    137      'arguments': {
    138          'driver': '$IMGFMT',
    139          'node-name': 'node-format',
    140          'file': 'node-protocol'
    141      } }" \
    142    'return'
    143
    144echo
    145echo '=== Mountpoint not present ==='
    146
    147rmdir "$EXT_MP" 2>/dev/null
    148rm -f "$EXT_MP"
    149output=$(fuse_export_add 'export-err' "'mountpoint': '$EXT_MP'" error)
    150
    151if echo "$output" | grep -q "Invalid parameter 'fuse'"; then
    152    _notrun 'No FUSE support'
    153fi
    154
    155echo "$output"
    156
    157echo
    158echo '=== Mountpoint is a directory ==='
    159
    160mkdir "$EXT_MP"
    161fuse_export_add 'export-err' "'mountpoint': '$EXT_MP'" error
    162rmdir "$EXT_MP"
    163
    164echo
    165echo '=== Mountpoint is a regular file ==='
    166
    167touch "$EXT_MP"
    168fuse_export_add 'export-mp' "'mountpoint': '$EXT_MP'"
    169
    170# Check that the export presents the same data as the original image
    171$QEMU_IMG compare -f raw -F $IMGFMT -U "$EXT_MP" "$TEST_IMG"
    172
    173# Some quick chmod tests
    174stat -c 'Permissions pre-chmod: %a' "$EXT_MP"
    175
    176# Verify that we cannot set +w
    177chmod u+w "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt
    178stat -c 'Permissions post-+w: %a' "$EXT_MP"
    179
    180# But that we can set, say, +x (if we are so inclined)
    181chmod u+x "$EXT_MP" 2>&1 | _filter_testdir | _filter_imgfmt
    182stat -c 'Permissions post-+x: %a' "$EXT_MP"
    183
    184echo
    185echo '=== Mount over existing file ==='
    186
    187# This is the coolest feature of FUSE exports: You can transparently
    188# make images in any format appear as raw images
    189fuse_export_add 'export-img' "'mountpoint': '$TEST_IMG'"
    190
    191# Accesses both exports at the same time, so we get a concurrency test
    192$QEMU_IMG compare -f raw -F raw -U "$EXT_MP" "$TEST_IMG"
    193
    194# Just to be sure, we later want to compare the data offline.  Also,
    195# this allows us to see that cp works without complaining.
    196# (This is not a given, because cp will expect a short read at EOF.
    197# Internally, qemu does not allow short reads, so we have to check
    198# whether the FUSE export driver lets them work.)
    199cp "$TEST_IMG" "$COPIED_IMG"
    200
    201# $TEST_IMG will be in mode 0400 because it is read-only; we are going
    202# to write to the copy, so make it writable
    203chmod 0600 "$COPIED_IMG"
    204
    205echo
    206echo '=== Double export ==='
    207
    208# We have already seen that exporting a node twice works fine, but you
    209# cannot export anything twice on the same mount point.  The reason is
    210# that qemu has to stat the given mount point, and this would have to
    211# be answered by the same qemu instance if it already has an export
    212# there.  However, it cannot answer the stat because it is itself
    213# caught up in that same stat.
    214fuse_export_add 'export-err' "'mountpoint': '$EXT_MP'" error
    215
    216echo
    217echo '=== Remove export ==='
    218
    219# Double-check that $EXT_MP appears as a non-empty file (the raw image)
    220$QEMU_IMG info -f raw "$EXT_MP" | grep 'virtual size'
    221
    222fuse_export_del 'export-mp'
    223
    224# See that the file appears empty again
    225$QEMU_IMG info -f raw "$EXT_MP" | grep 'virtual size'
    226
    227echo
    228echo '=== Writable export ==='
    229
    230fuse_export_add 'export-mp' "'mountpoint': '$EXT_MP', 'writable': true"
    231
    232# Check that writing to the read-only export fails
    233$QEMU_IO -f raw -c 'write -P 42 1M 64k' "$TEST_IMG" 2>&1 \
    234    | _filter_qemu_io | _filter_testdir | _filter_imgfmt
    235
    236# But here it should work
    237$QEMU_IO -f raw -c 'write -P 42 1M 64k' "$EXT_MP" | _filter_qemu_io
    238
    239# (Adjust the copy, too)
    240$QEMU_IO -f raw -c 'write -P 42 1M 64k' "$COPIED_IMG" | _filter_qemu_io
    241
    242echo
    243echo '=== Resizing exports ==='
    244
    245# Here, we need to export the protocol node -- the format layer may
    246# not be growable, simply because the format does not support it.
    247
    248# Remove all exports and the format node first so permissions will not
    249# get in the way
    250fuse_export_del 'export-mp'
    251fuse_export_del 'export-img'
    252
    253_send_qemu_cmd $QEMU_HANDLE \
    254    "{'execute': 'blockdev-del',
    255      'arguments': {
    256          'node-name': 'node-format'
    257      } }" \
    258    'return'
    259
    260# Now export the protocol node
    261fuse_export_add \
    262    'export-mp' \
    263    "'mountpoint': '$EXT_MP', 'writable': true" \
    264    'return' \
    265    'node-protocol'
    266
    267echo
    268echo '--- Try growing non-growable export ---'
    269
    270# Get the current size so we can write beyond the EOF
    271orig_len=$(get_proto_len "$EXT_MP" "$TEST_IMG")
    272orig_disk_usage=$(stat -c '%b' "$TEST_IMG")
    273
    274# Should fail (exports are non-growable by default)
    275# (Note that qemu-io can never write beyond the EOF, so we have to use
    276# dd here)
    277dd if=/dev/zero of="$EXT_MP" bs=1 count=64k seek=$orig_len 2>&1 \
    278    | _filter_testdir | _filter_imgfmt
    279
    280echo
    281echo '--- Resize export ---'
    282
    283# But we can truncate it explicitly; even with fallocate
    284fallocate -o "$orig_len" -l 64k "$EXT_MP"
    285
    286new_len=$(get_proto_len "$EXT_MP" "$TEST_IMG")
    287if [ "$new_len" != "$((orig_len + 65536))" ]; then
    288    echo 'ERROR: Unexpected post-truncate image size:'
    289    echo "$new_len != $((orig_len + 65536))"
    290else
    291    echo 'OK: Post-truncate image size is as expected'
    292fi
    293
    294new_disk_usage=$(stat -c '%b' "$TEST_IMG")
    295if [ "$new_disk_usage" -gt "$orig_disk_usage" ]; then
    296    echo 'OK: Disk usage grew with fallocate'
    297else
    298    echo 'ERROR: Disk usage did not grow despite fallocate:'
    299    echo "$orig_disk_usage => $new_disk_usage"
    300fi
    301
    302echo
    303echo '--- Try growing growable export ---'
    304
    305# Now export as growable
    306fuse_export_del 'export-mp'
    307fuse_export_add \
    308    'export-mp' \
    309    "'mountpoint': '$EXT_MP', 'writable': true, 'growable': true" \
    310    'return' \
    311    'node-protocol'
    312
    313# Now we should be able to write beyond the EOF
    314dd if=/dev/zero of="$EXT_MP" bs=1 count=64k seek=$new_len 2>&1 \
    315    | _filter_testdir | _filter_imgfmt
    316
    317new_len=$(get_proto_len "$EXT_MP" "$TEST_IMG")
    318if [ "$new_len" != "$((orig_len + 131072))" ]; then
    319    echo 'ERROR: Unexpected post-grow image size:'
    320    echo "$new_len != $((orig_len + 131072))"
    321else
    322    echo 'OK: Post-grow image size is as expected'
    323fi
    324
    325echo
    326echo '--- Shrink export ---'
    327
    328# Now go back to the original size
    329truncate -s "$orig_len" "$EXT_MP"
    330
    331new_len=$(get_proto_len "$EXT_MP" "$TEST_IMG")
    332if [ "$new_len" != "$orig_len" ]; then
    333    echo 'ERROR: Unexpected post-truncate image size:'
    334    echo "$new_len != $orig_len"
    335else
    336    echo 'OK: Post-truncate image size is as expected'
    337fi
    338
    339echo
    340echo '=== Tear down ==='
    341
    342_send_qemu_cmd $QEMU_HANDLE \
    343    "{'execute': 'quit'}" \
    344    'return'
    345
    346wait=yes _cleanup_qemu
    347
    348echo
    349echo '=== Compare copy with original ==='
    350
    351$QEMU_IMG compare -f raw -F $IMGFMT "$COPIED_IMG" "$TEST_IMG"
    352
    353# success, all done
    354echo "*** done"
    355rm -f $seq.full
    356status=0