-
Notifications
You must be signed in to change notification settings - Fork 184
Add host validation case for secure launch features #6813
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| - cpu.secure_launch.secure_host_validate: | ||
| type = secure_host_validate | ||
| start_vm = "no" | ||
| only x86_64 | ||
| kernel_msg_cmd = "journalctl -b 0 -k" | ||
| variants: | ||
| - amd_sev: | ||
| cpu_vendor = "amd" | ||
| secure_feature = "sev" | ||
| cpu_flags = ["sev"] | ||
| req_kernel_opts = ["mem_encrypt=on", "amd_iommu=on"] | ||
| kvm_probe_module_force_load = yes | ||
| kvm_probe_module_parameters = "sev=1" | ||
| req_cmds = {"sevctl": {"package": "sevctl", "cmd_params": "ok"}, "virt-host-validate": {"package": "libvirt", "cmd_params": ""}} | ||
| sevctl_patterns = ['\[.*PASS.*\]\s+-\s+Secure\sEncrypted\sVirtualization\s+\(SEV\)'] | ||
| virt-host-validate_patterns = ['QEMU: Checking for secure guest support\s+:\s+PASS\n\s*\(SEV'] | ||
| - amd_sev_es: | ||
| cpu_vendor = "amd" | ||
| secure_feature = "sev-es" | ||
| cpu_flags = ["sev", "sev_es"] | ||
| req_kernel_opts = ["mem_encrypt=on", "amd_iommu=on"] | ||
| kvm_probe_module_force_load = yes | ||
| kvm_probe_module_parameters = "sev=1 sev-es=1" | ||
| req_cmds = {"sevctl": {"package": "sevctl", "cmd_params": "ok"}, "virt-host-validate": {"package": "libvirt", "cmd_params": ""}} | ||
| sevctl_patterns = ['\[.*PASS.*\]\s+-\s+Secure\sEncrypted\sVirtualization\s+\(SEV\)', '\[.*PASS.*\]\s+-\s+Encrypted\sState\s+\(SEV-ES\)'] | ||
| virt-host-validate_patterns = ['QEMU: Checking for secure guest support\s+:\s+PASS\n\s*\(SEV'] | ||
| - amd_sev_snp: | ||
| cpu_vendor = "amd" | ||
| secure_feature = "sev-snp" | ||
| cpu_flags = ["sev", "sev_es", "sev_snp"] | ||
| req_kernel_opts = ["mem_encrypt=on", "amd_iommu=on"] | ||
| kvm_probe_module_force_load = yes | ||
| kvm_probe_module_parameters = "sev=1 sev-es=1 sev-snp=1" | ||
| req_cmds = {"sevctl": {"package": "sevctl", "cmd_params": "ok"}, "virt-host-validate": {"package": "libvirt", "cmd_params": ""}} | ||
| sevctl_patterns = ['\[.*PASS.*\]\s+-\s+Secure\sEncrypted\sVirtualization\s+\(SEV\)', '\[.*PASS.*\]\s+-\s+Encrypted\sState\s+\(SEV-ES\)', '\[.*PASS.*\]\s+-\s+Secure Nested Paging\s+\(SEV-SNP\)'] | ||
| virt-host-validate_patterns = ['QEMU: Checking for secure guest support\s+:\s+PASS\n\s*\(SEV-SNP\)'] | ||
| - intel_sgx: | ||
| func_supported_since_libvirt_ver = (9, 0, 0) | ||
| cpu_vendor = "intel" | ||
| secure_feature = "sgx" | ||
| cpu_flags = ["sgx", "sgx_lc"] | ||
| kernel_msg_patterns = ["EPC section"] | ||
| domcap_xpath = [{'element_attrs':[".//features/sgx[@supported='yes']"]}, {'element_attrs':[".//features/sgx/flc"],'text':'yes'}, {'element_attrs':[".//features/sgx/sgx1"],'text':'yes'}, {'element_attrs':[".//features/sgx/sgx2"],'text':'yes'}, {'element_attrs':[".//features/sgx/section_size[@unit='KiB']"]}, {'element_attrs':[".//features/sgx/sections"]}] | ||
| - intel_tdx: | ||
| func_supported_since_libvirt_ver = (11, 10, 0) | ||
| cpu_vendor = "intel" | ||
| secure_feature = "tdx" | ||
| cpu_flags = ["tdx_host_platform"] | ||
| req_kernel_opts = ["nohibernate", "intel_iommu=on"] | ||
| kvm_probe_module_force_load = yes | ||
| kvm_probe_module_parameters = "tdx=1" | ||
| kvm_probe_module_ignored_parameters = vmentry_l1d_flush=not required | ||
| domcap_xpath = [{'element_attrs':[".//features/launchSecurity[@supported='yes']"]}, {'element_attrs':[".//features/launchSecurity/enum[@name='sectype']"]}, {'element_attrs':[".//features/launchSecurity/enum/value"],'text':'tdx'}] | ||
| req_cmds = {"virt-host-validate": {"package": "libvirt", "cmd_params": ""}} | ||
| virt-host-validate_patterns = ['QEMU: Checking for secure guest support\s+:\s+PASS\n\s*\(TDX\)'] | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| # | ||
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| # | ||
| # Copyright Red Hat | ||
| # | ||
| # SPDX-License-Identifier: GPL-2.0 | ||
| # | ||
| # Author: Liang Cong <lcong@redhat.com> | ||
| # | ||
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| from avocado.utils import cpu as cpu_utils | ||
| from avocado.utils import process | ||
|
|
||
| from virttest import cpu | ||
| from virttest import libvirt_version | ||
| from virttest import utils_misc | ||
| from virttest import utils_package | ||
| from virttest.libvirt_xml import domcapability_xml | ||
| from virttest.utils_libvirt import libvirt_vmxml | ||
| from virttest.utils_test import libvirt | ||
|
|
||
|
|
||
| def run(test, params, env): | ||
| """ | ||
| Verify below secure launch features are supported by the host: | ||
| 1. AMD SEV, SEV-ES, SEV-SNP | ||
| 2. INTEL SGX | ||
| 3. INTEL TDX | ||
| """ | ||
| def validate_prerequisites(): | ||
| """ | ||
| Verify secure launch prerequisites of the host. | ||
| """ | ||
| def _validate_cpu_vendor(): | ||
| """ | ||
| Validate host CPU vendor matches the expected vendor specified | ||
| """ | ||
| exp_vendor = params.get("cpu_vendor") | ||
| act_vendor = cpu_utils.get_vendor() | ||
| if exp_vendor and exp_vendor != act_vendor: | ||
| test.cancel("%s is not supported on %s CPU." % (secure_feature, act_vendor)) | ||
|
|
||
| def _validate_cpu_flag(): | ||
| """ | ||
| Validate host CPU supports the required feature flags | ||
| """ | ||
| host_cpu_flags = set(cpu.get_cpu_flags()) | ||
| cpu_flags = eval(params.get("cpu_flags", "[]")) | ||
| if cpu_flags and not set(cpu_flags).issubset(host_cpu_flags): | ||
| test.cancel("%s requires cpu flags %s, but host cpu flags is %s." % (secure_feature, cpu_flags, host_cpu_flags)) | ||
|
|
||
| def _validate_kernel_option(): | ||
| """ | ||
| Validate required kernel options are present in kernel cmdline | ||
| """ | ||
| req_kernel_opts = eval(params.get("req_kernel_opts", "[]")) | ||
| ker_cml = utils_misc.get_ker_cmd() | ||
| if not all(opt in ker_cml for opt in req_kernel_opts): | ||
| test.cancel("%s requires kernel opt %s, but kernel cml is %s." % (secure_feature, req_kernel_opts, ker_cml)) | ||
|
|
||
| _validate_cpu_vendor() | ||
| _validate_cpu_flag() | ||
| _validate_kernel_option() | ||
|
|
||
| def setup_test(): | ||
| """ | ||
| Setup the environment: | ||
| 1. install require packages | ||
| """ | ||
| if req_cmds: | ||
| req_pkgs = [cmd_info["package"] for cmd_info in req_cmds.values()] | ||
| if not utils_package.package_install(req_pkgs): | ||
| test.error("Fail to install packages '%s'" % req_pkgs) | ||
|
|
||
| def run_test(): | ||
| """ | ||
| Test steps | ||
| 1. Check expected kernel msg | ||
| 2. Check specific tool output | ||
| 3. Check virsh domcapabilities output | ||
| """ | ||
| test.log.info("TEST_STEP1: Check expected kernel msg") | ||
| kernel_msg_cmd = params.get("kernel_msg_cmd") | ||
| kernel_msg_patterns = eval(params.get("kernel_msg_patterns", "[]")) | ||
| result = process.run(kernel_msg_cmd, shell=True) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# First, check if the file exists and examine the relevant lines
if [ -f libvirt/tests/src/cpu/secure_host_validate.py ]; then
echo "=== File found. Checking lines around 76 and 85 ==="
sed -n '70,90p' libvirt/tests/src/cpu/secure_host_validate.py | cat -n
else
echo "File not found at libvirt/tests/src/cpu/secure_host_validate.py"
# Try to find similar files
find . -name "*secure_host_validate*" -type f 2>/dev/null | head -10
fiRepository: autotest/tp-libvirt Length of output: 1322 🏁 Script executed: #!/bin/bash
# Search for shell=True usage in the file to verify the claim
echo "=== Checking for shell=True in the file ==="
rg -n 'shell=True' libvirt/tests/src/cpu/secure_host_validate.py 2>/dev/null || echo "Pattern not found or file not accessible"Repository: autotest/tp-libvirt Length of output: 231 🏁 Script executed: #!/bin/bash
# Get file size and full context around the flagged lines
echo "=== Full file structure ==="
wc -l libvirt/tests/src/cpu/secure_host_validate.py 2>/dev/null || echo "Cannot determine file size"
# Look at broader context to understand command construction
echo "=== Context around command execution ==="
sed -n '1,100p' libvirt/tests/src/cpu/secure_host_validate.py | cat -n 2>/dev/null || echo "Cannot read file"Repository: autotest/tp-libvirt Length of output: 4719 Avoid Lines 76 and 85 use Both calls can safely run without a shell. Replace Suggested fix+import shlex
from avocado.utils import cpu as cpu_utils
from avocado.utils import process
@@
- result = process.run(kernel_msg_cmd, shell=True)
+ result = process.run(kernel_msg_cmd, shell=False)
@@
- pkg_cmd = "%s %s" % (cmd, cmd_info["cmd_params"])
- result = process.run(pkg_cmd, shell=True)
+ pkg_cmd = [cmd] + shlex.split(cmd_info.get("cmd_params", ""))
+ result = process.run(pkg_cmd, shell=False)🧰 Tools🪛 Ruff (0.15.2)[error] 76-76: Function call with (S604) 🤖 Prompt for AI Agents |
||
| libvirt.check_result(result, expected_match=kernel_msg_patterns) | ||
|
|
||
| test.log.info("TEST_STEP2: Check specific tool output") | ||
| if req_cmds: | ||
| for cmd, cmd_info in req_cmds.items(): | ||
| pkg_patterns = eval(params.get("%s_patterns" % cmd, "[]")) | ||
| if pkg_patterns: | ||
| pkg_cmd = "%s %s" % (cmd, cmd_info["cmd_params"]) | ||
| result = process.run(pkg_cmd, shell=True) | ||
| for p_pattern in pkg_patterns: | ||
| libvirt.check_result(result, expected_match=p_pattern) | ||
|
|
||
| test.log.info("TEST_STEP3: Check virsh domcapabilities output") | ||
| domcap_xpath = eval(params.get("domcap_xpath", "[]")) | ||
| if domcap_xpath: | ||
| domcap_xml = domcapability_xml.DomCapabilityXML() | ||
| libvirt_vmxml.check_guest_xml_by_xpaths(domcap_xml, domcap_xpath) | ||
|
|
||
| libvirt_version.is_libvirt_feature_supported(params) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# First, find the file to understand the context
find . -name "secure_host_validate.py" -type fRepository: autotest/tp-libvirt Length of output: 110 🏁 Script executed: #!/bin/bash
# Search for is_libvirt_feature_supported function definition in the codebase
rg -n 'def is_libvirt_feature_supported' --type=pyRepository: autotest/tp-libvirt Length of output: 45 🏁 Script executed: #!/bin/bash
# Search for how is_libvirt_feature_supported is used - looking for its behavior
rg -n -B 2 -A 2 'is_libvirt_feature_supported\(' --type=py | head -100Repository: autotest/tp-libvirt Length of output: 8209 🏁 Script executed: #!/bin/bash
# Look at the context around line 104 in the file
find . -name "secure_host_validate.py" -type f -exec sed -n '95,115p' {} +Repository: autotest/tp-libvirt Length of output: 712 🏁 Script executed: #!/bin/bash
# Find where libvirt_version module is defined
find . -name "libvirt_version.py" -type fRepository: autotest/tp-libvirt Length of output: 92 🏁 Script executed: #!/bin/bash
# Search for the function definition and examine its implementation
rg -n -A 10 'def is_libvirt_feature_supported' --type=pyRepository: autotest/tp-libvirt Length of output: 45 🏁 Script executed: #!/bin/bash
# Examine the libvirt_version.py file to understand its structure and imports
head -100 ./provider/libvirt_version.pyRepository: autotest/tp-libvirt Length of output: 1815 🏁 Script executed: #!/bin/bash
# Search for is_libvirt_feature_supported in the libvirt_version.py file
rg -n 'is_libvirt_feature_supported' ./provider/libvirt_version.pyRepository: autotest/tp-libvirt Length of output: 45 🏁 Script executed: #!/bin/bash
# Check the imports in secure_host_validate.py to see where is_libvirt_feature_supported comes from
head -30 ./libvirt/tests/src/cpu/secure_host_validate.pyRepository: autotest/tp-libvirt Length of output: 875 🏁 Script executed: #!/bin/bash
# Search for imports of is_libvirt_feature_supported or where it's coming from
rg -n 'import.*is_libvirt_feature_supported|from.*import.*is_libvirt_feature_supported' --type=pyRepository: autotest/tp-libvirt Length of output: 45 🏁 Script executed: #!/bin/bash
# Check if libvirt_version is imported and how
rg -n 'import.*libvirt_version|from.*libvirt_version' ./libvirt/tests/src/cpu/secure_host_validate.pyRepository: autotest/tp-libvirt Length of output: 102 🏁 Script executed: #!/bin/bash
# Check if TestSkip or similar exceptions are used in the test file
rg -n 'TestSkip|TestCancel|test.skip|skip_test' ./libvirt/tests/src/cpu/secure_host_validate.pyRepository: autotest/tp-libvirt Length of output: 45 🏁 Script executed: #!/bin/bash
# Check how exceptions are typically handled in test files by looking at error handling patterns
rg -n 'except.*:' ./libvirt/tests/src/cpu/secure_host_validate.py | head -20Repository: autotest/tp-libvirt Length of output: 45 🌐 Web query:
💡 Result: In Avocado-VT ( What “supported” usually means in this context is:
So, behavior you should expect at call sites is:
Sources: Avocado-VT architecture overview (libvirt backend usage) [1]; libvirt capabilities XML ( [1] (deepwiki.com) Citations:
Check the return value of The 🤖 Prompt for AI Agents |
||
| secure_feature = params.get("secure_feature") | ||
| req_cmds = eval(params.get("req_cmds", "{}")) | ||
|
|
||
| validate_prerequisites() | ||
| setup_test() | ||
| run_test() | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: autotest/tp-libvirt
Length of output: 4622
Replace
eval()with safe literal parsing for cfg params.These
eval()calls parse config-provided strings as executable Python, enabling arbitrary code execution. Useast.literal_eval()instead, which safely parses Python literals.Suggested fix
Also applies to lines: 47, 75, 82, 90, 97
🧰 Tools
🪛 Ruff (0.15.2)
[error] 42-42: Use of possibly insecure function; consider using
ast.literal_eval(S307)
🤖 Prompt for AI Agents