diff options
Diffstat (limited to 'test/util')
-rw-r--r-- | test/util/BUILD | 13 | ||||
-rw-r--r-- | test/util/cgroup_util.cc | 14 | ||||
-rw-r--r-- | test/util/cgroup_util.h | 2 | ||||
-rw-r--r-- | test/util/verity_util.cc | 93 | ||||
-rw-r--r-- | test/util/verity_util.h | 75 |
5 files changed, 197 insertions, 0 deletions
diff --git a/test/util/BUILD b/test/util/BUILD index 8985b54af..cc83221ea 100644 --- a/test/util/BUILD +++ b/test/util/BUILD @@ -401,3 +401,16 @@ cc_library( "@com_google_absl//absl/strings", ], ) + +cc_library( + name = "verity_util", + testonly = 1, + srcs = ["verity_util.cc"], + hdrs = ["verity_util.h"], + deps = [ + ":fs_util", + ":mount_util", + ":posix_error", + ":temp_path", + ], +) diff --git a/test/util/cgroup_util.cc b/test/util/cgroup_util.cc index 04d4f8de0..977993f41 100644 --- a/test/util/cgroup_util.cc +++ b/test/util/cgroup_util.cc @@ -142,6 +142,20 @@ PosixError Mounter::Unmount(const Cgroup& c) { return NoError(); } +void Mounter::release(const Cgroup& c) { + auto mp = mountpoints_.find(c.id()); + if (mp != mountpoints_.end()) { + mp->second.release(); + mountpoints_.erase(mp); + } + + auto m = mounts_.find(c.id()); + if (m != mounts_.end()) { + m->second.Release(); + mounts_.erase(m); + } +} + constexpr char kProcCgroupsHeader[] = "#subsys_name\thierarchy\tnum_cgroups\tenabled"; diff --git a/test/util/cgroup_util.h b/test/util/cgroup_util.h index b797a8b24..e3f696a89 100644 --- a/test/util/cgroup_util.h +++ b/test/util/cgroup_util.h @@ -83,6 +83,8 @@ class Mounter { PosixError Unmount(const Cgroup& c); + void release(const Cgroup& c); + private: // The destruction order of these members avoids errors during cleanup. We // first unmount (by executing the mounts_ cleanups), then delete the diff --git a/test/util/verity_util.cc b/test/util/verity_util.cc new file mode 100644 index 000000000..f1b4c251b --- /dev/null +++ b/test/util/verity_util.cc @@ -0,0 +1,93 @@ +// Copyright 2021 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/verity_util.h" + +#include "test/util/fs_util.h" +#include "test/util/mount_util.h" +#include "test/util/temp_path.h" + +namespace gvisor { +namespace testing { + +std::string BytesToHexString(uint8_t bytes[], int size) { + std::stringstream ss; + ss << std::hex; + for (int i = 0; i < size; ++i) { + ss << std::setw(2) << std::setfill('0') << static_cast<int>(bytes[i]); + } + return ss.str(); +} + +std::string MerklePath(absl::string_view path) { + return JoinPath(Dirname(path), + std::string(kMerklePrefix) + std::string(Basename(path))); +} + +std::string MerkleRootPath(absl::string_view path) { + return JoinPath(Dirname(path), + std::string(kMerkleRootPrefix) + std::string(Basename(path))); +} + +PosixError FlipRandomBit(int fd, int size) { + // Generate a random offset in the file. + srand(time(nullptr)); + unsigned int seed = 0; + int random_offset = rand_r(&seed) % size; + + // Read a random byte and flip a bit in it. + char buf[1]; + RETURN_ERROR_IF_SYSCALL_FAIL(PreadFd(fd, buf, 1, random_offset)); + buf[0] ^= 1; + RETURN_ERROR_IF_SYSCALL_FAIL(PwriteFd(fd, buf, 1, random_offset)); + return NoError(); +} + +PosixErrorOr<std::string> MountVerity(std::string tmpfs_dir, + std::string filename) { + // Mount a verity fs on the existing tmpfs mount. + std::string mount_opts = "lower_path=" + tmpfs_dir; + ASSIGN_OR_RETURN_ERRNO(TempPath verity_dir, TempPath::CreateDir()); + RETURN_ERROR_IF_SYSCALL_FAIL( + mount("", verity_dir.path().c_str(), "verity", 0, mount_opts.c_str())); + + // Enable both the file and the directory. + ASSIGN_OR_RETURN_ERRNO( + auto fd, Open(JoinPath(verity_dir.path(), filename), O_RDONLY, 0777)); + RETURN_ERROR_IF_SYSCALL_FAIL(ioctl(fd.get(), FS_IOC_ENABLE_VERITY)); + ASSIGN_OR_RETURN_ERRNO(auto dir_fd, Open(verity_dir.path(), O_RDONLY, 0777)); + RETURN_ERROR_IF_SYSCALL_FAIL(ioctl(dir_fd.get(), FS_IOC_ENABLE_VERITY)); + + // Measure the root hash. + uint8_t digest_array[sizeof(struct fsverity_digest) + kMaxDigestSize] = {0}; + struct fsverity_digest* digest = + reinterpret_cast<struct fsverity_digest*>(digest_array); + digest->digest_size = kMaxDigestSize; + RETURN_ERROR_IF_SYSCALL_FAIL( + ioctl(dir_fd.get(), FS_IOC_MEASURE_VERITY, digest)); + + // Mount a verity fs with specified root hash. + mount_opts += + ",root_hash=" + BytesToHexString(digest->digest, digest->digest_size); + ASSIGN_OR_RETURN_ERRNO(TempPath verity_with_hash_dir, TempPath::CreateDir()); + RETURN_ERROR_IF_SYSCALL_FAIL(mount("", verity_with_hash_dir.path().c_str(), + "verity", 0, mount_opts.c_str())); + // Verity directories should not be deleted. Release the TempPath objects to + // prevent those directories from being deleted by the destructor. + verity_dir.release(); + return verity_with_hash_dir.release(); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/util/verity_util.h b/test/util/verity_util.h new file mode 100644 index 000000000..18743ecd6 --- /dev/null +++ b/test/util/verity_util.h @@ -0,0 +1,75 @@ +// Copyright 2021 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_VERITY_UTIL_H_ +#define GVISOR_TEST_UTIL_VERITY_UTIL_H_ + +#include <stdint.h> + +#include "test/util/posix_error.h" + +namespace gvisor { +namespace testing { + +#ifndef FS_IOC_ENABLE_VERITY +#define FS_IOC_ENABLE_VERITY 1082156677 +#endif + +#ifndef FS_IOC_MEASURE_VERITY +#define FS_IOC_MEASURE_VERITY 3221513862 +#endif + +#ifndef FS_VERITY_FL +#define FS_VERITY_FL 1048576 +#endif + +#ifndef FS_IOC_GETFLAGS +#define FS_IOC_GETFLAGS 2148034049 +#endif + +struct fsverity_digest { + unsigned short digest_algorithm; + unsigned short digest_size; /* input/output */ + unsigned char digest[]; +}; + +constexpr int kMaxDigestSize = 64; +constexpr int kDefaultDigestSize = 32; +constexpr char kContents[] = "foobarbaz"; +constexpr char kMerklePrefix[] = ".merkle.verity."; +constexpr char kMerkleRootPrefix[] = ".merkleroot.verity."; + +// Get the Merkle tree file path for |path|. +std::string MerklePath(absl::string_view path); + +// Get the root Merkle tree file path for |path|. +std::string MerkleRootPath(absl::string_view path); + +// Provide a function to convert bytes to hex string, since +// absl::BytesToHexString does not seem to be compatible with golang +// hex.DecodeString used in verity due to zero-padding. +std::string BytesToHexString(uint8_t bytes[], int size); + +// Flip a random bit in the file represented by fd. +PosixError FlipRandomBit(int fd, int size); + +// Mount a verity on the tmpfs and enable both the file and the direcotry. Then +// mount a new verity with measured root hash. +PosixErrorOr<std::string> MountVerity(std::string tmpfs_dir, + std::string filename); + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_UTIL_VERITY_UTIL_H_ |