summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChong Cai <chongc@google.com>2021-04-15 22:10:17 -0700
committergVisor bot <gvisor-bot@google.com>2021-04-15 22:12:37 -0700
commitc980fe573d7a3488dc27c58f84aecf9ae1814f49 (patch)
tree3386a5b4493557f2d7c22b310befdd8258a3e633
parent14b7d775c950070378ea799a0b6b7907f67a1f1e (diff)
Add verity ioctl test for mount with root hash
PiperOrigin-RevId: 368779532
-rw-r--r--test/syscalls/linux/verity_ioctl.cc85
1 files changed, 70 insertions, 15 deletions
diff --git a/test/syscalls/linux/verity_ioctl.cc b/test/syscalls/linux/verity_ioctl.cc
index dcd28f2c3..a81fe5724 100644
--- a/test/syscalls/linux/verity_ioctl.cc
+++ b/test/syscalls/linux/verity_ioctl.cc
@@ -12,8 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <stdint.h>
#include <sys/mount.h>
+#include <iomanip>
+#include <sstream>
+
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "test/util/capability_util.h"
@@ -49,8 +53,9 @@ struct fsverity_digest {
__u8 digest[];
};
-const int fsverity_max_digest_size = 64;
-const int fsverity_default_digest_size = 32;
+constexpr int kMaxDigestSize = 64;
+constexpr int kDefaultDigestSize = 32;
+constexpr char kContents[] = "foobarbaz";
class IoctlTest : public ::testing::Test {
protected:
@@ -65,7 +70,6 @@ class IoctlTest : public ::testing::Test {
SyscallSucceeds());
// Create a new file in the tmpfs mount.
- constexpr char kContents[] = "foobarbaz";
file_ = ASSERT_NO_ERRNO_AND_VALUE(
TempPath::CreateFileWith(tmpfs_dir_.path(), kContents, 0777));
filename_ = Basename(file_.path());
@@ -76,17 +80,26 @@ class IoctlTest : public ::testing::Test {
std::string filename_;
};
+// 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) {
+ 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();
+}
+
TEST_F(IoctlTest, Enable) {
- // mount a verity fs on the existing tmpfs mount.
+ // Mount a verity fs on the existing tmpfs mount.
std::string mount_opts = "lower_path=" + tmpfs_dir_.path();
auto const verity_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
ASSERT_THAT(
mount("", verity_dir.path().c_str(), "verity", 0, mount_opts.c_str()),
SyscallSucceeds());
- printf("verity path: %s, filename: %s\n", verity_dir.path().c_str(),
- filename_.c_str());
- fflush(nullptr);
// Confirm that the verity flag is absent.
int flag = 0;
auto const fd = ASSERT_NO_ERRNO_AND_VALUE(
@@ -101,7 +114,7 @@ TEST_F(IoctlTest, Enable) {
}
TEST_F(IoctlTest, Measure) {
- // mount a verity fs on the existing tmpfs mount.
+ // Mount a verity fs on the existing tmpfs mount.
std::string mount_opts = "lower_path=" + tmpfs_dir_.path();
auto const verity_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
ASSERT_THAT(
@@ -111,11 +124,10 @@ TEST_F(IoctlTest, Measure) {
// Confirm that the file cannot be measured.
auto const fd = ASSERT_NO_ERRNO_AND_VALUE(
Open(JoinPath(verity_dir.path(), filename_), O_RDONLY, 0777));
- int digest_size = sizeof(struct fsverity_digest) + fsverity_max_digest_size;
- struct fsverity_digest *digest =
- reinterpret_cast<struct fsverity_digest *>(malloc(digest_size));
- memset(digest, 0, digest_size);
- digest->digest_size = fsverity_max_digest_size;
+ 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;
ASSERT_THAT(ioctl(fd.get(), FS_IOC_MEASURE_VERITY, digest),
SyscallFailsWithErrno(ENODATA));
@@ -123,8 +135,51 @@ TEST_F(IoctlTest, Measure) {
ASSERT_THAT(ioctl(fd.get(), FS_IOC_ENABLE_VERITY), SyscallSucceeds());
ASSERT_THAT(ioctl(fd.get(), FS_IOC_MEASURE_VERITY, digest),
SyscallSucceeds());
- EXPECT_EQ(digest->digest_size, fsverity_default_digest_size);
- free(digest);
+ EXPECT_EQ(digest->digest_size, kDefaultDigestSize);
+}
+
+TEST_F(IoctlTest, Mount) {
+ // Mount a verity fs on the existing tmpfs mount.
+ std::string mount_opts = "lower_path=" + tmpfs_dir_.path();
+ auto verity_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ ASSERT_THAT(
+ mount("", verity_dir.path().c_str(), "verity", 0, mount_opts.c_str()),
+ SyscallSucceeds());
+
+ // Enable both the file and the directory.
+ auto const fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Open(JoinPath(verity_dir.path(), filename_), O_RDONLY, 0777));
+ ASSERT_THAT(ioctl(fd.get(), FS_IOC_ENABLE_VERITY), SyscallSucceeds());
+ auto const dir_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(verity_dir.path(), O_RDONLY, 0777));
+ ASSERT_THAT(ioctl(dir_fd.get(), FS_IOC_ENABLE_VERITY), SyscallSucceeds());
+
+ // 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;
+ ASSERT_THAT(ioctl(dir_fd.get(), FS_IOC_MEASURE_VERITY, digest),
+ SyscallSucceeds());
+
+ // Mount a verity fs with specified root hash.
+ mount_opts +=
+ ",root_hash=" + BytesToHexString(digest->digest, digest->digest_size);
+ auto verity_with_hash_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ ASSERT_THAT(mount("", verity_with_hash_dir.path().c_str(), "verity", 0,
+ mount_opts.c_str()),
+ SyscallSucceeds());
+
+ // Make sure the file can be open and read in the mounted verity fs.
+ auto const verity_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Open(JoinPath(verity_with_hash_dir.path(), filename_), O_RDONLY, 0777));
+ char buf[16];
+ EXPECT_THAT(ReadFd(fd.get(), buf, sizeof(kContents)), SyscallSucceeds());
+
+ // Verity directories should not be deleted. Release the TempPath objects to
+ // prevent those directories from being deleted by the destructor.
+ verity_dir.release();
+ verity_with_hash_dir.release();
}
} // namespace