1419b0af8Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2419b0af8Sopenharmony_ci/*
3419b0af8Sopenharmony_ci * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
4419b0af8Sopenharmony_ci */
5419b0af8Sopenharmony_ci#include <linux/string.h>
6419b0af8Sopenharmony_ci#include <linux/fcntl.h>
7419b0af8Sopenharmony_ci#include <linux/fs.h>
8419b0af8Sopenharmony_ci#include <linux/slab.h>
9419b0af8Sopenharmony_ci#include <linux/rwlock_types.h>
10419b0af8Sopenharmony_ci#include <linux/rwlock.h>
11419b0af8Sopenharmony_ci#include <linux/init.h>
12419b0af8Sopenharmony_ci#include <linux/moduleparam.h>
13419b0af8Sopenharmony_ci#include <linux/device-mapper.h>
14419b0af8Sopenharmony_ci#include <linux/kdev_t.h>
15419b0af8Sopenharmony_ci#include <linux/namei.h>
16419b0af8Sopenharmony_ci#include <linux/sched.h>
17419b0af8Sopenharmony_ci#include "mount.h"
18419b0af8Sopenharmony_ci#include "internal.h"
19419b0af8Sopenharmony_ci#include "exec_signature_info.h"
20419b0af8Sopenharmony_ci#include "xpm_report.h"
21419b0af8Sopenharmony_ci#include "xpm_log.h"
22419b0af8Sopenharmony_ci#include "code_sign_elf.h"
23419b0af8Sopenharmony_ci
24419b0af8Sopenharmony_ci#define VERITY_NODE_CACHE_LIMITS       10000
25419b0af8Sopenharmony_ci#define VERITY_NODE_CACHE_RECYCLE_NUM  200
26419b0af8Sopenharmony_ci
27419b0af8Sopenharmony_cistatic DEFINE_RWLOCK(dm_verity_tree_lock);
28419b0af8Sopenharmony_cistatic struct rb_root dm_verity_tree = RB_ROOT;
29419b0af8Sopenharmony_cistatic int dm_verity_node_count;
30419b0af8Sopenharmony_cistatic DEFINE_RWLOCK(fs_verity_tree_lock);
31419b0af8Sopenharmony_cistatic struct rb_root fs_verity_tree = RB_ROOT;
32419b0af8Sopenharmony_cistatic int fs_verity_node_count;
33419b0af8Sopenharmony_ci
34419b0af8Sopenharmony_cistruct verity_info {
35419b0af8Sopenharmony_ci	struct rb_root *root;
36419b0af8Sopenharmony_ci	rwlock_t *lock;
37419b0af8Sopenharmony_ci	int *node_count;
38419b0af8Sopenharmony_ci};
39419b0af8Sopenharmony_ci
40419b0af8Sopenharmony_cistatic bool dm_verity_enable_check;
41419b0af8Sopenharmony_ci
42419b0af8Sopenharmony_ci#define DM_PARTITION_PATH_MAX	30
43419b0af8Sopenharmony_ci#define DM_VERITY_INVALID_DEV	((dev_t)-1)
44419b0af8Sopenharmony_ci
45419b0af8Sopenharmony_cistruct dm_partition {
46419b0af8Sopenharmony_ci	char path[DM_PARTITION_PATH_MAX];
47419b0af8Sopenharmony_ci	int len;
48419b0af8Sopenharmony_ci	dev_t s_dev;
49419b0af8Sopenharmony_ci};
50419b0af8Sopenharmony_ci
51419b0af8Sopenharmony_cistatic struct dm_partition	dm_partition_table[] = {
52419b0af8Sopenharmony_ci	{ .path = "/",           .len = 1,  .s_dev = DM_VERITY_INVALID_DEV },
53419b0af8Sopenharmony_ci	{ .path = "/cust/",      .len = 6,  .s_dev = DM_VERITY_INVALID_DEV },
54419b0af8Sopenharmony_ci	{ .path = "/system/",    .len = 8,  .s_dev = DM_VERITY_INVALID_DEV },
55419b0af8Sopenharmony_ci	{ .path = "/vendor/",    .len = 8,  .s_dev = DM_VERITY_INVALID_DEV },
56419b0af8Sopenharmony_ci	{ .path = "/version/",   .len = 9,  .s_dev = DM_VERITY_INVALID_DEV },
57419b0af8Sopenharmony_ci	{ .path = "/preload/",   .len = 9,  .s_dev = DM_VERITY_INVALID_DEV },
58419b0af8Sopenharmony_ci	{ .path = "/sys_prod/",  .len = 10, .s_dev = DM_VERITY_INVALID_DEV },
59419b0af8Sopenharmony_ci	{ .path = "/chip_prod/", .len = 11, .s_dev = DM_VERITY_INVALID_DEV },
60419b0af8Sopenharmony_ci	{ .path = "/module_updata/",             .len = 15, .s_dev = DM_VERITY_INVALID_DEV },
61419b0af8Sopenharmony_ci	{ .path = "/vendor/modem/modem_vendor/", .len = 27, .s_dev = DM_VERITY_INVALID_DEV },
62419b0af8Sopenharmony_ci	{ .path = "/vendor/modem/modem_driver/", .len = 27, .s_dev = DM_VERITY_INVALID_DEV },
63419b0af8Sopenharmony_ci	{ .path = "/misc/",      .len = 6,  .s_dev = DM_VERITY_INVALID_DEV },
64419b0af8Sopenharmony_ci};
65419b0af8Sopenharmony_ci
66419b0af8Sopenharmony_cistatic struct path	root_path;
67419b0af8Sopenharmony_ci
68419b0af8Sopenharmony_cistatic dev_t get_file_dev(struct file *file)
69419b0af8Sopenharmony_ci{
70419b0af8Sopenharmony_ci	struct super_block *sb;
71419b0af8Sopenharmony_ci	struct mount *mnt;
72419b0af8Sopenharmony_ci
73419b0af8Sopenharmony_ci	if (file->f_path.mnt == NULL)
74419b0af8Sopenharmony_ci		return DM_VERITY_INVALID_DEV;
75419b0af8Sopenharmony_ci
76419b0af8Sopenharmony_ci	mnt = container_of(file->f_path.mnt, struct mount, mnt);
77419b0af8Sopenharmony_ci	sb = mnt->mnt.mnt_sb;
78419b0af8Sopenharmony_ci	if (sb == NULL)
79419b0af8Sopenharmony_ci		return DM_VERITY_INVALID_DEV;
80419b0af8Sopenharmony_ci
81419b0af8Sopenharmony_ci	return sb->s_dev;
82419b0af8Sopenharmony_ci}
83419b0af8Sopenharmony_ci
84419b0af8Sopenharmony_cistatic dev_t get_partition_dev_form_mnt(const struct vfsmount *mnt)
85419b0af8Sopenharmony_ci{
86419b0af8Sopenharmony_ci	if (IS_ERR_OR_NULL(mnt) || IS_ERR_OR_NULL(mnt->mnt_sb))
87419b0af8Sopenharmony_ci		return DM_VERITY_INVALID_DEV;
88419b0af8Sopenharmony_ci	return mnt->mnt_sb->s_dev;
89419b0af8Sopenharmony_ci}
90419b0af8Sopenharmony_ci
91419b0af8Sopenharmony_cistatic dev_t get_root_partition_dev(struct path *root_path)
92419b0af8Sopenharmony_ci{
93419b0af8Sopenharmony_ci	int ret;
94419b0af8Sopenharmony_ci	struct path path;
95419b0af8Sopenharmony_ci	struct path *mount_path = &path;
96419b0af8Sopenharmony_ci	dev_t s_dev;
97419b0af8Sopenharmony_ci
98419b0af8Sopenharmony_ci	if (root_path != NULL)
99419b0af8Sopenharmony_ci		mount_path = root_path;
100419b0af8Sopenharmony_ci	ret = kern_path("/", LOOKUP_DIRECTORY, mount_path);
101419b0af8Sopenharmony_ci	if (ret) {
102419b0af8Sopenharmony_ci		xpm_log_error("get / path failed");
103419b0af8Sopenharmony_ci		return DM_VERITY_INVALID_DEV;
104419b0af8Sopenharmony_ci	}
105419b0af8Sopenharmony_ci	s_dev = get_partition_dev_form_mnt(mount_path->mnt);
106419b0af8Sopenharmony_ci	if (s_dev == DM_VERITY_INVALID_DEV || root_path == NULL)
107419b0af8Sopenharmony_ci		path_put(mount_path);
108419b0af8Sopenharmony_ci	xpm_log_info("get / dev=%u:%u success", s_dev, MINOR(s_dev));
109419b0af8Sopenharmony_ci	return s_dev;
110419b0af8Sopenharmony_ci}
111419b0af8Sopenharmony_ci
112419b0af8Sopenharmony_cistatic dev_t get_dm_verity_partition_dev(const char *dir, struct path *root_path)
113419b0af8Sopenharmony_ci{
114419b0af8Sopenharmony_ci	int ret;
115419b0af8Sopenharmony_ci	struct path path;
116419b0af8Sopenharmony_ci	dev_t s_dev;
117419b0af8Sopenharmony_ci
118419b0af8Sopenharmony_ci	ret = vfs_path_lookup(root_path->dentry, root_path->mnt, dir, LOOKUP_DIRECTORY, &path);
119419b0af8Sopenharmony_ci	if (ret) {
120419b0af8Sopenharmony_ci		xpm_log_error("get %s path failed", dir);
121419b0af8Sopenharmony_ci		return DM_VERITY_INVALID_DEV;
122419b0af8Sopenharmony_ci	}
123419b0af8Sopenharmony_ci	s_dev = get_partition_dev_form_mnt(path.mnt);
124419b0af8Sopenharmony_ci	path_put(&path);
125419b0af8Sopenharmony_ci	xpm_log_info("get %s dev=%u:%u success", dir, s_dev, MINOR(s_dev));
126419b0af8Sopenharmony_ci	return s_dev;
127419b0af8Sopenharmony_ci}
128419b0af8Sopenharmony_ci
129419b0af8Sopenharmony_cistatic bool find_partition_dev(struct file *file, dev_t s_dev)
130419b0af8Sopenharmony_ci{
131419b0af8Sopenharmony_ci	char *full_path = NULL;
132419b0af8Sopenharmony_ci	char path[PATH_MAX] = {0};
133419b0af8Sopenharmony_ci	struct dm_partition *dm_path;
134419b0af8Sopenharmony_ci	int i;
135419b0af8Sopenharmony_ci
136419b0af8Sopenharmony_ci	for (i = 1; i < sizeof(dm_partition_table) / sizeof(struct dm_partition); i++) {
137419b0af8Sopenharmony_ci		dm_path = &dm_partition_table[i];
138419b0af8Sopenharmony_ci		if (dm_path->s_dev != DM_VERITY_INVALID_DEV)
139419b0af8Sopenharmony_ci			continue;
140419b0af8Sopenharmony_ci
141419b0af8Sopenharmony_ci		if (full_path == NULL) {
142419b0af8Sopenharmony_ci			full_path = file_path(file, path, PATH_MAX-1);
143419b0af8Sopenharmony_ci			if (IS_ERR(full_path))
144419b0af8Sopenharmony_ci				return false;
145419b0af8Sopenharmony_ci		}
146419b0af8Sopenharmony_ci		if (strncmp(dm_path->path, full_path, dm_path->len) != 0)
147419b0af8Sopenharmony_ci			continue;
148419b0af8Sopenharmony_ci
149419b0af8Sopenharmony_ci		dm_path->s_dev = get_dm_verity_partition_dev(dm_path->path, &root_path);
150419b0af8Sopenharmony_ci		if (dm_path->s_dev == DM_VERITY_INVALID_DEV)
151419b0af8Sopenharmony_ci			return false;
152419b0af8Sopenharmony_ci		if (dm_path->s_dev == s_dev)
153419b0af8Sopenharmony_ci			return true;
154419b0af8Sopenharmony_ci		return false;
155419b0af8Sopenharmony_ci	}
156419b0af8Sopenharmony_ci
157419b0af8Sopenharmony_ci	return false;
158419b0af8Sopenharmony_ci}
159419b0af8Sopenharmony_ci
160419b0af8Sopenharmony_cistatic bool dm_verity_check_for_path(struct file *file)
161419b0af8Sopenharmony_ci{
162419b0af8Sopenharmony_ci	static int system_startup_stage;
163419b0af8Sopenharmony_ci	struct dm_partition *dm_path;
164419b0af8Sopenharmony_ci	dev_t s_dev, root_dev;
165419b0af8Sopenharmony_ci	int i;
166419b0af8Sopenharmony_ci
167419b0af8Sopenharmony_ci	s_dev = get_file_dev(file);
168419b0af8Sopenharmony_ci	for (i = 0; i < sizeof(dm_partition_table) / sizeof(struct dm_partition); i++) {
169419b0af8Sopenharmony_ci		dm_path = &dm_partition_table[i];
170419b0af8Sopenharmony_ci		if (dm_path->s_dev == s_dev)
171419b0af8Sopenharmony_ci			return true;
172419b0af8Sopenharmony_ci	}
173419b0af8Sopenharmony_ci
174419b0af8Sopenharmony_ci	if (system_startup_stage)
175419b0af8Sopenharmony_ci		return find_partition_dev(file, s_dev);
176419b0af8Sopenharmony_ci
177419b0af8Sopenharmony_ci	if (task_pid_nr(current) == 1) {
178419b0af8Sopenharmony_ci		root_dev = get_root_partition_dev(NULL);
179419b0af8Sopenharmony_ci		if (root_dev != dm_partition_table[0].s_dev) {
180419b0af8Sopenharmony_ci			path_put(&root_path);
181419b0af8Sopenharmony_ci			dm_partition_table[0].s_dev = get_root_partition_dev(&root_path);
182419b0af8Sopenharmony_ci			for (i = 1; i < sizeof(dm_partition_table) / sizeof(struct dm_partition); i++)
183419b0af8Sopenharmony_ci				dm_partition_table[i].s_dev = DM_VERITY_INVALID_DEV;
184419b0af8Sopenharmony_ci			system_startup_stage = 1;
185419b0af8Sopenharmony_ci		}
186419b0af8Sopenharmony_ci	}
187419b0af8Sopenharmony_ci	return find_partition_dev(file, s_dev);
188419b0af8Sopenharmony_ci}
189419b0af8Sopenharmony_ci
190419b0af8Sopenharmony_ci#ifdef CONFIG_DM_VERITY
191419b0af8Sopenharmony_ci#define HVB_CMDLINE_VB_STATE	"ohos.boot.hvb.enable"
192419b0af8Sopenharmony_cistatic bool dm_verity_enable;
193419b0af8Sopenharmony_ci
194419b0af8Sopenharmony_cistatic int hvb_boot_param_cb(char *param, char *val,
195419b0af8Sopenharmony_ci	const char *unused, void *arg)
196419b0af8Sopenharmony_ci{
197419b0af8Sopenharmony_ci	if (param == NULL || val == NULL)
198419b0af8Sopenharmony_ci		return 0;
199419b0af8Sopenharmony_ci
200419b0af8Sopenharmony_ci	if (strcmp(param, HVB_CMDLINE_VB_STATE) != 0)
201419b0af8Sopenharmony_ci		return 0;
202419b0af8Sopenharmony_ci
203419b0af8Sopenharmony_ci	if (strcmp(val, "true") == 0 || strcmp(val, "green") == 0)
204419b0af8Sopenharmony_ci		dm_verity_enable = true;
205419b0af8Sopenharmony_ci
206419b0af8Sopenharmony_ci	return 0;
207419b0af8Sopenharmony_ci}
208419b0af8Sopenharmony_ci
209419b0af8Sopenharmony_cistatic bool dm_verity_is_enable(void)
210419b0af8Sopenharmony_ci{
211419b0af8Sopenharmony_ci	char *cmdline;
212419b0af8Sopenharmony_ci
213419b0af8Sopenharmony_ci	if (dm_verity_enable || dm_verity_enable_check)
214419b0af8Sopenharmony_ci		return dm_verity_enable;
215419b0af8Sopenharmony_ci
216419b0af8Sopenharmony_ci	cmdline = kstrdup(saved_command_line, GFP_KERNEL);
217419b0af8Sopenharmony_ci	if (cmdline == NULL)
218419b0af8Sopenharmony_ci		return false;
219419b0af8Sopenharmony_ci
220419b0af8Sopenharmony_ci	parse_args("hvb.enable params", cmdline, NULL,
221419b0af8Sopenharmony_ci		0, 0, 0, NULL, &hvb_boot_param_cb);
222419b0af8Sopenharmony_ci	kfree(cmdline);
223419b0af8Sopenharmony_ci	dm_verity_enable_check = true;
224419b0af8Sopenharmony_ci	if (!dm_verity_enable) {
225419b0af8Sopenharmony_ci		dm_partition_table[0].s_dev = get_root_partition_dev(&root_path);
226419b0af8Sopenharmony_ci		report_init_event(DM_DISABLE);
227419b0af8Sopenharmony_ci	}
228419b0af8Sopenharmony_ci	return dm_verity_enable;
229419b0af8Sopenharmony_ci}
230419b0af8Sopenharmony_ci
231419b0af8Sopenharmony_cistatic bool dm_verity_check_for_mnt(struct file *file)
232419b0af8Sopenharmony_ci{
233419b0af8Sopenharmony_ci	struct mapped_device *device;
234419b0af8Sopenharmony_ci
235419b0af8Sopenharmony_ci	device = dm_get_md(get_file_dev(file));
236419b0af8Sopenharmony_ci	if (device == NULL)
237419b0af8Sopenharmony_ci		return false;
238419b0af8Sopenharmony_ci
239419b0af8Sopenharmony_ci	dm_put(device);
240419b0af8Sopenharmony_ci	return true;
241419b0af8Sopenharmony_ci}
242419b0af8Sopenharmony_ci#endif
243419b0af8Sopenharmony_ci
244419b0af8Sopenharmony_cistatic bool is_dm_verity(struct file *file)
245419b0af8Sopenharmony_ci{
246419b0af8Sopenharmony_ci#ifdef CONFIG_DM_VERITY
247419b0af8Sopenharmony_ci	if (dm_verity_is_enable())
248419b0af8Sopenharmony_ci		return dm_verity_check_for_mnt(file);
249419b0af8Sopenharmony_ci#endif
250419b0af8Sopenharmony_ci
251419b0af8Sopenharmony_ci	if (!dm_verity_enable_check) {
252419b0af8Sopenharmony_ci		dm_partition_table[0].s_dev = get_root_partition_dev(&root_path);
253419b0af8Sopenharmony_ci		dm_verity_enable_check = true;
254419b0af8Sopenharmony_ci		report_init_event(DM_DISABLE);
255419b0af8Sopenharmony_ci	}
256419b0af8Sopenharmony_ci	return dm_verity_check_for_path(file);
257419b0af8Sopenharmony_ci}
258419b0af8Sopenharmony_ci
259419b0af8Sopenharmony_ci#ifdef CONFIG_FS_VERITY
260419b0af8Sopenharmony_cistatic bool is_fs_verity(struct file *file)
261419b0af8Sopenharmony_ci{
262419b0af8Sopenharmony_ci	struct inode *file_node;
263419b0af8Sopenharmony_ci
264419b0af8Sopenharmony_ci	file_node = file_inode(file);
265419b0af8Sopenharmony_ci	if (file_node == NULL)
266419b0af8Sopenharmony_ci		return false;
267419b0af8Sopenharmony_ci
268419b0af8Sopenharmony_ci	if (file_node->i_verity_info == NULL)
269419b0af8Sopenharmony_ci		return false;
270419b0af8Sopenharmony_ci
271419b0af8Sopenharmony_ci	return true;
272419b0af8Sopenharmony_ci}
273419b0af8Sopenharmony_ci#endif
274419b0af8Sopenharmony_ci
275419b0af8Sopenharmony_cistatic int check_exec_file_is_verity(struct file *file, bool is_exec)
276419b0af8Sopenharmony_ci{
277419b0af8Sopenharmony_ci#ifdef CONFIG_FS_VERITY
278419b0af8Sopenharmony_ci	if (is_fs_verity(file))
279419b0af8Sopenharmony_ci		return FILE_SIGNATURE_FS_VERITY;
280419b0af8Sopenharmony_ci#endif
281419b0af8Sopenharmony_ci
282419b0af8Sopenharmony_ci	if (is_dm_verity(file))
283419b0af8Sopenharmony_ci		return FILE_SIGNATURE_DM_VERITY;
284419b0af8Sopenharmony_ci
285419b0af8Sopenharmony_ci#ifdef CONFIG_SECURITY_CODE_SIGN
286419b0af8Sopenharmony_ci	if (is_exec && !elf_file_enable_fs_verity(file))
287419b0af8Sopenharmony_ci		return FILE_SIGNATURE_FS_VERITY;
288419b0af8Sopenharmony_ci#endif
289419b0af8Sopenharmony_ci
290419b0af8Sopenharmony_ci	return FILE_SIGNATURE_INVALID;
291419b0af8Sopenharmony_ci}
292419b0af8Sopenharmony_ci
293419b0af8Sopenharmony_cistatic struct exec_file_signature_info *rb_search_node(struct rb_root *root, uintptr_t file_inode)
294419b0af8Sopenharmony_ci{
295419b0af8Sopenharmony_ci	struct rb_node *node = root->rb_node;
296419b0af8Sopenharmony_ci	struct exec_file_signature_info *file_node;
297419b0af8Sopenharmony_ci
298419b0af8Sopenharmony_ci	while (node != NULL) {
299419b0af8Sopenharmony_ci		file_node = rb_entry(node, struct exec_file_signature_info, rb_node);
300419b0af8Sopenharmony_ci		if (file_inode < file_node->inode) {
301419b0af8Sopenharmony_ci			node = file_node->rb_node.rb_left;
302419b0af8Sopenharmony_ci		} else if (file_inode > file_node->inode) {
303419b0af8Sopenharmony_ci			node = file_node->rb_node.rb_right;
304419b0af8Sopenharmony_ci		} else {
305419b0af8Sopenharmony_ci			atomic_inc(&file_node->reference);
306419b0af8Sopenharmony_ci			return file_node;
307419b0af8Sopenharmony_ci		}
308419b0af8Sopenharmony_ci	}
309419b0af8Sopenharmony_ci	return NULL;
310419b0af8Sopenharmony_ci}
311419b0af8Sopenharmony_ci
312419b0af8Sopenharmony_cistatic struct exec_file_signature_info *rb_add_node(struct rb_root *root, int *node_count,
313419b0af8Sopenharmony_ci	struct exec_file_signature_info *node)
314419b0af8Sopenharmony_ci{
315419b0af8Sopenharmony_ci	struct rb_node **p = &root->rb_node;
316419b0af8Sopenharmony_ci	struct rb_node *parent = NULL;
317419b0af8Sopenharmony_ci	struct exec_file_signature_info *file;
318419b0af8Sopenharmony_ci
319419b0af8Sopenharmony_ci	while (*p != NULL) {
320419b0af8Sopenharmony_ci		parent = *p;
321419b0af8Sopenharmony_ci		file = rb_entry(parent, struct exec_file_signature_info, rb_node);
322419b0af8Sopenharmony_ci		if (node->inode < file->inode) {
323419b0af8Sopenharmony_ci			p = &(*p)->rb_left;
324419b0af8Sopenharmony_ci		} else if (node->inode > file->inode) {
325419b0af8Sopenharmony_ci			p = &(*p)->rb_right;
326419b0af8Sopenharmony_ci		} else {
327419b0af8Sopenharmony_ci			atomic_inc(&file->reference);
328419b0af8Sopenharmony_ci			return file;
329419b0af8Sopenharmony_ci		}
330419b0af8Sopenharmony_ci	}
331419b0af8Sopenharmony_ci
332419b0af8Sopenharmony_ci	rb_link_node(&node->rb_node, parent, p);
333419b0af8Sopenharmony_ci	rb_insert_color(&node->rb_node, root);
334419b0af8Sopenharmony_ci	atomic_inc(&node->reference);
335419b0af8Sopenharmony_ci	(*node_count)++;
336419b0af8Sopenharmony_ci	return NULL;
337419b0af8Sopenharmony_ci}
338419b0af8Sopenharmony_ci
339419b0af8Sopenharmony_cistatic void rb_erase_node(struct rb_root *root, int *node_count,
340419b0af8Sopenharmony_ci	struct exec_file_signature_info *node)
341419b0af8Sopenharmony_ci{
342419b0af8Sopenharmony_ci	rb_erase(&node->rb_node, root);
343419b0af8Sopenharmony_ci	(*node_count)--;
344419b0af8Sopenharmony_ci}
345419b0af8Sopenharmony_ci
346419b0af8Sopenharmony_cistatic int find_idle_nodes(struct rb_root *root, uintptr_t *ilde_nodes, int count)
347419b0af8Sopenharmony_ci{
348419b0af8Sopenharmony_ci	int i = 0;
349419b0af8Sopenharmony_ci	struct exec_file_signature_info *code_segment;
350419b0af8Sopenharmony_ci	struct rb_node *node;
351419b0af8Sopenharmony_ci
352419b0af8Sopenharmony_ci	for (node = rb_first(root); node != NULL && i < count; node = rb_next(node)) {
353419b0af8Sopenharmony_ci		code_segment = rb_entry(node, struct exec_file_signature_info, rb_node);
354419b0af8Sopenharmony_ci		if (atomic_read(&code_segment->reference) > 0)
355419b0af8Sopenharmony_ci			continue;
356419b0af8Sopenharmony_ci
357419b0af8Sopenharmony_ci		ilde_nodes[i++] = (uintptr_t)code_segment;
358419b0af8Sopenharmony_ci	}
359419b0af8Sopenharmony_ci	return i;
360419b0af8Sopenharmony_ci}
361419b0af8Sopenharmony_ci
362419b0af8Sopenharmony_cistatic void clear_code_segment_info_cache(struct rb_root *root, int *node_count)
363419b0af8Sopenharmony_ci{
364419b0af8Sopenharmony_ci	struct exec_file_signature_info *code_segment_info;
365419b0af8Sopenharmony_ci	uintptr_t *code_segments;
366419b0af8Sopenharmony_ci	int i = 0;
367419b0af8Sopenharmony_ci	int count = VERITY_NODE_CACHE_RECYCLE_NUM;
368419b0af8Sopenharmony_ci
369419b0af8Sopenharmony_ci	code_segments = kcalloc(count, sizeof(uintptr_t), GFP_KERNEL);
370419b0af8Sopenharmony_ci	if (code_segments == NULL)
371419b0af8Sopenharmony_ci		return;
372419b0af8Sopenharmony_ci
373419b0af8Sopenharmony_ci	count = find_idle_nodes(root, code_segments, count);
374419b0af8Sopenharmony_ci	while (i < count) {
375419b0af8Sopenharmony_ci		code_segment_info = (struct exec_file_signature_info *)code_segments[i];
376419b0af8Sopenharmony_ci		rb_erase_node(root, node_count, code_segment_info);
377419b0af8Sopenharmony_ci		kfree(code_segment_info);
378419b0af8Sopenharmony_ci		i++;
379419b0af8Sopenharmony_ci	}
380419b0af8Sopenharmony_ci	kfree(code_segments);
381419b0af8Sopenharmony_ci}
382419b0af8Sopenharmony_ci
383419b0af8Sopenharmony_ci#ifdef CONFIG_SECURITY_XPM_DEBUG
384419b0af8Sopenharmony_cistatic size_t test_elf_code_segment_info_size(struct rb_root *root)
385419b0af8Sopenharmony_ci{
386419b0af8Sopenharmony_ci	size_t size = 0;
387419b0af8Sopenharmony_ci	struct exec_file_signature_info *file_node;
388419b0af8Sopenharmony_ci	struct rb_node *node;
389419b0af8Sopenharmony_ci
390419b0af8Sopenharmony_ci	for (node = rb_first(root); node != NULL; node = rb_next(node)) {
391419b0af8Sopenharmony_ci		file_node = rb_entry(node, struct exec_file_signature_info, rb_node);
392419b0af8Sopenharmony_ci		size += sizeof(struct exec_file_signature_info) +
393419b0af8Sopenharmony_ci				file_node->code_segment_count * sizeof(struct exec_segment_info);
394419b0af8Sopenharmony_ci	}
395419b0af8Sopenharmony_ci	return size;
396419b0af8Sopenharmony_ci}
397419b0af8Sopenharmony_ci
398419b0af8Sopenharmony_cistatic void test_printf_code_segment_cache_size(void)
399419b0af8Sopenharmony_ci{
400419b0af8Sopenharmony_ci	size_t cache_size = 0;
401419b0af8Sopenharmony_ci	int dm_count;
402419b0af8Sopenharmony_ci	int fs_count;
403419b0af8Sopenharmony_ci
404419b0af8Sopenharmony_ci	read_lock(&dm_verity_tree_lock);
405419b0af8Sopenharmony_ci	cache_size += test_elf_code_segment_info_size(&dm_verity_tree);
406419b0af8Sopenharmony_ci	dm_count = dm_verity_node_count;
407419b0af8Sopenharmony_ci	read_unlock(&dm_verity_tree_lock);
408419b0af8Sopenharmony_ci
409419b0af8Sopenharmony_ci	read_lock(&fs_verity_tree_lock);
410419b0af8Sopenharmony_ci	cache_size += test_elf_code_segment_info_size(&fs_verity_tree);
411419b0af8Sopenharmony_ci	fs_count = fs_verity_node_count;
412419b0af8Sopenharmony_ci	read_unlock(&fs_verity_tree_lock);
413419b0af8Sopenharmony_ci
414419b0af8Sopenharmony_ci	xpm_log_info("cache dm count=%d, fs count=%d, cache size=%d KB\n", dm_count, fs_count, cache_size / 1024);
415419b0af8Sopenharmony_ci}
416419b0af8Sopenharmony_ci
417419b0af8Sopenharmony_cistatic void test_print_info(struct file *file, unsigned int type, const struct exec_file_signature_info *file_info)
418419b0af8Sopenharmony_ci{
419419b0af8Sopenharmony_ci	char *full_path;
420419b0af8Sopenharmony_ci	char path[PATH_MAX] = {0};
421419b0af8Sopenharmony_ci	static int code_segment_test_count = 100;
422419b0af8Sopenharmony_ci	int i;
423419b0af8Sopenharmony_ci
424419b0af8Sopenharmony_ci	code_segment_test_count--;
425419b0af8Sopenharmony_ci	if (code_segment_test_count > 0)
426419b0af8Sopenharmony_ci		return;
427419b0af8Sopenharmony_ci
428419b0af8Sopenharmony_ci	full_path = file_path(file, path, PATH_MAX - 1);
429419b0af8Sopenharmony_ci	if (IS_ERR(full_path))
430419b0af8Sopenharmony_ci		return;
431419b0af8Sopenharmony_ci
432419b0af8Sopenharmony_ci	for (i = 0; i < file_info->code_segment_count; i++)
433419b0af8Sopenharmony_ci		xpm_log_info("%s -> type: %s, info: offset=0x%llx size=0x%lx\n",
434419b0af8Sopenharmony_ci			full_path, type == FILE_SIGNATURE_DM_VERITY ? "dm" : "fs",
435419b0af8Sopenharmony_ci			file_info->code_segments->file_offset, file_info->code_segments->size);
436419b0af8Sopenharmony_ci
437419b0af8Sopenharmony_ci	code_segment_test_count = 100;
438419b0af8Sopenharmony_ci}
439419b0af8Sopenharmony_ci#endif
440419b0af8Sopenharmony_ci
441419b0af8Sopenharmony_cistatic void rm_code_segment_info(void)
442419b0af8Sopenharmony_ci{
443419b0af8Sopenharmony_ci	if (dm_verity_node_count + fs_verity_node_count < VERITY_NODE_CACHE_LIMITS)
444419b0af8Sopenharmony_ci		return;
445419b0af8Sopenharmony_ci
446419b0af8Sopenharmony_ci#ifdef CONFIG_SECURITY_XPM_DEBUG
447419b0af8Sopenharmony_ci	test_printf_code_segment_cache_size();
448419b0af8Sopenharmony_ci#endif
449419b0af8Sopenharmony_ci
450419b0af8Sopenharmony_ci	if (dm_verity_node_count > fs_verity_node_count) {
451419b0af8Sopenharmony_ci		write_lock(&dm_verity_tree_lock);
452419b0af8Sopenharmony_ci		clear_code_segment_info_cache(&dm_verity_tree, &dm_verity_node_count);
453419b0af8Sopenharmony_ci		write_unlock(&dm_verity_tree_lock);
454419b0af8Sopenharmony_ci		return;
455419b0af8Sopenharmony_ci	}
456419b0af8Sopenharmony_ci
457419b0af8Sopenharmony_ci	write_lock(&fs_verity_tree_lock);
458419b0af8Sopenharmony_ci	clear_code_segment_info_cache(&fs_verity_tree, &fs_verity_node_count);
459419b0af8Sopenharmony_ci	write_unlock(&fs_verity_tree_lock);
460419b0af8Sopenharmony_ci}
461419b0af8Sopenharmony_ci
462419b0af8Sopenharmony_cistatic int get_verity_info(int type, struct verity_info *verity)
463419b0af8Sopenharmony_ci{
464419b0af8Sopenharmony_ci	if (type == FILE_SIGNATURE_DM_VERITY) {
465419b0af8Sopenharmony_ci		verity->root = &dm_verity_tree;
466419b0af8Sopenharmony_ci		verity->lock = &dm_verity_tree_lock;
467419b0af8Sopenharmony_ci		verity->node_count = &dm_verity_node_count;
468419b0af8Sopenharmony_ci		return 0;
469419b0af8Sopenharmony_ci	}
470419b0af8Sopenharmony_ci
471419b0af8Sopenharmony_ci	if (type == FILE_SIGNATURE_FS_VERITY) {
472419b0af8Sopenharmony_ci		verity->lock = &fs_verity_tree_lock;
473419b0af8Sopenharmony_ci		verity->root = &fs_verity_tree;
474419b0af8Sopenharmony_ci		verity->node_count = &fs_verity_node_count;
475419b0af8Sopenharmony_ci		return 0;
476419b0af8Sopenharmony_ci	}
477419b0af8Sopenharmony_ci
478419b0af8Sopenharmony_ci	return -EINVAL;
479419b0af8Sopenharmony_ci}
480419b0af8Sopenharmony_ci
481419b0af8Sopenharmony_cistatic void insert_new_signature_info(struct inode *file_node, int type,
482419b0af8Sopenharmony_ci	struct verity_info *verity, struct exec_file_signature_info *new_info, struct exec_file_signature_info **old_info)
483419b0af8Sopenharmony_ci{
484419b0af8Sopenharmony_ci	new_info->type = (unsigned int)type;
485419b0af8Sopenharmony_ci	new_info->inode = (uintptr_t)file_node;
486419b0af8Sopenharmony_ci	RB_CLEAR_NODE(&new_info->rb_node);
487419b0af8Sopenharmony_ci	if ((*old_info) != NULL) {
488419b0af8Sopenharmony_ci		write_lock(verity->lock);
489419b0af8Sopenharmony_ci		if ((*old_info) != NULL) {
490419b0af8Sopenharmony_ci			if (atomic_sub_return(1, &(*old_info)->reference) <= 0) {
491419b0af8Sopenharmony_ci				rb_erase_node(verity->root, verity->node_count, *old_info);
492419b0af8Sopenharmony_ci				(*old_info)->type |= FILE_SIGNATURE_DELETE;
493419b0af8Sopenharmony_ci				kfree(*old_info);
494419b0af8Sopenharmony_ci				*old_info = NULL;
495419b0af8Sopenharmony_ci			}
496419b0af8Sopenharmony_ci		}
497419b0af8Sopenharmony_ci		write_unlock(verity->lock);
498419b0af8Sopenharmony_ci	}
499419b0af8Sopenharmony_ci
500419b0af8Sopenharmony_ci	write_lock(verity->lock);
501419b0af8Sopenharmony_ci	*old_info = rb_add_node(verity->root, verity->node_count, new_info);
502419b0af8Sopenharmony_ci	write_unlock(verity->lock);
503419b0af8Sopenharmony_ci}
504419b0af8Sopenharmony_ci
505419b0af8Sopenharmony_cistatic int get_elf_code_segment_info(struct file *file, bool is_exec, int type,
506419b0af8Sopenharmony_ci	struct exec_file_signature_info **code_segment_info)
507419b0af8Sopenharmony_ci{
508419b0af8Sopenharmony_ci	int ret;
509419b0af8Sopenharmony_ci	struct verity_info verity;
510419b0af8Sopenharmony_ci	struct inode *file_node;
511419b0af8Sopenharmony_ci	struct exec_file_signature_info *new_info;
512419b0af8Sopenharmony_ci	struct exec_file_signature_info *old_info;
513419b0af8Sopenharmony_ci
514419b0af8Sopenharmony_ci	if (get_verity_info(type, &verity) < 0)
515419b0af8Sopenharmony_ci		return -EINVAL;
516419b0af8Sopenharmony_ci
517419b0af8Sopenharmony_ci	file_node = file_inode(file);
518419b0af8Sopenharmony_ci	if (file_node == NULL)
519419b0af8Sopenharmony_ci		return -EINVAL;
520419b0af8Sopenharmony_ci
521419b0af8Sopenharmony_ci	read_lock(verity.lock);
522419b0af8Sopenharmony_ci	old_info = rb_search_node(verity.root, (uintptr_t)file_node);
523419b0af8Sopenharmony_ci	read_unlock(verity.lock);
524419b0af8Sopenharmony_ci	if (old_info != NULL) {
525419b0af8Sopenharmony_ci		if (is_exec && old_info->code_segments == NULL)
526419b0af8Sopenharmony_ci			goto need_parse;
527419b0af8Sopenharmony_ci
528419b0af8Sopenharmony_ci		*code_segment_info = old_info;
529419b0af8Sopenharmony_ci		return 0;
530419b0af8Sopenharmony_ci	}
531419b0af8Sopenharmony_ci
532419b0af8Sopenharmony_cineed_parse:
533419b0af8Sopenharmony_ci	rm_code_segment_info();
534419b0af8Sopenharmony_ci
535419b0af8Sopenharmony_ci	if (!is_exec) {
536419b0af8Sopenharmony_ci		new_info = kzalloc(sizeof(struct exec_file_signature_info), GFP_KERNEL);
537419b0af8Sopenharmony_ci		if (new_info == NULL)
538419b0af8Sopenharmony_ci			return -ENOMEM;
539419b0af8Sopenharmony_ci	} else {
540419b0af8Sopenharmony_ci		ret = parse_elf_code_segment_info(file, &new_info);
541419b0af8Sopenharmony_ci		if (ret < 0) {
542419b0af8Sopenharmony_ci			report_file_event(FORMAT_UNDEF, file);
543419b0af8Sopenharmony_ci			return ret;
544419b0af8Sopenharmony_ci		}
545419b0af8Sopenharmony_ci#ifdef CONFIG_SECURITY_XPM_DEBUG
546419b0af8Sopenharmony_ci		test_print_info(file, type, new_info);
547419b0af8Sopenharmony_ci#endif
548419b0af8Sopenharmony_ci	}
549419b0af8Sopenharmony_ci
550419b0af8Sopenharmony_ci	insert_new_signature_info(file_node, type, &verity, new_info, &old_info);
551419b0af8Sopenharmony_ci	if (old_info != NULL) {
552419b0af8Sopenharmony_ci		kfree(new_info);
553419b0af8Sopenharmony_ci		new_info = old_info;
554419b0af8Sopenharmony_ci	}
555419b0af8Sopenharmony_ci	*code_segment_info = new_info;
556419b0af8Sopenharmony_ci	return 0;
557419b0af8Sopenharmony_ci}
558419b0af8Sopenharmony_ci
559419b0af8Sopenharmony_ciint get_exec_file_signature_info(struct file *file, bool is_exec,
560419b0af8Sopenharmony_ci	struct exec_file_signature_info **info_ptr)
561419b0af8Sopenharmony_ci{
562419b0af8Sopenharmony_ci	int type;
563419b0af8Sopenharmony_ci
564419b0af8Sopenharmony_ci	if (file == NULL || info_ptr == NULL)
565419b0af8Sopenharmony_ci		return -EINVAL;
566419b0af8Sopenharmony_ci
567419b0af8Sopenharmony_ci	type = check_exec_file_is_verity(file, is_exec);
568419b0af8Sopenharmony_ci	return get_elf_code_segment_info(file, is_exec, type, info_ptr);
569419b0af8Sopenharmony_ci}
570419b0af8Sopenharmony_ci
571419b0af8Sopenharmony_ciint put_exec_file_signature_info(struct exec_file_signature_info *exec_info)
572419b0af8Sopenharmony_ci{
573419b0af8Sopenharmony_ci	if ((exec_info == NULL) ||
574419b0af8Sopenharmony_ci		!exec_file_signature_is_verity(exec_info))
575419b0af8Sopenharmony_ci		return -EINVAL;
576419b0af8Sopenharmony_ci
577419b0af8Sopenharmony_ci	if (atomic_sub_return(1, &exec_info->reference) <= 0 &&
578419b0af8Sopenharmony_ci		exec_file_signature_is_delete(exec_info))
579419b0af8Sopenharmony_ci		kfree(exec_info);
580419b0af8Sopenharmony_ci	return 0;
581419b0af8Sopenharmony_ci}
582419b0af8Sopenharmony_ci
583419b0af8Sopenharmony_cistatic struct exec_file_signature_info *elf_code_segment_info_delete(struct rb_root *root,
584419b0af8Sopenharmony_ci	int *node_count, struct inode *file_node)
585419b0af8Sopenharmony_ci{
586419b0af8Sopenharmony_ci	struct exec_file_signature_info *signature_info;
587419b0af8Sopenharmony_ci
588419b0af8Sopenharmony_ci	signature_info = rb_search_node(root, (uintptr_t)file_node);
589419b0af8Sopenharmony_ci	if (signature_info != NULL) {
590419b0af8Sopenharmony_ci		rb_erase_node(root, node_count, signature_info);
591419b0af8Sopenharmony_ci		if (atomic_sub_return(1, &signature_info->reference) > 0)
592419b0af8Sopenharmony_ci			signature_info->type |= FILE_SIGNATURE_DELETE;
593419b0af8Sopenharmony_ci		else
594419b0af8Sopenharmony_ci			kfree(signature_info);
595419b0af8Sopenharmony_ci	}
596419b0af8Sopenharmony_ci	return signature_info;
597419b0af8Sopenharmony_ci}
598419b0af8Sopenharmony_ci
599419b0af8Sopenharmony_civoid delete_exec_file_signature_info(struct inode *file_node)
600419b0af8Sopenharmony_ci{
601419b0af8Sopenharmony_ci	struct exec_file_signature_info *signature_info;
602419b0af8Sopenharmony_ci
603419b0af8Sopenharmony_ci	if (file_node == NULL)
604419b0af8Sopenharmony_ci		return;
605419b0af8Sopenharmony_ci
606419b0af8Sopenharmony_ci	write_lock(&fs_verity_tree_lock);
607419b0af8Sopenharmony_ci	signature_info = elf_code_segment_info_delete(&fs_verity_tree, &fs_verity_node_count, file_node);
608419b0af8Sopenharmony_ci	write_unlock(&fs_verity_tree_lock);
609419b0af8Sopenharmony_ci	if (signature_info != NULL)
610419b0af8Sopenharmony_ci		return;
611419b0af8Sopenharmony_ci
612419b0af8Sopenharmony_ci	write_lock(&dm_verity_tree_lock);
613419b0af8Sopenharmony_ci	signature_info = elf_code_segment_info_delete(&dm_verity_tree, &dm_verity_node_count, file_node);
614419b0af8Sopenharmony_ci	write_unlock(&dm_verity_tree_lock);
615419b0af8Sopenharmony_ci}
616