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

virtiofs_submounts.py (8713B)


      1import logging
      2import re
      3import os
      4import subprocess
      5import time
      6
      7from avocado import skipUnless
      8from avocado_qemu import LinuxTest, BUILD_DIR
      9from avocado_qemu import wait_for_console_pattern
     10from avocado.utils import ssh
     11
     12
     13def run_cmd(args):
     14    subp = subprocess.Popen(args,
     15                            stdout=subprocess.PIPE,
     16                            stderr=subprocess.PIPE,
     17                            universal_newlines=True)
     18    stdout, stderr = subp.communicate()
     19    ret = subp.returncode
     20
     21    return (stdout, stderr, ret)
     22
     23def has_cmd(name, args=None):
     24    """
     25    This function is for use in a @avocado.skipUnless decorator, e.g.:
     26
     27        @skipUnless(*has_cmd('sudo -n', ('sudo', '-n', 'true')))
     28        def test_something_that_needs_sudo(self):
     29            ...
     30    """
     31
     32    if args is None:
     33        args = ('which', name)
     34
     35    try:
     36        _, stderr, exitcode = run_cmd(args)
     37    except Exception as e:
     38        exitcode = -1
     39        stderr = str(e)
     40
     41    if exitcode != 0:
     42        cmd_line = ' '.join(args)
     43        err = f'{name} required, but "{cmd_line}" failed: {stderr.strip()}'
     44        return (False, err)
     45    else:
     46        return (True, '')
     47
     48def has_cmds(*cmds):
     49    """
     50    This function is for use in a @avocado.skipUnless decorator and
     51    allows checking for the availability of multiple commands, e.g.:
     52
     53        @skipUnless(*has_cmds(('cmd1', ('cmd1', '--some-parameter')),
     54                              'cmd2', 'cmd3'))
     55        def test_something_that_needs_cmd1_and_cmd2(self):
     56            ...
     57    """
     58
     59    for cmd in cmds:
     60        if isinstance(cmd, str):
     61            cmd = (cmd,)
     62
     63        ok, errstr = has_cmd(*cmd)
     64        if not ok:
     65            return (False, errstr)
     66
     67    return (True, '')
     68
     69
     70class VirtiofsSubmountsTest(LinuxTest):
     71    """
     72    :avocado: tags=arch:x86_64
     73    :avocado: tags=accel:kvm
     74    """
     75
     76    def run(self, args, ignore_error=False):
     77        stdout, stderr, ret = run_cmd(args)
     78
     79        if ret != 0:
     80            cmdline = ' '.join(args)
     81            if not ignore_error:
     82                self.fail(f'{cmdline}: Returned {ret}: {stderr}')
     83            else:
     84                self.log.warn(f'{cmdline}: Returned {ret}: {stderr}')
     85
     86        return (stdout, stderr, ret)
     87
     88    def set_up_shared_dir(self):
     89        self.shared_dir = os.path.join(self.workdir, 'virtiofs-shared')
     90
     91        os.mkdir(self.shared_dir)
     92
     93        self.run(('cp', self.get_data('guest.sh'),
     94                 os.path.join(self.shared_dir, 'check.sh')))
     95
     96        self.run(('cp', self.get_data('guest-cleanup.sh'),
     97                 os.path.join(self.shared_dir, 'cleanup.sh')))
     98
     99    def set_up_virtiofs(self):
    100        attmp = os.getenv('AVOCADO_TESTS_COMMON_TMPDIR')
    101        self.vfsdsock = os.path.join(attmp, 'vfsdsock')
    102
    103        self.run(('sudo', '-n', 'rm', '-f', self.vfsdsock), ignore_error=True)
    104
    105        self.virtiofsd = \
    106            subprocess.Popen(('sudo', '-n',
    107                              'tools/virtiofsd/virtiofsd',
    108                              f'--socket-path={self.vfsdsock}',
    109                              '-o', f'source={self.shared_dir}',
    110                              '-o', 'cache=always',
    111                              '-o', 'xattr',
    112                              '-o', 'announce_submounts',
    113                              '-f'),
    114                             stdout=subprocess.DEVNULL,
    115                             stderr=subprocess.PIPE,
    116                             universal_newlines=True)
    117
    118        while not os.path.exists(self.vfsdsock):
    119            if self.virtiofsd.poll() is not None:
    120                self.fail('virtiofsd exited prematurely: ' +
    121                          self.virtiofsd.communicate()[1])
    122            time.sleep(0.1)
    123
    124        self.run(('sudo', '-n', 'chmod', 'go+rw', self.vfsdsock))
    125
    126        self.vm.add_args('-chardev',
    127                         f'socket,id=vfsdsock,path={self.vfsdsock}',
    128                         '-device',
    129                         'vhost-user-fs-pci,queue-size=1024,chardev=vfsdsock' \
    130                             ',tag=host',
    131                         '-object',
    132                         'memory-backend-file,id=mem,size=1G,' \
    133                             'mem-path=/dev/shm,share=on',
    134                         '-numa',
    135                         'node,memdev=mem')
    136
    137    def set_up_nested_mounts(self):
    138        scratch_dir = os.path.join(self.shared_dir, 'scratch')
    139        try:
    140            os.mkdir(scratch_dir)
    141        except FileExistsError:
    142            pass
    143
    144        args = ['bash', self.get_data('host.sh'), scratch_dir]
    145        if self.seed:
    146            args += [self.seed]
    147
    148        out, _, _ = self.run(args)
    149        seed = re.search(r'^Seed: \d+', out)
    150        self.log.info(seed[0])
    151
    152    def mount_in_guest(self):
    153        self.ssh_command('mkdir -p /mnt/host')
    154        self.ssh_command('mount -t virtiofs host /mnt/host')
    155
    156    def check_in_guest(self):
    157        self.ssh_command('bash /mnt/host/check.sh /mnt/host/scratch/share')
    158
    159    def live_cleanup(self):
    160        self.ssh_command('bash /mnt/host/cleanup.sh /mnt/host/scratch')
    161
    162        # It would be nice if the above was sufficient to make virtiofsd clear
    163        # all references to the mounted directories (so they can be unmounted
    164        # on the host), but unfortunately it is not.  To do so, we have to
    165        # resort to a remount.
    166        self.ssh_command('mount -o remount /mnt/host')
    167
    168        scratch_dir = os.path.join(self.shared_dir, 'scratch')
    169        self.run(('bash', self.get_data('cleanup.sh'), scratch_dir))
    170
    171    @skipUnless(*has_cmds(('sudo -n', ('sudo', '-n', 'true')),
    172                          'ssh-keygen', 'bash', 'losetup', 'mkfs.xfs', 'mount'))
    173    def setUp(self):
    174        vmlinuz = self.params.get('vmlinuz')
    175        if vmlinuz is None:
    176            """
    177            The Linux kernel supports FUSE auto-submounts only as of 5.10.
    178            boot_linux.py currently provides Fedora 31, whose kernel is too
    179            old, so this test cannot pass with the on-image kernel (you are
    180            welcome to try, hence the option to force such a test with
    181            -p vmlinuz='').  Therefore, for now the user must provide a
    182            sufficiently new custom kernel, or effectively explicitly
    183            request failure with -p vmlinuz=''.
    184            Once an image with a sufficiently new kernel is available
    185            (probably Fedora 34), we can make -p vmlinuz='' the default, so
    186            that this parameter no longer needs to be specified.
    187            """
    188            self.cancel('vmlinuz parameter not set; you must point it to a '
    189                        'Linux kernel binary to test (to run this test with ' \
    190                        'the on-image kernel, set it to an empty string)')
    191
    192        self.seed = self.params.get('seed')
    193
    194        self.ssh_key = os.path.join(self.workdir, 'id_ed25519')
    195
    196        self.run(('ssh-keygen', '-N', '', '-t', 'ed25519', '-f', self.ssh_key))
    197
    198        pubkey = self.ssh_key + '.pub'
    199
    200        super(VirtiofsSubmountsTest, self).setUp(pubkey)
    201
    202        if vmlinuz:
    203            self.vm.add_args('-kernel', vmlinuz,
    204                             '-append', 'console=ttyS0 root=/dev/sda1')
    205
    206        self.require_accelerator("kvm")
    207        self.vm.add_args('-accel', 'kvm')
    208
    209    def tearDown(self):
    210        try:
    211            self.vm.shutdown()
    212        except:
    213            pass
    214
    215        scratch_dir = os.path.join(self.shared_dir, 'scratch')
    216        self.run(('bash', self.get_data('cleanup.sh'), scratch_dir),
    217                 ignore_error=True)
    218
    219    def test_pre_virtiofsd_set_up(self):
    220        self.set_up_shared_dir()
    221
    222        self.set_up_nested_mounts()
    223
    224        self.set_up_virtiofs()
    225        self.launch_and_wait()
    226        self.mount_in_guest()
    227        self.check_in_guest()
    228
    229    def test_pre_launch_set_up(self):
    230        self.set_up_shared_dir()
    231        self.set_up_virtiofs()
    232
    233        self.set_up_nested_mounts()
    234
    235        self.launch_and_wait()
    236        self.mount_in_guest()
    237        self.check_in_guest()
    238
    239    def test_post_launch_set_up(self):
    240        self.set_up_shared_dir()
    241        self.set_up_virtiofs()
    242        self.launch_and_wait()
    243
    244        self.set_up_nested_mounts()
    245
    246        self.mount_in_guest()
    247        self.check_in_guest()
    248
    249    def test_post_mount_set_up(self):
    250        self.set_up_shared_dir()
    251        self.set_up_virtiofs()
    252        self.launch_and_wait()
    253        self.mount_in_guest()
    254
    255        self.set_up_nested_mounts()
    256
    257        self.check_in_guest()
    258
    259    def test_two_runs(self):
    260        self.set_up_shared_dir()
    261
    262        self.set_up_nested_mounts()
    263
    264        self.set_up_virtiofs()
    265        self.launch_and_wait()
    266        self.mount_in_guest()
    267        self.check_in_guest()
    268
    269        self.live_cleanup()
    270        self.set_up_nested_mounts()
    271
    272        self.check_in_guest()