read_metadata.c (4919B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Ioctl to read verity metadata 4 * 5 * Copyright 2021 Google LLC 6 */ 7 8#include "fsverity_private.h" 9 10#include <linux/backing-dev.h> 11#include <linux/highmem.h> 12#include <linux/sched/signal.h> 13#include <linux/uaccess.h> 14 15static int fsverity_read_merkle_tree(struct inode *inode, 16 const struct fsverity_info *vi, 17 void __user *buf, u64 offset, int length) 18{ 19 const struct fsverity_operations *vops = inode->i_sb->s_vop; 20 u64 end_offset; 21 unsigned int offs_in_page; 22 pgoff_t index, last_index; 23 int retval = 0; 24 int err = 0; 25 26 end_offset = min(offset + length, vi->tree_params.tree_size); 27 if (offset >= end_offset) 28 return 0; 29 offs_in_page = offset_in_page(offset); 30 last_index = (end_offset - 1) >> PAGE_SHIFT; 31 32 /* 33 * Iterate through each Merkle tree page in the requested range and copy 34 * the requested portion to userspace. Note that the Merkle tree block 35 * size isn't important here, as we are returning a byte stream; i.e., 36 * we can just work with pages even if the tree block size != PAGE_SIZE. 37 */ 38 for (index = offset >> PAGE_SHIFT; index <= last_index; index++) { 39 unsigned long num_ra_pages = 40 min_t(unsigned long, last_index - index + 1, 41 inode->i_sb->s_bdi->io_pages); 42 unsigned int bytes_to_copy = min_t(u64, end_offset - offset, 43 PAGE_SIZE - offs_in_page); 44 struct page *page; 45 const void *virt; 46 47 page = vops->read_merkle_tree_page(inode, index, num_ra_pages); 48 if (IS_ERR(page)) { 49 err = PTR_ERR(page); 50 fsverity_err(inode, 51 "Error %d reading Merkle tree page %lu", 52 err, index); 53 break; 54 } 55 56 virt = kmap(page); 57 if (copy_to_user(buf, virt + offs_in_page, bytes_to_copy)) { 58 kunmap(page); 59 put_page(page); 60 err = -EFAULT; 61 break; 62 } 63 kunmap(page); 64 put_page(page); 65 66 retval += bytes_to_copy; 67 buf += bytes_to_copy; 68 offset += bytes_to_copy; 69 70 if (fatal_signal_pending(current)) { 71 err = -EINTR; 72 break; 73 } 74 cond_resched(); 75 offs_in_page = 0; 76 } 77 return retval ? retval : err; 78} 79 80/* Copy the requested portion of the buffer to userspace. */ 81static int fsverity_read_buffer(void __user *dst, u64 offset, int length, 82 const void *src, size_t src_length) 83{ 84 if (offset >= src_length) 85 return 0; 86 src += offset; 87 src_length -= offset; 88 89 length = min_t(size_t, length, src_length); 90 91 if (copy_to_user(dst, src, length)) 92 return -EFAULT; 93 94 return length; 95} 96 97static int fsverity_read_descriptor(struct inode *inode, 98 void __user *buf, u64 offset, int length) 99{ 100 struct fsverity_descriptor *desc; 101 size_t desc_size; 102 int res; 103 104 res = fsverity_get_descriptor(inode, &desc); 105 if (res) 106 return res; 107 108 /* don't include the signature */ 109 desc_size = offsetof(struct fsverity_descriptor, signature); 110 desc->sig_size = 0; 111 112 res = fsverity_read_buffer(buf, offset, length, desc, desc_size); 113 114 kfree(desc); 115 return res; 116} 117 118static int fsverity_read_signature(struct inode *inode, 119 void __user *buf, u64 offset, int length) 120{ 121 struct fsverity_descriptor *desc; 122 int res; 123 124 res = fsverity_get_descriptor(inode, &desc); 125 if (res) 126 return res; 127 128 if (desc->sig_size == 0) { 129 res = -ENODATA; 130 goto out; 131 } 132 133 /* 134 * Include only the signature. Note that fsverity_get_descriptor() 135 * already verified that sig_size is in-bounds. 136 */ 137 res = fsverity_read_buffer(buf, offset, length, desc->signature, 138 le32_to_cpu(desc->sig_size)); 139out: 140 kfree(desc); 141 return res; 142} 143 144/** 145 * fsverity_ioctl_read_metadata() - read verity metadata from a file 146 * @filp: file to read the metadata from 147 * @uarg: user pointer to fsverity_read_metadata_arg 148 * 149 * Return: length read on success, 0 on EOF, -errno on failure 150 */ 151int fsverity_ioctl_read_metadata(struct file *filp, const void __user *uarg) 152{ 153 struct inode *inode = file_inode(filp); 154 const struct fsverity_info *vi; 155 struct fsverity_read_metadata_arg arg; 156 int length; 157 void __user *buf; 158 159 vi = fsverity_get_info(inode); 160 if (!vi) 161 return -ENODATA; /* not a verity file */ 162 /* 163 * Note that we don't have to explicitly check that the file is open for 164 * reading, since verity files can only be opened for reading. 165 */ 166 167 if (copy_from_user(&arg, uarg, sizeof(arg))) 168 return -EFAULT; 169 170 if (arg.__reserved) 171 return -EINVAL; 172 173 /* offset + length must not overflow. */ 174 if (arg.offset + arg.length < arg.offset) 175 return -EINVAL; 176 177 /* Ensure that the return value will fit in INT_MAX. */ 178 length = min_t(u64, arg.length, INT_MAX); 179 180 buf = u64_to_user_ptr(arg.buf_ptr); 181 182 switch (arg.metadata_type) { 183 case FS_VERITY_METADATA_TYPE_MERKLE_TREE: 184 return fsverity_read_merkle_tree(inode, vi, buf, arg.offset, 185 length); 186 case FS_VERITY_METADATA_TYPE_DESCRIPTOR: 187 return fsverity_read_descriptor(inode, buf, arg.offset, length); 188 case FS_VERITY_METADATA_TYPE_SIGNATURE: 189 return fsverity_read_signature(inode, buf, arg.offset, length); 190 default: 191 return -EINVAL; 192 } 193} 194EXPORT_SYMBOL_GPL(fsverity_ioctl_read_metadata);