Create platform_util for tests.

PiperOrigin-RevId: 291869423
This commit is contained in:
Adin Scannell
2020-01-27 22:28:43 -08:00
committed by gVisor bot
parent 2a2da5be31
commit 5d569408ef
11 changed files with 218 additions and 95 deletions
+77 -57
View File
@@ -15,10 +15,12 @@
#include <string.h>
#include <sys/mman.h>
#include "gtest/gtest.h"
#include "absl/base/macros.h"
#include "test/util/memory_util.h"
#include "test/util/platform_util.h"
#include "test/util/posix_error.h"
#include "test/util/test_util.h"
#include "gtest/gtest.h"
#ifndef __x86_64__
#error "This test is x86-64 specific."
@@ -30,7 +32,6 @@ namespace testing {
namespace {
constexpr char kInt3 = '\xcc';
constexpr char kInt80[2] = {'\xcd', '\x80'};
constexpr char kSyscall[2] = {'\x0f', '\x05'};
constexpr char kSysenter[2] = {'\x0f', '\x34'};
@@ -43,6 +44,7 @@ void ExitGroup32(const char instruction[2], int code) {
// Fill with INT 3 in case we execute too far.
memset(m.ptr(), kInt3, m.len());
// Copy in the actual instruction.
memcpy(m.ptr(), instruction, 2);
// We're playing *extremely* fast-and-loose with the various syscall ABIs
@@ -78,70 +80,87 @@ void ExitGroup32(const char instruction[2], int code) {
constexpr int kExitCode = 42;
TEST(Syscall32Bit, Int80) {
switch (GvisorPlatform()) {
case Platform::kKVM:
// TODO(b/111805002): 32-bit segments are broken (but not explictly
// disabled).
return;
case Platform::kPtrace:
// TODO(gvisor.dev/issue/167): The ptrace platform does not have a
// consistent story here.
return;
case Platform::kNative:
switch (PlatformSupport32Bit()) {
case PlatformSupport::NotSupported:
break;
case PlatformSupport::Segfault:
EXPECT_EXIT(ExitGroup32(kInt80, kExitCode),
::testing::KilledBySignal(SIGSEGV), "");
break;
case PlatformSupport::Ignored:
// Since the call is ignored, we'll hit the int3 trap.
EXPECT_EXIT(ExitGroup32(kInt80, kExitCode),
::testing::KilledBySignal(SIGTRAP), "");
break;
case PlatformSupport::Allowed:
EXPECT_EXIT(ExitGroup32(kInt80, kExitCode), ::testing::ExitedWithCode(42),
"");
break;
}
// Upstream Linux. 32-bit syscalls allowed.
EXPECT_EXIT(ExitGroup32(kInt80, kExitCode), ::testing::ExitedWithCode(42),
"");
}
TEST(Syscall32Bit, Sysenter) {
switch (GvisorPlatform()) {
case Platform::kKVM:
// TODO(b/111805002): See above.
return;
case Platform::kPtrace:
// TODO(gvisor.dev/issue/167): See above.
return;
case Platform::kNative:
break;
}
if (GetCPUVendor() == CPUVendor::kAMD) {
if (PlatformSupport32Bit() == PlatformSupport::Allowed &&
GetCPUVendor() == CPUVendor::kAMD) {
// SYSENTER is an illegal instruction in compatibility mode on AMD.
EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode),
::testing::KilledBySignal(SIGILL), "");
return;
}
// Upstream Linux on !AMD, 32-bit syscalls allowed.
EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode), ::testing::ExitedWithCode(42),
"");
switch (PlatformSupport32Bit()) {
case PlatformSupport::NotSupported:
break;
case PlatformSupport::Segfault:
EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode),
::testing::KilledBySignal(SIGSEGV), "");
break;
case PlatformSupport::Ignored:
// See above, except expected code is SIGSEGV.
EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode),
::testing::KilledBySignal(SIGSEGV), "");
break;
case PlatformSupport::Allowed:
EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode),
::testing::ExitedWithCode(42), "");
break;
}
}
TEST(Syscall32Bit, Syscall) {
switch (GvisorPlatform()) {
case Platform::kKVM:
// TODO(b/111805002): See above.
return;
case Platform::kPtrace:
// TODO(gvisor.dev/issue/167): See above.
return;
case Platform::kNative:
break;
}
if (GetCPUVendor() == CPUVendor::kIntel) {
if (PlatformSupport32Bit() == PlatformSupport::Allowed &&
GetCPUVendor() == CPUVendor::kIntel) {
// SYSCALL is an illegal instruction in compatibility mode on Intel.
EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode),
::testing::KilledBySignal(SIGILL), "");
return;
}
// Upstream Linux on !Intel, 32-bit syscalls allowed.
EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode), ::testing::ExitedWithCode(42),
"");
switch (PlatformSupport32Bit()) {
case PlatformSupport::NotSupported:
break;
case PlatformSupport::Segfault:
EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode),
::testing::KilledBySignal(SIGSEGV), "");
break;
case PlatformSupport::Ignored:
// See above.
EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode),
::testing::KilledBySignal(SIGILL), "");
break;
case PlatformSupport::Allowed:
EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode),
::testing::ExitedWithCode(42), "");
break;
}
}
// Far call code called below.
@@ -205,19 +224,20 @@ void FarCall32() {
}
TEST(Call32Bit, Disallowed) {
switch (GvisorPlatform()) {
case Platform::kKVM:
// TODO(b/111805002): See above.
return;
case Platform::kPtrace:
// The ptrace platform cannot prevent switching to compatibility mode.
ABSL_FALLTHROUGH_INTENDED;
case Platform::kNative:
switch (PlatformSupport32Bit()) {
case PlatformSupport::NotSupported:
break;
}
// Shouldn't crash.
FarCall32();
case PlatformSupport::Segfault:
EXPECT_EXIT(FarCall32(), ::testing::KilledBySignal(SIGSEGV), "");
break;
case PlatformSupport::Ignored:
ABSL_FALLTHROUGH_INTENDED;
case PlatformSupport::Allowed:
// Shouldn't crash.
FarCall32();
}
}
} // namespace
+5
View File
@@ -197,9 +197,11 @@ cc_binary(
linkstatic = 1,
deps = [
"//test/util:memory_util",
"//test/util:platform_util",
"//test/util:posix_error",
"//test/util:test_main",
"//test/util:test_util",
"@com_google_absl//absl/base:core_headers",
"@com_google_googletest//:gtest",
],
)
@@ -479,6 +481,7 @@ cc_binary(
srcs = ["concurrency.cc"],
linkstatic = 1,
deps = [
"//test/util:platform_util",
"//test/util:test_main",
"//test/util:test_util",
"//test/util:thread_util",
@@ -584,6 +587,7 @@ cc_binary(
linkstatic = 1,
deps = [
"//test/util:logging",
"//test/util:platform_util",
"//test/util:signal_util",
"//test/util:test_main",
"//test/util:test_util",
@@ -1658,6 +1662,7 @@ cc_binary(
deps = [
"//test/util:logging",
"//test/util:multiprocess_util",
"//test/util:platform_util",
"//test/util:signal_util",
"//test/util:test_util",
"//test/util:thread_util",
-2
View File
@@ -14,10 +14,8 @@
#include <asm/prctl.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include "gtest/gtest.h"
#include "test/util/file_descriptor.h"
#include "test/util/test_util.h"
// glibc does not provide a prototype for arch_prctl() so declare it here.
+2
View File
@@ -20,6 +20,7 @@
#include "absl/strings/string_view.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "test/util/platform_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
@@ -99,6 +100,7 @@ TEST(ConcurrencyTest, MultiProcessMultithreaded) {
// Test that multiple processes can execute concurrently, even if one process
// never yields.
TEST(ConcurrencyTest, MultiProcessConcurrency) {
SKIP_IF(PlatformSupportMultiProcess() == PlatformSupport::NotSupported);
pid_t child_pid = fork();
if (child_pid == 0) {
+2
View File
@@ -16,6 +16,7 @@
#include "gtest/gtest.h"
#include "test/util/logging.h"
#include "test/util/platform_util.h"
#include "test/util/signal_util.h"
#include "test/util/test_util.h"
@@ -324,6 +325,7 @@ TEST(ExceptionTest, AlignmentHalt) {
}
TEST(ExceptionTest, AlignmentCheck) {
SKIP_IF(PlatformSupportAlignmentCheck() != PlatformSupport::Allowed);
// See above.
struct sigaction sa = {};
+3 -7
View File
@@ -32,6 +32,7 @@
#include "absl/time/time.h"
#include "test/util/logging.h"
#include "test/util/multiprocess_util.h"
#include "test/util/platform_util.h"
#include "test/util/signal_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
@@ -824,13 +825,8 @@ TEST(PtraceTest,
// These tests requires knowledge of architecture-specific syscall convention.
#ifdef __x86_64__
TEST(PtraceTest, Int3) {
switch (GvisorPlatform()) {
case Platform::kKVM:
// TODO(b/124248694): int3 isn't handled properly.
return;
default:
break;
}
SKIP_IF(PlatformSupportInt3() == PlatformSupport::NotSupported);
pid_t const child_pid = fork();
if (child_pid == 0) {
// In child process.
+9 -6
View File
@@ -165,6 +165,14 @@ cc_library(
],
)
cc_library(
name = "platform_util",
testonly = 1,
srcs = ["platform_util.cc"],
hdrs = ["platform_util.h"],
deps = [":test_util"],
)
cc_library(
name = "posix_error",
testonly = 1,
@@ -238,12 +246,7 @@ cc_library(
"test_util_runfiles.cc",
],
hdrs = ["test_util.h"],
defines = select_system(
fuchsia = [
"__opensource__",
"__fuchsia__",
],
),
defines = select_system(),
deps = [
":fs_util",
":logging",
+49
View File
@@ -0,0 +1,49 @@
// Copyright 2020 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "test/util/platform_util.h"
#include "test/util/test_util.h"
namespace gvisor {
namespace testing {
PlatformSupport PlatformSupport32Bit() {
if (GvisorPlatform() == Platform::kPtrace) {
return PlatformSupport::NotSupported;
} else if (GvisorPlatform() == Platform::kKVM) {
return PlatformSupport::Segfault;
} else {
return PlatformSupport::Allowed;
}
}
PlatformSupport PlatformSupportAlignmentCheck() {
return PlatformSupport::Allowed;
}
PlatformSupport PlatformSupportMultiProcess() {
return PlatformSupport::Allowed;
}
PlatformSupport PlatformSupportInt3() {
if (GvisorPlatform() == Platform::kKVM) {
return PlatformSupport::NotSupported;
} else {
return PlatformSupport::Allowed;
}
}
} // namespace testing
} // namespace gvisor
+56
View File
@@ -0,0 +1,56 @@
// Copyright 2020 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef GVISOR_TEST_UTIL_PLATFORM_UTIL_H_
#define GVISOR_TEST_UTIL_PLATFORM_UTIL_H_
namespace gvisor {
namespace testing {
// PlatformSupport is a generic enumeration of classes of support.
//
// It is up to the individual functions and callers to agree on the precise
// definition for each case. The document here generally refers to 32-bit
// as an example. Many cases will use only NotSupported and Allowed.
enum class PlatformSupport {
// The feature is not supported on the current platform.
//
// In the case of 32-bit, this means that calls will generally be interpreted
// as 64-bit calls, and there is no support for 32-bit binaries, long calls,
// etc. This usually means that the underlying implementation just pretends
// that 32-bit doesn't exist.
NotSupported,
// Calls will be ignored by the kernel with a fixed error.
Ignored,
// Calls will result in a SIGSEGV or similar fault.
Segfault,
// The feature is supported as expected.
//
// In the case of 32-bit, this means that the system call or far call will be
// handled properly.
Allowed,
};
PlatformSupport PlatformSupport32Bit();
PlatformSupport PlatformSupportAlignmentCheck();
PlatformSupport PlatformSupportMultiProcess();
PlatformSupport PlatformSupportInt3();
} // namespace testing
} // namespace gvisor
#endif // GVISOR_TEST_UTIL_PLATFORM_UTL_H_
+2 -9
View File
@@ -45,20 +45,13 @@ namespace testing {
bool IsRunningOnGvisor() { return GvisorPlatform() != Platform::kNative; }
Platform GvisorPlatform() {
const std::string GvisorPlatform() {
// Set by runner.go.
char* env = getenv(TEST_ON_GVISOR);
if (!env) {
return Platform::kNative;
}
if (strcmp(env, "ptrace") == 0) {
return Platform::kPtrace;
}
if (strcmp(env, "kvm") == 0) {
return Platform::kKVM;
}
std::cerr << "unknown platform " << env;
abort();
return std::string(env);
}
bool IsRunningWithHostinet() {
+13 -14
View File
@@ -26,16 +26,13 @@
// IsRunningOnGvisor returns true if the test is known to be running on gVisor.
// GvisorPlatform can be used to get more detail:
//
// switch (GvisorPlatform()) {
// case Platform::kNative:
// case Platform::kGvisor:
// EXPECT_THAT(mmap(...), SyscallSucceeds());
// break;
// case Platform::kPtrace:
// EXPECT_THAT(mmap(...), SyscallFailsWithErrno(ENOSYS));
// break;
// if (GvisorPlatform() == Platform::kPtrace) {
// ...
// }
//
// SetupGvisorDeathTest ensures that signal handling does not interfere with
/// tests that rely on fatal signals.
//
// Matchers
// ========
//
@@ -213,13 +210,15 @@ void TestInit(int* argc, char*** argv);
if (expr) GTEST_SKIP() << #expr; \
} while (0)
enum class Platform {
kNative,
kKVM,
kPtrace,
};
// Platform contains platform names.
namespace Platform {
constexpr char kNative[] = "native";
constexpr char kPtrace[] = "ptrace";
constexpr char kKVM[] = "kvm";
} // namespace Platform
bool IsRunningOnGvisor();
Platform GvisorPlatform();
const std::string GvisorPlatform();
bool IsRunningWithHostinet();
#ifdef __linux__