예제 #1
0
def test_config_machine_config_params(test_microvm_with_api, test_config):
    """
    Test microvm start with optional `machine_config` parameters.

    @type: functional
    """
    test_microvm = test_microvm_with_api

    # Test configuration determines if the file is a valid config or not
    # based on the CPU
    (vm_config_file, fail_intel, fail_amd, fail_aarch64) = test_config

    _configure_vm_from_json(test_microvm, vm_config_file)
    test_microvm.jailer.extra_args.update({'no-api': None})

    test_microvm.spawn()

    cpu_vendor = utils_cpuid.get_cpu_vendor()

    check_for_failed_start = (
        (cpu_vendor == utils_cpuid.CpuVendor.AMD and fail_amd)
        or (cpu_vendor == utils_cpuid.CpuVendor.INTEL and fail_intel)
        or (platform.machine() == "aarch64" and fail_aarch64))

    if check_for_failed_start:
        test_microvm.check_any_log_message([
            "Building VMM configured from cmdline json failed: ",
            "Configuration for VMM from one single json failed"
        ])
    else:
        test_microvm.check_log_message(
            "Successfully started microvm that was configured "
            "from one single json")
def test_cpu_template(test_microvm_with_api, network_config, cpu_template):
    """
    Test masked and enabled cpu features against the expected template.

    This test checks that all expected masked features are not present in the
    guest and that expected enabled features are present for each of the
    supported CPU templates.

    @type: functional
    """
    test_microvm = test_microvm_with_api
    test_microvm.spawn()

    test_microvm.basic_config(vcpu_count=1)
    # Set template as specified in the `cpu_template` parameter.
    response = test_microvm.machine_cfg.put(
        vcpu_count=1,
        mem_size_mib=256,
        cpu_template=cpu_template,
    )
    assert test_microvm.api_session.is_status_no_content(response.status_code)
    _tap, _, _ = test_microvm.ssh_network_config(network_config, "1")

    response = test_microvm.actions.put(action_type="InstanceStart")
    if utils.get_cpu_vendor() != utils.CpuVendor.INTEL:
        # We shouldn't be able to apply Intel templates on AMD hosts
        assert test_microvm.api_session.is_status_bad_request(
            response.status_code)
        return

    assert test_microvm.api_session.is_status_no_content(response.status_code)
    check_masked_features(test_microvm, cpu_template)
    check_enabled_features(test_microvm, cpu_template)
예제 #3
0
def test_cpu_template(test_microvm_with_ssh, network_config, cpu_template):
    """Check that AVX2 & AVX512 instructions are disabled.

    This is a rather dummy test for checking that some features are not
    exposed by mistake. It is a first step into checking the t2 & c3
    templates. In a next iteration we should check **all** cpuid entries, not
    just these features. We can achieve this with a template
    containing all features on a t2/c3 instance and check that the cpuid in
    the guest is an exact match of the template.
    """
    test_microvm = test_microvm_with_ssh
    test_microvm.spawn()

    test_microvm.basic_config(vcpu_count=1)
    # Set template as specified in the `cpu_template` parameter.
    response = test_microvm.machine_cfg.put(
        vcpu_count=1,
        mem_size_mib=256,
        ht_enabled=False,
        cpu_template=cpu_template,
    )
    assert test_microvm.api_session.is_status_no_content(response.status_code)
    _tap, _, _ = test_microvm.ssh_network_config(network_config, '1')

    response = test_microvm.actions.put(action_type='InstanceStart')
    if utils.get_cpu_vendor() != utils.CpuVendor.INTEL:
        # We shouldn't be able to apply Intel templates on AMD hosts
        assert test_microvm.api_session.is_status_bad_request(
            response.status_code)
        return

    assert test_microvm.api_session.is_status_no_content(
            response.status_code)
    check_masked_features(test_microvm, cpu_template)
    check_enabled_features(test_microvm, cpu_template)
