diff --git a/fs/incfs/Makefile b/fs/incfs/Makefile index 0c3f6d348bec..3503eda7a6e6 100644 --- a/fs/incfs/Makefile +++ b/fs/incfs/Makefile @@ -8,3 +8,5 @@ incrementalfs-y := \ main.o \ pseudo_files.o \ vfs.o + +incrementalfs-$(CONFIG_FS_VERITY) += verity.o diff --git a/fs/incfs/data_mgmt.c b/fs/incfs/data_mgmt.c index e127e9f2b3e6..d8173e10f5e0 100644 --- a/fs/incfs/data_mgmt.c +++ b/fs/incfs/data_mgmt.c @@ -260,6 +260,8 @@ struct data_file *incfs_open_data_file(struct mount_info *mi, struct file *bf) goto out; } + mutex_init(&df->df_enable_verity); + df->df_backing_file_context = bfc; df->df_mount_info = mi; for (i = 0; i < ARRAY_SIZE(df->df_segments); i++) @@ -329,6 +331,8 @@ void incfs_free_data_file(struct data_file *df) incfs_free_mtree(df->df_hash_tree); incfs_free_bfc(df->df_backing_file_context); kfree(df->df_signature); + kfree(df->df_verity_file_digest.data); + mutex_destroy(&df->df_enable_verity); kfree(df); } diff --git a/fs/incfs/data_mgmt.h b/fs/incfs/data_mgmt.h index 77e0f557f4e4..04b963e215b2 100644 --- a/fs/incfs/data_mgmt.h +++ b/fs/incfs/data_mgmt.h @@ -273,9 +273,30 @@ struct data_file { /* Offset to status metadata header */ loff_t df_status_offset; + /* + * Mutex acquired while enabling verity. Note that df_hash_tree is set + * by enable verity. + * + * The backing file mutex bc_mutex may be taken while this mutex is + * held. + */ + struct mutex df_enable_verity; + + /* + * Set either at construction time or during enabling verity. In the + * latter case, set via smp_store_release, so use smp_load_acquire to + * read it. + */ struct mtree *df_hash_tree; + /* Guaranteed set if df_hash_tree is set. */ struct incfs_df_signature *df_signature; + + /* + * The verity file digest, set when verity is enabled and the file has + * been opened + */ + struct mem_range df_verity_file_digest; }; struct dir_file { diff --git a/fs/incfs/internal.h b/fs/incfs/internal.h index 0a85eaed41d3..c2df8bf82d6f 100644 --- a/fs/incfs/internal.h +++ b/fs/incfs/internal.h @@ -18,4 +18,6 @@ static inline struct mem_range range(u8 *data, size_t len) #define LOCK_REQUIRED(lock) WARN_ON_ONCE(!mutex_is_locked(&lock)) +#define EFSCORRUPTED EUCLEAN + #endif /* _INCFS_INTERNAL_H */ diff --git a/fs/incfs/verity.c b/fs/incfs/verity.c new file mode 100644 index 000000000000..8c50e4813f4b --- /dev/null +++ b/fs/incfs/verity.c @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020 Google LLC + */ + +/* + * fs-verity integration into incfs + * + * Since incfs has its own merkle tree implementation, most of fs-verity code + * is not needed. The key part that is needed is the signature check, since + * that is based on the private /proc/sys/fs/verity/require_signatures value + * and a private keyring. Thus the first change is to modify verity code to + * export a version of fsverity_verify_signature. + * + * fs-verity integration then consists of the following modifications: + * + * 1. Add the (optional) verity signature to the incfs file format + * 2. Add a pointer to the digest of the fs-verity descriptor struct to the + * data_file struct that incfs attaches to each file inode. + * 3. Add the following ioclts: + * - FS_IOC_ENABLE_VERITY + * - FS_IOC_GETFLAGS + * - FS_IOC_MEASURE_VERITY + * 4. When FS_IOC_ENABLE_VERITY is called on a non-verity file, the + * fs-verity descriptor struct is populated and digested. If it passes the + * signature check or the signature is NULL and + * fs.verity.require_signatures=0, then the S_VERITY flag is set and the + * xattr incfs.verity is set. If the signature is non-NULL, an + * INCFS_MD_VERITY_SIGNATURE is added to the backing file containing the + * signature. + * 5. When a file with an incfs.verity xattr's inode is initialized, the + * inode’s S_VERITY flag is set. + * 6. When a file with the S_VERITY flag set on its inode is opened, the + * data_file is checked for its verity digest. If the file doesn’t have a + * digest, the file’s digest is calculated as above, checked, and set, or the + * open is denied if it is not valid. + * 7. FS_IOC_GETFLAGS simply returns the value of the S_VERITY flag + * 8. FS_IOC_MEASURE_VERITY simply returns the cached digest + * 9. The final complication is that if FS_IOC_ENABLE_VERITY is called on a file + * which doesn’t have a merkle tree, the merkle tree is calculated before the + * rest of the process is completed. + */ + +#include +#include +#include +#include + +#include "verity.h" + +#include "data_mgmt.h" +#include "integrity.h" +#include "vfs.h" + +#define FS_VERITY_MAX_SIGNATURE_SIZE 16128 + +static int incfs_get_root_hash(struct file *filp, u8 *root_hash) +{ + struct data_file *df = get_incfs_data_file(filp); + + if (!df) + return -EINVAL; + + memcpy(root_hash, df->df_hash_tree->root_hash, + df->df_hash_tree->alg->digest_size); + + return 0; +} + +static int incfs_end_enable_verity(struct file *filp) +{ + struct inode *inode = file_inode(filp); + + inode_set_flags(inode, S_VERITY, S_VERITY); + return 0; +} + +static int incfs_compute_file_digest(struct incfs_hash_alg *alg, + struct fsverity_descriptor *desc, + u8 *digest) +{ + SHASH_DESC_ON_STACK(d, alg->shash); + + d->tfm = alg->shash; + return crypto_shash_digest(d, (u8 *)desc, sizeof(*desc), digest); +} + +static enum incfs_hash_tree_algorithm incfs_convert_fsverity_hash_alg( + int hash_alg) +{ + switch (hash_alg) { + case FS_VERITY_HASH_ALG_SHA256: + return INCFS_HASH_TREE_SHA256; + default: + return -EINVAL; + } +} + +static struct mem_range incfs_get_verity_digest(struct inode *inode) +{ + struct inode_info *node = get_incfs_node(inode); + struct data_file *df; + struct mem_range verity_file_digest; + + if (!node) { + pr_warn("Invalid inode\n"); + return range(NULL, 0); + } + + df = node->n_file; + + /* + * Pairs with the cmpxchg_release() in incfs_set_verity_digest(). + * I.e., another task may publish ->df_verity_file_digest concurrently, + * executing a RELEASE barrier. We need to use smp_load_acquire() here + * to safely ACQUIRE the memory the other task published. + */ + verity_file_digest.data = smp_load_acquire( + &df->df_verity_file_digest.data); + verity_file_digest.len = df->df_verity_file_digest.len; + return verity_file_digest; +} + +static void incfs_set_verity_digest(struct inode *inode, + struct mem_range verity_file_digest) +{ + struct inode_info *node = get_incfs_node(inode); + struct data_file *df; + + if (!node) { + pr_warn("Invalid inode\n"); + kfree(verity_file_digest.data); + return; + } + + df = node->n_file; + df->df_verity_file_digest.len = verity_file_digest.len; + + /* + * Multiple tasks may race to set ->df_verity_file_digest.data, so use + * cmpxchg_release(). This pairs with the smp_load_acquire() in + * incfs_get_verity_digest(). I.e., here we publish + * ->df_verity_file_digest.data, with a RELEASE barrier so that other + * tasks can ACQUIRE it. + */ + if (cmpxchg_release(&df->df_verity_file_digest.data, NULL, + verity_file_digest.data) != NULL) + /* Lost the race, so free the file_digest we allocated. */ + kfree(verity_file_digest.data); +} + +/* + * Calculate the digest of the fsverity_descriptor. The signature (if present) + * is also checked. + */ +static struct mem_range incfs_calc_verity_digest_from_desc( + const struct inode *inode, + struct fsverity_descriptor *desc, + u8 *signature, size_t sig_size) +{ + enum incfs_hash_tree_algorithm incfs_hash_alg; + struct mem_range verity_file_digest; + int err; + struct incfs_hash_alg *hash_alg; + + incfs_hash_alg = incfs_convert_fsverity_hash_alg(desc->hash_algorithm); + if (incfs_hash_alg < 0) + return range(ERR_PTR(incfs_hash_alg), 0); + + hash_alg = incfs_get_hash_alg(incfs_hash_alg); + if (IS_ERR(hash_alg)) + return range((u8 *)hash_alg, 0); + + verity_file_digest = range(kzalloc(hash_alg->digest_size, GFP_KERNEL), + hash_alg->digest_size); + if (!verity_file_digest.data) + return range(ERR_PTR(-ENOMEM), 0); + + err = incfs_compute_file_digest(hash_alg, desc, + verity_file_digest.data); + if (err) { + pr_err("Error %d computing file digest", err); + goto out; + } + pr_debug("Computed file digest: %s:%*phN\n", + hash_alg->name, (int) verity_file_digest.len, + verity_file_digest.data); + + err = __fsverity_verify_signature(inode, signature, sig_size, + verity_file_digest.data, + desc->hash_algorithm); +out: + if (err) { + kfree(verity_file_digest.data); + verity_file_digest = range(ERR_PTR(err), 0); + } + return verity_file_digest; +} + +static struct mem_range incfs_calc_verity_digest( + struct inode *inode, struct file *filp, + u8 *signature, size_t signature_size, + int hash_algorithm) +{ + struct fsverity_descriptor *desc = kzalloc(sizeof(*desc), GFP_KERNEL); + int err; + struct mem_range verity_file_digest; + + if (!desc) + return range(ERR_PTR(-ENOMEM), 0); + + *desc = (struct fsverity_descriptor) { + .version = 1, + .hash_algorithm = hash_algorithm, + .log_blocksize = ilog2(INCFS_DATA_FILE_BLOCK_SIZE), + .data_size = cpu_to_le64(inode->i_size), + }; + + err = incfs_get_root_hash(filp, desc->root_hash); + if (err) + goto out; + + verity_file_digest = incfs_calc_verity_digest_from_desc(inode, desc, + signature, signature_size); + +out: + kfree(desc); + if (err) + return range(ERR_PTR(err), 0); + return verity_file_digest; +} + +static int incfs_enable_verity(struct file *filp, + const struct fsverity_enable_arg *arg) +{ + struct inode *inode = file_inode(filp); + struct data_file *df = get_incfs_data_file(filp); + u8 *signature = NULL; + struct mem_range verity_file_digest = range(NULL, 0); + int err; + + if (!df) + return -EFSCORRUPTED; + + err = mutex_lock_interruptible(&df->df_enable_verity); + if (err) + return err; + + /* Get the signature if the user provided one */ + if (arg->sig_size) { + signature = memdup_user(u64_to_user_ptr(arg->sig_ptr), + arg->sig_size); + if (IS_ERR(signature)) { + err = PTR_ERR(signature); + signature = NULL; + goto out; + } + } + + verity_file_digest = incfs_calc_verity_digest(inode, filp, signature, + arg->sig_size, arg->hash_algorithm); + if (IS_ERR(verity_file_digest.data)) { + err = PTR_ERR(verity_file_digest.data); + verity_file_digest.data = NULL; + goto out; + } + + err = incfs_end_enable_verity(filp); + if (err) + goto out; + + /* Successfully enabled verity */ + incfs_set_verity_digest(inode, verity_file_digest); + verity_file_digest.data = NULL; +out: + mutex_unlock(&df->df_enable_verity); + kfree(signature); + kfree(verity_file_digest.data); + if (err) + pr_err("%s failed with err %d\n", __func__, err); + return err; +} + +int incfs_ioctl_enable_verity(struct file *filp, const void __user *uarg) +{ + struct inode *inode = file_inode(filp); + struct fsverity_enable_arg arg; + + if (copy_from_user(&arg, uarg, sizeof(arg))) + return -EFAULT; + + if (arg.version != 1) + return -EINVAL; + + if (arg.__reserved1 || + memchr_inv(arg.__reserved2, 0, sizeof(arg.__reserved2))) + return -EINVAL; + + if (arg.hash_algorithm != FS_VERITY_HASH_ALG_SHA256) + return -EINVAL; + + if (arg.block_size != PAGE_SIZE) + return -EINVAL; + + if (arg.salt_size) + return -EINVAL; + + if (arg.sig_size > FS_VERITY_MAX_SIGNATURE_SIZE) + return -EMSGSIZE; + + if (S_ISDIR(inode->i_mode)) + return -EISDIR; + + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + + return incfs_enable_verity(filp, &arg); +} diff --git a/fs/incfs/verity.h b/fs/incfs/verity.h new file mode 100644 index 000000000000..8902d1489f00 --- /dev/null +++ b/fs/incfs/verity.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2020 Google LLC + */ + +#ifndef _INCFS_VERITY_H +#define _INCFS_VERITY_H + +#ifdef CONFIG_FS_VERITY + +int incfs_ioctl_enable_verity(struct file *filp, const void __user *uarg); + +#else /* !CONFIG_FS_VERITY */ + +static inline int incfs_ioctl_enable_verity(struct file *filp, + const void __user *uarg) +{ + return -EOPNOTSUPP; +} + +#endif /* !CONFIG_FS_VERITY */ + +#endif diff --git a/fs/incfs/vfs.c b/fs/incfs/vfs.c index e81b8819a5e3..ab3fa56490f0 100644 --- a/fs/incfs/vfs.c +++ b/fs/incfs/vfs.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,7 @@ #include "format.h" #include "internal.h" #include "pseudo_files.h" +#include "verity.h" static int incfs_remount_fs(struct super_block *sb, int *flags, char *data); @@ -815,6 +817,8 @@ static long dispatch_ioctl(struct file *f, unsigned int req, unsigned long arg) return ioctl_get_filled_blocks(f, (void __user *)arg); case INCFS_IOC_GET_BLOCK_COUNT: return ioctl_get_block_count(f, (void __user *)arg); + case FS_IOC_ENABLE_VERITY: + return incfs_ioctl_enable_verity(f, (const void __user *)arg); default: return -EINVAL; } diff --git a/tools/testing/selftests/filesystems/incfs/incfs_test.c b/tools/testing/selftests/filesystems/incfs/incfs_test.c index fe22077c4521..9939792275d6 100644 --- a/tools/testing/selftests/filesystems/incfs/incfs_test.c +++ b/tools/testing/selftests/filesystems/incfs/incfs_test.c @@ -27,7 +27,11 @@ #include #include +#include +#include + #include +#include #include "utils.h" @@ -73,6 +77,23 @@ struct { #define TESTNE(statement, res) \ TESTCOND((statement) != (res)) +void print_bytes(const void *data, size_t size) +{ + const uint8_t *bytes = data; + int i; + + for (i = 0; i < size; ++i) { + if (i % 0x10 == 0) + printf("%08x:", i); + printf("%02x ", (unsigned int) bytes[i]); + if (i % 0x10 == 0x0f) + printf("\n"); + } + + if (i % 0x10 != 0) + printf("\n"); +} + struct hash_block { char data[INCFS_DATA_FILE_BLOCK_SIZE]; }; @@ -3620,6 +3641,266 @@ static int inotify_test(const char *mount_dir) return result; } +static EVP_PKEY *create_key(void) +{ + EVP_PKEY *pkey = NULL; + RSA *rsa = NULL; + BIGNUM *bn = NULL; + + pkey = EVP_PKEY_new(); + if (!pkey) + goto fail; + + bn = BN_new(); + BN_set_word(bn, RSA_F4); + + rsa = RSA_new(); + if (!rsa) + goto fail; + + RSA_generate_key_ex(rsa, 4096, bn, NULL); + EVP_PKEY_assign_RSA(pkey, rsa); + + BN_free(bn); + return pkey; + +fail: + BN_free(bn); + EVP_PKEY_free(pkey); + return NULL; +} + +static X509 *get_cert(EVP_PKEY *key) +{ + X509 *x509 = NULL; + X509_NAME *name = NULL; + + x509 = X509_new(); + if (!x509) + return NULL; + + ASN1_INTEGER_set(X509_get_serialNumber(x509), 1); + X509_gmtime_adj(X509_get_notBefore(x509), 0); + X509_gmtime_adj(X509_get_notAfter(x509), 31536000L); + X509_set_pubkey(x509, key); + + name = X509_get_subject_name(x509); + X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, + (const unsigned char *)"US", -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_ASC, + (const unsigned char *)"CA", -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "L", MBSTRING_ASC, + (const unsigned char *)"San Jose", -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, + (const unsigned char *)"Example", -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_ASC, + (const unsigned char *)"Org", -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, + (const unsigned char *)"www.example.com", -1, -1, 0); + X509_set_issuer_name(x509, name); + + if (!X509_sign(x509, key, EVP_sha256())) + return NULL; + + return x509; +} + +static int sign(EVP_PKEY *key, X509 *cert, const char *data, size_t len, + unsigned char **sig, size_t *sig_len) +{ + const int pkcs7_flags = PKCS7_BINARY | PKCS7_NOATTR | PKCS7_PARTIAL | + PKCS7_DETACHED; + const EVP_MD *md = EVP_sha256(); + + int result = TEST_FAILURE; + + BIO *bio = NULL; + PKCS7 *p7 = NULL; + unsigned char *bio_buffer; + + TEST(bio = BIO_new_mem_buf(data, len), bio); + TEST(p7 = PKCS7_sign(NULL, NULL, NULL, bio, pkcs7_flags), p7); + TESTNE(PKCS7_sign_add_signer(p7, cert, key, md, pkcs7_flags), 0); + TESTEQUAL(PKCS7_final(p7, bio, pkcs7_flags), 1); + TEST(*sig_len = i2d_PKCS7(p7, NULL), *sig_len); + TEST(bio_buffer = malloc(*sig_len), bio_buffer); + *sig = bio_buffer; + TEST(*sig_len = i2d_PKCS7(p7, &bio_buffer), *sig_len); + TESTEQUAL(PKCS7_verify(p7, NULL, NULL, bio, NULL, + pkcs7_flags | PKCS7_NOVERIFY | PKCS7_NOSIGS), 1); + + result = TEST_SUCCESS; +out: + PKCS7_free(p7); + BIO_free(bio); + return result; +} + +static int validate_verity(const char *mount_dir, struct test_file *file, + EVP_PKEY *key, X509 *cert, bool use_signatures) +{ + int result = TEST_FAILURE; + char *filename = NULL; + int fd = -1; + struct fsverity_enable_arg fear = { + .version = 1, + .hash_algorithm = FS_VERITY_HASH_ALG_SHA256, + .block_size = INCFS_DATA_FILE_BLOCK_SIZE, + .sig_size = 0, + .sig_ptr = 0, + }; + unsigned char *sig = NULL; + size_t sig_len; + struct { + __u8 version; /* must be 1 */ + __u8 hash_algorithm; /* Merkle tree hash algorithm */ + __u8 log_blocksize; /* log2 of size of data and tree blocks */ + __u8 salt_size; /* size of salt in bytes; 0 if none */ + __le32 sig_size; /* must be 0 */ + __le64 data_size; /* size of file the Merkle tree is built over */ + __u8 root_hash[64]; /* Merkle tree root hash */ + __u8 salt[32]; /* salt prepended to each hashed block */ + __u8 __reserved[144]; /* must be 0's */ + } __packed fsverity_descriptor = { + .version = 1, + .hash_algorithm = 1, + .log_blocksize = 12, + .data_size = file->size, + }; + struct { + char magic[8]; /* must be "FSVerity" */ + __le16 digest_algorithm; + __le16 digest_size; + __u8 digest[32]; + } __packed fsverity_signed_digest = { + .digest_algorithm = 1, + .digest_size = 32 + }; + + memcpy(fsverity_signed_digest.magic, "FSVerity", 8); + + TEST(filename = concat_file_name(mount_dir, file->name), filename); + TEST(fd = open(filename, O_RDONLY | O_CLOEXEC), fd != -1); + + /* First try to enable verity with random digest */ + TESTEQUAL(sign(key, cert, (void *)&fsverity_signed_digest, + sizeof(fsverity_signed_digest), &sig, &sig_len), + 0); + + fear.sig_size = sig_len; + fear.sig_ptr = ptr_to_u64(sig); + TESTEQUAL(ioctl(fd, FS_IOC_ENABLE_VERITY, &fear), -1); + + /* Now try with correct digest */ + memcpy(fsverity_descriptor.root_hash, file->root_hash, 32); + sha256((char *)&fsverity_descriptor, sizeof(fsverity_descriptor), + (char *)fsverity_signed_digest.digest); + + if (ioctl(fd, FS_IOC_ENABLE_VERITY, NULL) == -1 && + errno == EOPNOTSUPP) { + result = TEST_SUCCESS; + goto out; + } + + TESTEQUAL(sign(key, cert, (void *)&fsverity_signed_digest, + sizeof(fsverity_signed_digest), &sig, &sig_len), + 0); + + if (use_signatures) { + fear.sig_size = sig_len; + fear.sig_ptr = ptr_to_u64(sig); + } else { + fear.sig_size = 0; + fear.sig_ptr = 0; + } + TESTEQUAL(ioctl(fd, FS_IOC_ENABLE_VERITY, &fear), 0); + + result = TEST_SUCCESS; +out: + free(sig); + close(fd); + free(filename); + return result; +} + +static int verity_test_optional_sigs(const char *mount_dir, bool use_signatures) +{ + int result = TEST_FAILURE; + char *backing_dir = NULL; + int cmd_fd = -1; + int i; + struct test_files_set test = get_test_files_set(); + const int file_num = test.files_count; + EVP_PKEY *key = NULL; + X509 *cert = NULL; + BIO *mem = NULL; + long len; + void *ptr; + FILE *proc_key_fd = NULL; + char *line = NULL; + size_t read = 0; + int key_id = -1; + + TEST(backing_dir = create_backing_dir(mount_dir), backing_dir); + TESTEQUAL(mount_fs_opt(mount_dir, backing_dir, "readahead=0", false), + 0); + TEST(cmd_fd = open_commands_file(mount_dir), cmd_fd != -1); + TEST(key = create_key(), key); + TEST(cert = get_cert(key), cert); + + TEST(proc_key_fd = fopen("/proc/keys", "r"), proc_key_fd != NULL); + while (getline(&line, &read, proc_key_fd) != -1) + if (strstr(line, ".fs-verity")) + key_id = strtol(line, NULL, 16); + + TEST(mem = BIO_new(BIO_s_mem()), mem != NULL); + TESTEQUAL(i2d_X509_bio(mem, cert), 1); + TEST(len = BIO_get_mem_data(mem, &ptr), len != 0); + TESTCOND(key_id == -1 + || syscall(__NR_add_key, "asymmetric", "test:key", ptr, len, + key_id) != -1); + + for (i = 0; i < file_num; i++) { + struct test_file *file = &test.files[i]; + + build_mtree(file); + TESTEQUAL(crypto_emit_file(cmd_fd, NULL, file->name, &file->id, + file->size, file->root_hash, + file->sig.add_data), 0); + + TESTEQUAL(emit_partial_test_file_hash(mount_dir, file), 0); + } + + for (i = 0; i < file_num; i++) + TESTEQUAL(validate_verity(mount_dir, &test.files[i], key, cert, + use_signatures), + 0); + + result = TEST_SUCCESS; + +out: + free(line); + BIO_free(mem); + X509_free(cert); + EVP_PKEY_free(key); + fclose(proc_key_fd); + close(cmd_fd); + umount(mount_dir); + free(backing_dir); + return result; +} + +static int verity_test(const char *mount_dir) +{ + int result = TEST_FAILURE; + + TESTEQUAL(verity_test_optional_sigs(mount_dir, true), TEST_SUCCESS); + TESTEQUAL(verity_test_optional_sigs(mount_dir, false), TEST_SUCCESS); + result = TEST_SUCCESS; +out: + return result; +} + static char *setup_mount_dir() { struct stat st; @@ -3734,6 +4015,7 @@ int main(int argc, char *argv[]) MAKE_TEST(hash_block_count_test), MAKE_TEST(per_uid_read_timeouts_test), MAKE_TEST(inotify_test), + MAKE_TEST(verity_test), }; #undef MAKE_TEST