Linux fsverity_file_open fs-verity Merkle树校验

发布时间:2026/6/15 7:08:07
Linux fsverity_file_open fs-verity Merkle树校验 Linux fsverity_file_open fs-verity Merkle树校验fs-verity是Linux内核的只读文件完整性保护机制基于Merkle树实现逐块哈希验证。核心入口是fsverity_file_open在文件打开时验证完整性元数据并初始化验证上下文。整体框架位于fs/verity/目录由open.c、verify.c、measure.c、enable.c等文件组成。fsverity_file_open是文件打开路径的钩子在__fput之前调用int fsverity_file_open(struct inode *inode, struct file *file){struct fsverity_info *vi;int err;vi fsverity_get_info(inode);if (!vi)return 0;err fsverity_check_digest(inode, vi);if (err)return err;file-f_mode | FMODE_VERITY;return 0;}fsverity_get_info从inode中获取或构造fsverity_info结构体包含Merkle树的根哈希和测量数据struct fsverity_info *fsverity_get_info(const struct inode *inode){struct fsverity_info *vi;if (inode-i_verity_info)return inode-i_verity_info;vi fsverity_create_info(inode);if (IS_ERR(vi))return NULL;inode-i_verity_info vi;return vi;}fsverity_create_info解析verity元数据存储在文件的扩展属性或特殊块中并构建Merkle树验证上下文struct fsverity_info *fsverity_create_info(const struct inode *inode){struct fsverity_info *vi;struct fsverity_descriptor *desc;int ret;vi kzalloc(sizeof(*vi), GFP_KERNEL);if (!vi)return ERR_PTR(-ENOMEM);desc kzalloc(sizeof(*desc) FS_VERITY_MAX_LEVEL_SIZE, GFP_KERNEL);if (!desc) {kfree(vi);return ERR_PTR(-ENOMEM);}ret inode-i_sb-s_vop-get_verity_descriptor(inode, desc, sizeof(*desc));if (ret 0) {kfree(desc);kfree(vi);return ERR_PTR(ret);}vi-tree_params.hash_algorithm desc-hash_algorithm;vi-tree_params.block_size le32_to_cpu(desc-log_blocksize) ?: PAGE_SIZE;vi-data_params fsverity_init_hash_tree_params(desc, 0);vi-tree_params fsverity_init_hash_tree_params(desc, FS_VERITY_HASH_TREE);vi-level_params fsverity_init_level_params(desc);memcpy(vi-root_hash, desc-root_hash, desc-hash_algorithm-digest_size);kfree(desc);return vi;}Merkle树验证发生在每次page fault时的数据读取路径。核心函数是fsverity_verify_blocks它验证一个或多个数据块bool fsverity_verify_blocks(struct inode *inode, struct page **pages, unsigned int count){struct fsverity_info *vi inode-i_verity_info;unsigned int level;int err;for (unsigned int i 0; i count; i) {struct page *page pages[i];unsigned long offset page-index PAGE_SHIFT;struct ahash_request *req;SHA256_CTX ctx;err fsverity_verify_level(vi, offset, vi-level_params,level, vi-root_hash);if (err)return false;req ahash_request_alloc(vi-tree_params.hash_alg-tfm, GFP_NOFS);ahash_request_set_crypt(req, NULL, vi-measurement, 0);crypto_ahash_init(req);crypto_ahash_update(req, page, PAGE_SIZE);crypto_ahash_final(req);if (crypto_memneq(req-result, vi-measurement, vi-digest_size))return false;ahash_request_free(req);}return true;}实际实现中fsverity_verify_level从叶节点开始逐层向上验证直到根节点。每个Merkle树节点的哈希验证函数static int fsverity_verify_level(struct inode *inode, unsigned long offset,struct merkle_tree_params *params,unsigned int *level, const u8 *root_hash){struct fsverity_blockbuf block;struct fsverity_hash_alg *alg params-hash_alg;u8 digest[FS_VERITY_MAX_DIGEST_SIZE];int err;for (*level params-num_levels; *level 0; (*level)--) {unsigned long hash_offset fsverity_hash_level_offset(params, *level, offset);err params-hash_blocks[*level - 1]-read(inode, hash_offset, block);if (err)return err;if (*level params-num_levels)fsverity_compute_hash(alg, block.kaddr, block.len, digest);elsefsverity_compute_hash(alg, block.kaddr, block.len, digest);if (crypto_memneq(digest, root_hash, alg-digest_size))return -EBADMSG;}return 0;}fs-verity的哈希树参数定义struct merkle_tree_params {struct fsverity_hash_alg *hash_alg;unsigned int block_size;unsigned int log_blocksize;unsigned int num_levels;struct fsverity_blockbuf *hash_blocks;unsigned int hashes_per_block;};Merkle树的层级计算公式每个数据块计算一个哈希值多个哈希值组成上一级的哈希块。层级数由文件大小和块大小决定static unsigned int fsverity_compute_num_levels(unsigned long data_size,unsigned int block_size){unsigned int num_levels 0;unsigned long blocks data_size / block_size;while (blocks 1) {blocks DIV_ROUND_UP(blocks, block_size / sizeof(struct fsverity_hash));num_levels;}return num_levels;}文件打开时的最终校验fsverity_check_digest将文件测量值与用户指定的期望值比较static int fsverity_check_digest(const struct inode *inode,struct fsverity_info *vi){struct fsverity_digest *expected fsverity_get_digest(inode);int ret;if (!expected)return 0;if (expected-digest_algorithm ! vi-tree_params.hash_algorithm) {ret -EINVAL;goto out;}if (crypto_memneq(vi-root_hash, expected-digest,vi-tree_params.hash_algorithm-digest_size))ret -EIO;elseret 0;out:kfree(expected);return ret;}fs-verity与IMAIntegrity Measurement Architecture的区别fs-verity专注于文件内容的运行时验证基于Merkle树实现O(1)的增量验证成本每个读操作只验证访问的数据块路径而非整个文件。DM-verity类似但工作在块设备层而fs-verity工作在文件系统层支持ext4和f2fs。

月新闻