예제 #4
0
def main():
    """
    Run the main logic.

    Create snapshot artifacts from complex microVMs with all Firecracker's
    functionality enabled. The kernels are parametrized to include all guest
    supported versions.

    Artifacts are saved in the following format:
    snapshot_artifacts
        |
        -> <guest_kernel_supported_0>_<cpu_template>_guest_snapshot
            |
            -> vm.mem
            -> vm.vmstate
            -> ubuntu-18.04.id_rsa
            -> ubuntu-18.04.ext4
        -> <guest_kernel_supported_1>_<cpu_template>_guest_snapshot
            |
            ...
    """
    # Create directory dedicated to store snapshot artifacts for
    # each guest kernel version.
    shutil.rmtree(SNAPSHOT_ARTIFACTS_ROOT_DIR, ignore_errors=True)
    os.mkdir(SNAPSHOT_ARTIFACTS_ROOT_DIR)

    root_path = tempfile.mkdtemp(prefix=MicrovmBuilder.ROOT_PREFIX,
                                 dir=f"{DEFAULT_TEST_SESSION_ROOT_PATH}")

    # Compile new-pid cloner helper.
    bin_cloner_path = compile_file(file_name='newpid_cloner.c',
                                   bin_name='newpid_cloner',
                                   dest_path=root_path)

    # Fetch kernel and rootfs artifacts from S3 bucket.
    artifacts = ArtifactCollection(_test_images_s3_bucket())
    kernel_artifacts = ArtifactSet(artifacts.kernels())
    disk_artifacts = ArtifactSet(artifacts.disks(keyword="ubuntu"))

    cpu_templates = ["None"]
    if get_cpu_vendor() == CpuVendor.INTEL:
        cpu_templates.extend(["C3", "T2"])

    for cpu_template in cpu_templates:
        # Create a test context.
        test_context = TestContext()
        test_context.custom = {
            'bin_cloner_path': bin_cloner_path,
            'session_root_path': root_path,
            'cpu_template': cpu_template
        }

        # Create the test matrix.
        test_matrix = TestMatrix(
            context=test_context,
            artifact_sets=[kernel_artifacts, disk_artifacts])

        test_matrix.run_test(create_snapshots)
예제 #5
0
def test_cpu_template(test_microvm_with_ssh, network_config, cpu_template):
    """Check that AVX2 & AVX512 instructions are disabled.

    This is a rather dummy test for checking that some features are not
    exposed by mistake. It is a first step into checking the t2 & c3
    templates. In a next iteration we should check **all** cpuid entries, not
    just these features. We can achieve this with a template
    containing all features on a t2/c3 instance and check that the cpuid in
    the guest is an exact match of the template.
    """
    common_masked_features = [
        "avx512", "mpx", "clflushopt", "clwb", "xsavec", "xgetbv1", "xsaves",
        "pku", "ospke"
    ]
    c3_masked_features = ["avx2"]

    test_microvm = test_microvm_with_ssh
    test_microvm.spawn()

    test_microvm.basic_config(vcpu_count=1)
    # Set template as specified in the `cpu_template` parameter.
    response = test_microvm.machine_cfg.put(
        vcpu_count=1,
        mem_size_mib=256,
        ht_enabled=False,
        cpu_template=cpu_template,
    )
    assert test_microvm.api_session.is_status_no_content(response.status_code)
    _tap, _, _ = test_microvm.ssh_network_config(network_config, '1')

    response = test_microvm.actions.put(action_type='InstanceStart')
    if utils.get_cpu_vendor() != utils.CpuVendor.INTEL:
        # We shouldn't be able to apply Intel templates on AMD hosts
        assert test_microvm.api_session.is_status_bad_request(
            response.status_code)
        return

    assert test_microvm.api_session.is_status_no_content(response.status_code)

    ssh_connection = net_tools.SSHConnection(test_microvm.ssh_config)
    guest_cmd = "cat /proc/cpuinfo | grep 'flags' | head -1"
    _, stdout, stderr = ssh_connection.execute_command(guest_cmd)
    assert stderr.read() == ''

    cpu_flags_output = stdout.readline().rstrip()

    if cpu_template == "C3":
        for feature in c3_masked_features:
            assert feature not in cpu_flags_output
    # Check that all features in `common_masked_features` are properly masked.
    for feature in common_masked_features:
        assert feature not in cpu_flags_output

    # Check if XSAVE PKRU is masked for T3/C2.
    expected_cpu_features = {"XCR0 supported: PKRU state": "false"}

    utils.check_guest_cpuid_output(test_microvm, "cpuid -1", None, '=',
                                   expected_cpu_features)
