Recently while trying to fix some unit tests I found a CVE in SVM nested code.
In 'shutdown_interception' vmexit handler we call kvm_vcpu_reset.
However if running nested and L1 doesn't intercept shutdown, we will still end
up running this function and trigger a bug in it.
The bug is that this function resets the 'vcpu->arch.hflags' without properly
leaving the nested state, which leaves the vCPU in inconsistent state, which
later triggers a kernel panic in SVM code.
The same bug can likely be triggered by sending INIT via local apic to a vCPU
which runs a nested guest.
On VMX we are lucky that the issue can't happen because VMX always intercepts
triple faults, thus triple fault in L2 will always be redirected to L1.
Plus the 'handle_triple_fault' of VMX doesn't reset the vCPU.
INIT IPI can't happen on VMX either because INIT events are masked while in
VMX mode.
First 4 patches in this series address the above issue, and are
already posted on the list with title,
('nSVM: fix L0 crash if L2 has shutdown condtion which L1 doesn't intercept')
I addressed the review feedback and also added a unit test to hit this issue.
In addition to these patches I noticed that KVM doesn't honour SHUTDOWN intercept bit
of L1 on SVM, and I included a fix to do so - its only for correctness
as a normal hypervisor should always intercept SHUTDOWN.
A unit test on the other hand might want to not do so.
I also extendted the triple_fault_test selftest to hit this issue.
Finaly I found another security issue, I found a way to
trigger a kernel non rate limited printk on SVM from the guest, and
last patch in the series fixes that.
A unit test I posted to kvm-unit-tests project hits this issue, so
no selftest was added.
Best regards,
Maxim Levitsky
Maxim Levitsky (9):
KVM: x86: nSVM: leave nested mode on vCPU free
KVM: x86: nSVM: harden svm_free_nested against freeing vmcb02 while
still in use
KVM: x86: add kvm_leave_nested
KVM: x86: forcibly leave nested mode on vCPU reset
KVM: selftests: move idt_entry to header
kvm: selftests: add svm nested shutdown test
KVM: x86: allow L1 to not intercept triple fault
KVM: selftests: add svm part to triple_fault_test
KVM: x86: remove exit_int_info warning in svm_handle_exit
arch/x86/kvm/svm/nested.c | 12 ++-
arch/x86/kvm/svm/svm.c | 10 +--
arch/x86/kvm/vmx/nested.c | 4 +-
arch/x86/kvm/x86.c | 29 ++++++--
tools/testing/selftests/kvm/.gitignore | 1 +
tools/testing/selftests/kvm/Makefile | 1 +
.../selftests/kvm/include/x86_64/processor.h | 13 ++++
.../selftests/kvm/lib/x86_64/processor.c | 13 ----
.../kvm/x86_64/svm_nested_shutdown_test.c | 67 +++++++++++++++++
.../kvm/x86_64/triple_fault_event_test.c | 73 ++++++++++++++-----
10 files changed, 172 insertions(+), 51 deletions(-)
create mode 100644 tools/testing/selftests/kvm/x86_64/svm_nested_shutdown_test.c
--
2.34.3
The `nettest` binary, built from `selftests/net/nettest.c`,
was expected to be found in the path during test execution of
`fcnal-test.sh` and `pmtu.sh`, leading to tests getting
skipped when the binary is not installed in the system, as can
be seen in these logs found in the wild [1]:
# TEST: vti4: PMTU exceptions [SKIP]
[ 350.600250] IPv6: ADDRCONF(NETDEV_CHANGE): veth_b: link becomes ready
[ 350.607421] IPv6: ADDRCONF(NETDEV_CHANGE): veth_a: link becomes ready
# 'nettest' command not found; skipping tests
# xfrm6udp not supported
# TEST: vti6: PMTU exceptions (ESP-in-UDP) [SKIP]
[ 351.605102] IPv6: ADDRCONF(NETDEV_CHANGE): veth_b: link becomes ready
[ 351.612243] IPv6: ADDRCONF(NETDEV_CHANGE): veth_a: link becomes ready
# 'nettest' command not found; skipping tests
# xfrm4udp not supported
The `unicast_extensions.sh` tests also rely on `nettest`, but
it runs fine there because it looks for the binary in the
current working directory [2]:
The same mechanism that works for the Unicast extensions tests
is here copied over to the PMTU and functional tests.
[1] https://lkft.validation.linaro.org/scheduler/job/5839508#L6221
[2] https://lkft.validation.linaro.org/scheduler/job/5839508#L7958
Signed-off-by: Daniel Díaz <daniel.diaz(a)linaro.org>
---
tools/testing/selftests/net/fcnal-test.sh | 11 +++++++----
tools/testing/selftests/net/pmtu.sh | 10 ++++++----
2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/tools/testing/selftests/net/fcnal-test.sh b/tools/testing/selftests/net/fcnal-test.sh
index 31c3b6ebd388..21ca91473c09 100755
--- a/tools/testing/selftests/net/fcnal-test.sh
+++ b/tools/testing/selftests/net/fcnal-test.sh
@@ -4196,10 +4196,13 @@ elif [ "$TESTS" = "ipv6" ]; then
TESTS="$TESTS_IPV6"
fi
-which nettest >/dev/null
-if [ $? -ne 0 ]; then
- echo "'nettest' command not found; skipping tests"
- exit $ksft_skip
+# nettest can be run from PATH or from same directory as this selftest
+if ! which nettest >/dev/null; then
+ PATH=$PWD:$PATH
+ if ! which nettest >/dev/null; then
+ echo "'nettest' command not found; skipping tests"
+ exit $ksft_skip
+ fi
fi
declare -i nfail=0
diff --git a/tools/testing/selftests/net/pmtu.sh b/tools/testing/selftests/net/pmtu.sh
index 736e358dc549..dfe3d287f01d 100755
--- a/tools/testing/selftests/net/pmtu.sh
+++ b/tools/testing/selftests/net/pmtu.sh
@@ -686,10 +686,12 @@ setup_xfrm() {
}
setup_nettest_xfrm() {
- which nettest >/dev/null
- if [ $? -ne 0 ]; then
- echo "'nettest' command not found; skipping tests"
- return 1
+ if ! which nettest >/dev/null; then
+ PATH=$PWD:$PATH
+ if ! which nettest >/dev/null; then
+ echo "'nettest' command not found; skipping tests"
+ return 1
+ fi
fi
[ ${1} -eq 6 ] && proto="-6" || proto=""
--
2.34.1
We currently tell people we "couldn't find any KTAP output" with no
indication as to what this might mean.
After this patch, we get:
$ ./tools/testing/kunit/kunit.py parse /dev/null
============================================================
[ERROR] Test: <missing>: Could not find any KTAP output. Did any KUnit tests run?
============================================================
Testing complete. Ran 0 tests: errors: 1
Note: we could try and generate a more verbose message like
> Please check .kunit/test.log to see the raw kernel output.
or the like, but we'd need to know what the build dir was to know where
test.log actually lives.
This patch tries to make a more minimal improvement.
Signed-off-by: Daniel Latypov <dlatypov(a)google.com>
---
tools/testing/kunit/kunit_parser.py | 2 +-
tools/testing/kunit/kunit_tool_test.py | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py
index a56c75a973b5..d0ed5dd5cfc4 100644
--- a/tools/testing/kunit/kunit_parser.py
+++ b/tools/testing/kunit/kunit_parser.py
@@ -782,7 +782,7 @@ def parse_run_tests(kernel_output: Iterable[str]) -> Test:
test = Test()
if not lines:
test.name = '<missing>'
- test.add_error('could not find any KTAP output!')
+ test.add_error('Could not find any KTAP output. Did any KUnit tests run?')
test.status = TestStatus.FAILURE_TO_PARSE_TESTS
else:
test = parse_test(lines, 0, [])
diff --git a/tools/testing/kunit/kunit_tool_test.py b/tools/testing/kunit/kunit_tool_test.py
index 90c65b072be9..84a08cf07242 100755
--- a/tools/testing/kunit/kunit_tool_test.py
+++ b/tools/testing/kunit/kunit_tool_test.py
@@ -207,7 +207,7 @@ class KUnitParserTest(unittest.TestCase):
with open(crash_log) as file:
result = kunit_parser.parse_run_tests(
kunit_parser.extract_tap_lines(file.readlines()))
- print_mock.assert_any_call(StrContains('could not find any KTAP output!'))
+ print_mock.assert_any_call(StrContains('Could not find any KTAP output.'))
print_mock.stop()
self.assertEqual(0, len(result.subtests))
self.assertEqual(result.counts.errors, 1)
@@ -588,7 +588,7 @@ class KUnitMainTest(unittest.TestCase):
self.assertEqual(e.exception.code, 1)
self.assertEqual(self.linux_source_mock.build_reconfig.call_count, 1)
self.assertEqual(self.linux_source_mock.run_kernel.call_count, 1)
- self.print_mock.assert_any_call(StrContains('could not find any KTAP output!'))
+ self.print_mock.assert_any_call(StrContains('Could not find any KTAP output.'))
def test_exec_no_tests(self):
self.linux_source_mock.run_kernel = mock.Mock(return_value=['TAP version 14', '1..0'])
base-commit: 870f63b7cd78d0055902d839a60408f7428b4e84
--
2.38.1.431.g37b22c650d-goog
usage.rst had most of the content of the tips.rst page copied over.
But it's missing https://www.kernel.org/doc/html/v6.0/dev-tools/kunit/tips.html#customizing-…
Copy it over so we can retire tips.rst w/o losing content.
And in that process, it also gained a duplicate section about how
KUNIT_ASSERT_*() exit the test case early. Remove that.
Signed-off-by: Daniel Latypov <dlatypov(a)google.com>
Reviewed-by: Sadiya Kazi <sadiyakazi(a)google.com>
---
Documentation/dev-tools/kunit/usage.rst | 49 ++++++++++++++++---------
1 file changed, 31 insertions(+), 18 deletions(-)
diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst
index 2737863ef365..b0a6c3bc0eeb 100644
--- a/Documentation/dev-tools/kunit/usage.rst
+++ b/Documentation/dev-tools/kunit/usage.rst
@@ -118,6 +118,37 @@ expectation could crash the test case. `ASSERT_NOT_ERR_OR_NULL(...)` allows us
to bail out of the test case if the appropriate conditions are not satisfied to
complete the test.
+Customizing error messages
+--------------------------
+
+Each of the ``KUNIT_EXPECT`` and ``KUNIT_ASSERT`` macros have a ``_MSG``
+variant. These take a format string and arguments to provide additional
+context to the automatically generated error messages.
+
+.. code-block:: c
+
+ char some_str[41];
+ generate_sha1_hex_string(some_str);
+
+ /* Before. Not easy to tell why the test failed. */
+ KUNIT_EXPECT_EQ(test, strlen(some_str), 40);
+
+ /* After. Now we see the offending string. */
+ KUNIT_EXPECT_EQ_MSG(test, strlen(some_str), 40, "some_str='%s'", some_str);
+
+Alternatively, one can take full control over the error message by using
+``KUNIT_FAIL()``, e.g.
+
+.. code-block:: c
+
+ /* Before */
+ KUNIT_EXPECT_EQ(test, some_setup_function(), 0);
+
+ /* After: full control over the failure message. */
+ if (some_setup_function())
+ KUNIT_FAIL(test, "Failed to setup thing for testing");
+
+
Test Suites
~~~~~~~~~~~
@@ -546,24 +577,6 @@ By reusing the same ``cases`` array from above, we can write the test as a
{}
};
-Exiting Early on Failed Expectations
-------------------------------------
-
-We can use ``KUNIT_EXPECT_EQ`` to mark the test as failed and continue
-execution. In some cases, it is unsafe to continue. We can use the
-``KUNIT_ASSERT`` variant to exit on failure.
-
-.. code-block:: c
-
- void example_test_user_alloc_function(struct kunit *test)
- {
- void *object = alloc_some_object_for_me();
-
- /* Make sure we got a valid pointer back. */
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, object);
- do_something_with_object(object);
- }
-
Allocating Memory
-----------------
base-commit: 870f63b7cd78d0055902d839a60408f7428b4e84
--
2.38.1.431.g37b22c650d-goog