def test_brand_string(test_microvm_with_api, network_config):
    """
    Ensure good formatting for the guest brand string.

    * For Intel CPUs, the guest brand string should be:
        Intel(R) Xeon(R) Processor @ {host frequency}
    where {host frequency} is the frequency reported by the host CPUID
    (e.g. 4.01GHz)
    * For AMD CPUs, the guest brand string should be:
        AMD EPYC
    * For other CPUs, the guest brand string should be:
        ""

    @type: functional
    """
    cif = open("/proc/cpuinfo", "r", encoding="utf-8")
    host_brand_string = None
    while True:
        line = cif.readline()
        if line == "":
            break
        mo = re.search("^model name\\s+:\\s+(.+)$", line)
        if mo:
            host_brand_string = mo.group(1)
    cif.close()
    assert host_brand_string is not None

    test_microvm = test_microvm_with_api
    test_microvm.spawn()

    test_microvm.basic_config(vcpu_count=1)
    _tap, _, _ = test_microvm.ssh_network_config(network_config, "1")
    test_microvm.start()

    ssh_connection = net_tools.SSHConnection(test_microvm.ssh_config)

    guest_cmd = "cat /proc/cpuinfo | grep 'model name' | head -1"
    _, stdout, stderr = ssh_connection.execute_command(guest_cmd)
    assert stderr.read() == ""

    line = stdout.readline().rstrip()
    mo = re.search("^model name\\s+:\\s+(.+)$", line)
    assert mo
    guest_brand_string = mo.group(1)
    assert guest_brand_string

    cpu_vendor = utils.get_cpu_vendor()
    expected_guest_brand_string = ""
    if cpu_vendor == utils.CpuVendor.AMD:
        expected_guest_brand_string += "AMD EPYC"
    elif cpu_vendor == utils.CpuVendor.INTEL:
        expected_guest_brand_string = "Intel(R) Xeon(R) Processor"
        mo = re.search("[.0-9]+[MG]Hz", host_brand_string)
        if mo:
            expected_guest_brand_string += " @ " + mo.group(0)

    assert guest_brand_string == expected_guest_brand_string
예제 #7
0
def _check_cache_topology_x86(test_microvm, num_vcpus_on_lvl_1_cache,
                              num_vcpus_on_lvl_3_cache):
    vm = test_microvm
    expected_lvl_1_str = '{} ({})'.format(hex(num_vcpus_on_lvl_1_cache),
                                          num_vcpus_on_lvl_1_cache)
    expected_lvl_3_str = '{} ({})'.format(hex(num_vcpus_on_lvl_3_cache),
                                          num_vcpus_on_lvl_3_cache)

    cpu_vendor = utils.get_cpu_vendor()
    if cpu_vendor == utils.CpuVendor.AMD:
        expected_level_1_topology = {
            "level": '0x1 (1)',
            "extra cores sharing this cache": expected_lvl_1_str
        }
        expected_level_3_topology = {
            "level": '0x3 (3)',
            "extra cores sharing this cache": expected_lvl_3_str
        }
    elif cpu_vendor == utils.CpuVendor.INTEL:
        expected_level_1_topology = {
            "cache level": '0x1 (1)',
            "extra threads sharing this cache": expected_lvl_1_str,
        }
        expected_level_3_topology = {
            "cache level": '0x3 (3)',
            "extra threads sharing this cache": expected_lvl_3_str,
        }

    utils.check_guest_cpuid_output(vm, "cpuid -1", "--- cache 0 ---", '=',
                                   expected_level_1_topology)
    utils.check_guest_cpuid_output(vm, "cpuid -1", "--- cache 1 ---", '=',
                                   expected_level_1_topology)
    utils.check_guest_cpuid_output(vm, "cpuid -1", "--- cache 2 ---", '=',
                                   expected_level_1_topology)
    utils.check_guest_cpuid_output(vm, "cpuid -1", "--- cache 3 ---", '=',
                                   expected_level_3_topology)
    code, _, _ = ssh_connection.execute_command(cmd)
    assert code == 0

    # The base microVM had MMDS version 2 configured, which was persisted
    # across the snapshot-restore.
    token = generate_mmds_session_token(ssh_connection, mmds_ipv4_address, token_ttl=60)

    cmd = generate_mmds_get_request(mmds_ipv4_address, token=token)
    _, stdout, _ = ssh_connection.execute_command(cmd)
    assert json.load(stdout) == data_store


@pytest.mark.nonci
@pytest.mark.parametrize(
    "cpu_template",
    ["C3", "T2", "None"] if get_cpu_vendor() == CpuVendor.INTEL else ["None"],
)
def test_snap_restore_from_artifacts(
    bin_cloner_path, bin_vsock_path, test_fc_session_root_path, cpu_template
):
    """
    Restore from snapshots obtained with all supported guest kernel versions.

    The snapshot artifacts have been generated through the
    `create_snapshot_artifacts` devtool command. The base microVM snapshotted
    has been built from the config file at
    ~/firecracker/tools/create_snapshot_artifact/complex_vm_config.json.

    @type: functional
    """
    logger = logging.getLogger("cross_kernel_snapshot_restore")
예제 #9
0
# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
"""Tests ensuring security vulnerabilities are not present in dependencies."""

import pytest

from framework.utils_cpuid import CpuVendor, get_cpu_vendor
from framework import utils
from framework import defs


@pytest.mark.skipif(get_cpu_vendor() != CpuVendor.INTEL,
                    reason="The audit is based on cargo.lock which "
                    "is identical on all platforms")
def test_cargo_audit():
    """
    Run cargo audit to check for crates with security vulnerabilities.

    @type: security
    """
    # Run command and raise exception if non-zero return code
    utils.run_cmd('cargo audit --deny warnings -q', cwd=defs.FC_WORKSPACE_DIR)
예제 #10
0
def test_api_machine_config(test_microvm_with_api):
    """
    Test /machine_config PUT/PATCH scenarios that unit tests can't cover.

    @type: functional
    """
    test_microvm = test_microvm_with_api
    test_microvm.spawn()

    # Test invalid vcpu count < 0.
    response = test_microvm.machine_cfg.put(vcpu_count='-2')
    assert test_microvm.api_session.is_status_bad_request(response.status_code)

    # Test invalid type for ht_enabled flag.
    response = test_microvm.machine_cfg.put(ht_enabled='random_string')
    assert test_microvm.api_session.is_status_bad_request(response.status_code)

    # Test invalid CPU template.
    response = test_microvm.machine_cfg.put(cpu_template='random_string')
    assert test_microvm.api_session.is_status_bad_request(response.status_code)

    response = test_microvm.machine_cfg.patch(track_dirty_pages=True)
    assert test_microvm.api_session.is_status_bad_request(response.status_code)

    response = test_microvm.machine_cfg.patch(cpu_template='C3')
    if platform.machine() == "x86_64":
        assert test_microvm.api_session.is_status_no_content(
            response.status_code)
    else:
        assert test_microvm.api_session.is_status_bad_request(
            response.status_code)
        assert "CPU templates are not supported on aarch64" in response.text

    # Test invalid mem_size_mib < 0.
    response = test_microvm.machine_cfg.put(mem_size_mib='-2')
    assert test_microvm.api_session.is_status_bad_request(response.status_code)

    # Test invalid mem_size_mib > usize::MAX.
    bad_size = 1 << 64
    response = test_microvm.machine_cfg.put(mem_size_mib=bad_size)
    fail_msg = "error occurred when deserializing the json body of a " \
               "request: invalid type"
    assert test_microvm.api_session.is_status_bad_request(response.status_code)
    assert fail_msg in response.text

    # Test mem_size_mib of valid type, but too large.
    test_microvm.basic_config()
    firecracker_pid = int(test_microvm.jailer_clone_pid)
    resource.prlimit(firecracker_pid, resource.RLIMIT_AS,
                     (MEM_LIMIT, resource.RLIM_INFINITY))

    bad_size = (1 << 64) - 1
    response = test_microvm.machine_cfg.patch(mem_size_mib=bad_size)
    assert test_microvm.api_session.is_status_no_content(response.status_code)

    response = test_microvm.actions.put(action_type='InstanceStart')
    fail_msg = "Invalid Memory Configuration: MmapRegion(Mmap(Os { code: " \
               "12, kind: Other, message: Out of memory }))"
    assert test_microvm.api_session.is_status_bad_request(response.status_code)
    assert fail_msg in response.text

    # Test invalid mem_size_mib = 0.
    response = test_microvm.machine_cfg.patch(mem_size_mib=0)
    assert test_microvm.api_session.is_status_bad_request(response.status_code)
    assert "The memory size (MiB) is invalid." in response.text

    # Test valid mem_size_mib.
    response = test_microvm.machine_cfg.patch(mem_size_mib=256)
    assert test_microvm.api_session.is_status_no_content(response.status_code)

    response = test_microvm.actions.put(action_type='InstanceStart')
    if utils.get_cpu_vendor() != utils.CpuVendor.INTEL:
        # We shouldn't be able to apply Intel templates on AMD hosts
        fail_msg = "Internal error while starting microVM: Error configuring" \
                   " the vcpu for boot: Cpuid error: InvalidVendor"
        assert test_microvm.api_session.is_status_bad_request(
            response.status_code)
        assert fail_msg in response.text
    else:
        assert test_microvm.api_session.is_status_no_content(
            response.status_code)

    # Validate full vm configuration after patching machine config.
    response = test_microvm.full_cfg.get()
    assert test_microvm.api_session.is_status_ok(response.status_code)
    assert response.json()['machine-config']['vcpu_count'] == 2
    assert response.json()['machine-config']['mem_size_mib'] == 256
예제 #11
0
def test_cpu_template(test_microvm_with_ssh, network_config, cpu_template):
    """Check that AVX2 & AVX512 instructions are disabled.

    This is a rather dummy test for checking that some features are not
    exposed by mistake. It is a first step into checking the t2 & c3
    templates. In a next iteration we should check **all** cpuid entries, not
    just these features. We can achieve this with a template
    containing all features on a t2/c3 instance and check that the cpuid in
    the guest is an exact match of the template.
    """
    common_masked_features_lscpu = [
        "dtes64", "monitor", "ds_cpl", "tm2", "cnxt-id", "sdbg", "xtpr",
        "pdcm", "osxsave", "psn", "ds", "acpi", "tm", "ss", "pbe", "fpdp",
        "rdt_m", "rdt_a", "mpx", "avx512f", "intel_pt", "avx512_vpopcntdq",
        "3dnowprefetch", "pdpe1gb"
    ]

    common_masked_features_cpuid = {
        "SGX": "false",
        "HLE": "false",
        "RTM": "false",
        "RDSEED": "false",
        "ADX": "false",
        "AVX512IFMA": "false",
        "CLFLUSHOPT": "false",
        "CLWB": "false",
        "AVX512PF": "false",
        "AVX512ER": "false",
        "AVX512CD": "false",
        "SHA": "false",
        "AVX512BW": "false",
        "AVX512VL": "false",
        "AVX512VBMI": "false",
        "PKU": "false",
        "OSPKE": "false",
        "RDPID": "false",
        "SGX_LC": "false",
        "AVX512_4VNNIW": "false",
        "AVX512_4FMAPS": "false",
        "XSAVEC": "false",
        "XGETBV": "false",
        "XSAVES": "false"
    }

    # These are all discoverable by cpuid -1.
    c3_masked_features = {
        "FMA": "false",
        "MOVBE": "false",
        "BMI": "false",
        "AVX2": "false",
        "BMI2": "false",
        "INVPCID": "false"
    }

    test_microvm = test_microvm_with_ssh
    test_microvm.spawn()

    test_microvm.basic_config(vcpu_count=1)
    # Set template as specified in the `cpu_template` parameter.
    response = test_microvm.machine_cfg.put(
        vcpu_count=1,
        mem_size_mib=256,
        ht_enabled=False,
        cpu_template=cpu_template,
    )
    assert test_microvm.api_session.is_status_no_content(response.status_code)
    _tap, _, _ = test_microvm.ssh_network_config(network_config, '1')

    response = test_microvm.actions.put(action_type='InstanceStart')
    if utils.get_cpu_vendor() != utils.CpuVendor.INTEL:
        # We shouldn't be able to apply Intel templates on AMD hosts
        assert test_microvm.api_session.is_status_bad_request(
            response.status_code)
        return

    assert test_microvm.api_session.is_status_no_content(response.status_code)

    # Check that all common features discoverable with lscpu
    # are properly masked.
    ssh_connection = net_tools.SSHConnection(test_microvm.ssh_config)
    guest_cmd = "cat /proc/cpuinfo | grep 'flags' | head -1"
    _, stdout, stderr = ssh_connection.execute_command(guest_cmd)
    assert stderr.read() == ''

    cpu_flags_output = stdout.readline().rstrip().split(' ')

    for feature in common_masked_features_lscpu:
        assert feature not in cpu_flags_output, feature

    # Check that all common features discoverable with cpuid
    # are properly masked.
    utils.check_guest_cpuid_output(test_microvm, "cpuid -1", None, '=',
                                   common_masked_features_cpuid)

    if cpu_template == "C3":
        utils.check_guest_cpuid_output(test_microvm, "cpuid -1", None, '=',
                                       c3_masked_features)

    # Check if XSAVE PKRU is masked for T3/C2.
    expected_cpu_features = {"XCR0 supported: PKRU state": "false"}

    utils.check_guest_cpuid_output(test_microvm, "cpuid -1", None, '=',
                                   expected_cpu_features)
예제 #12
0
# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
"""Tests ensuring security vulnerabilities are not present in dependencies."""

import pytest

from framework.utils_cpuid import CpuVendor, get_cpu_vendor
from framework import utils
from framework import defs


@pytest.mark.skipif(
    get_cpu_vendor() != CpuVendor.INTEL,
    reason="The audit is based on cargo.lock which "
    "is identical on all platforms",
)
def test_cargo_audit():
    """
    Run cargo audit to check for crates with security vulnerabilities.

    @type: security
    """
    # Run command and raise exception if non-zero return code
    utils.run_cmd("cargo audit --deny warnings -q", cwd=defs.FC_WORKSPACE_DIR)