xref: /linux/tools/testing/selftests/bpf/progs/lsm_bdev.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
1*96f4c251SChristian Brauner // SPDX-License-Identifier: GPL-2.0
2*96f4c251SChristian Brauner /* Copyright (c) 2026 Christian Brauner <brauner@kernel.org> */
3*96f4c251SChristian Brauner 
4*96f4c251SChristian Brauner /*
5*96f4c251SChristian Brauner  * BPF LSM block device integrity tracker for dm-verity.
6*96f4c251SChristian Brauner  *
7*96f4c251SChristian Brauner  * Tracks block devices in a hashmap keyed by bd_dev.  When dm-verity
8*96f4c251SChristian Brauner  * calls security_bdev_setintegrity() during verity_preresume(), the
9*96f4c251SChristian Brauner  * setintegrity hook records the roothash and signature-validity data.
10*96f4c251SChristian Brauner  * The free hook cleans up when the device goes away.  The alloc hook
11*96f4c251SChristian Brauner  * counts allocations for test validation.
12*96f4c251SChristian Brauner  *
13*96f4c251SChristian Brauner  * The sleepable hooks exercise bpf_copy_from_user() to verify that
14*96f4c251SChristian Brauner  * the sleepable classification actually permits sleepable helpers.
15*96f4c251SChristian Brauner  */
16*96f4c251SChristian Brauner 
17*96f4c251SChristian Brauner #include "vmlinux.h"
18*96f4c251SChristian Brauner #include <bpf/bpf_helpers.h>
19*96f4c251SChristian Brauner #include <bpf/bpf_tracing.h>
20*96f4c251SChristian Brauner 
21*96f4c251SChristian Brauner struct verity_info {
22*96f4c251SChristian Brauner 	__u8  has_roothash;	/* LSM_INT_DMVERITY_ROOTHASH seen */
23*96f4c251SChristian Brauner 	__u8  sig_valid;	/* LSM_INT_DMVERITY_SIG_VALID value (non-NULL = valid) */
24*96f4c251SChristian Brauner 	__u32 setintegrity_cnt;	/* total setintegrity calls for this dev */
25*96f4c251SChristian Brauner };
26*96f4c251SChristian Brauner 
27*96f4c251SChristian Brauner struct {
28*96f4c251SChristian Brauner 	__uint(type, BPF_MAP_TYPE_HASH);
29*96f4c251SChristian Brauner 	__uint(max_entries, 64);
30*96f4c251SChristian Brauner 	__type(key, __u32);		/* dev_t from bdev->bd_dev */
31*96f4c251SChristian Brauner 	__type(value, struct verity_info);
32*96f4c251SChristian Brauner } verity_devices SEC(".maps");
33*96f4c251SChristian Brauner 
34*96f4c251SChristian Brauner /* Global counters exposed to userspace via skeleton bss. */
35*96f4c251SChristian Brauner int alloc_count;
36*96f4c251SChristian Brauner 
37*96f4c251SChristian Brauner char _license[] SEC("license") = "GPL";
38*96f4c251SChristian Brauner 
39*96f4c251SChristian Brauner SEC("lsm.s/bdev_setintegrity")
40*96f4c251SChristian Brauner int BPF_PROG(bdev_setintegrity, struct block_device *bdev,
41*96f4c251SChristian Brauner 	     enum lsm_integrity_type type, const void *value, size_t size)
42*96f4c251SChristian Brauner {
43*96f4c251SChristian Brauner 	struct verity_info zero = {};
44*96f4c251SChristian Brauner 	struct verity_info *info;
45*96f4c251SChristian Brauner 	__u32 dev;
46*96f4c251SChristian Brauner 	char buf;
47*96f4c251SChristian Brauner 
48*96f4c251SChristian Brauner 	/*
49*96f4c251SChristian Brauner 	 * Exercise a sleepable helper to confirm the verifier
50*96f4c251SChristian Brauner 	 * allows it in this sleepable hook.
51*96f4c251SChristian Brauner 	 */
52*96f4c251SChristian Brauner 	(void)bpf_copy_from_user(&buf, sizeof(buf), NULL);
53*96f4c251SChristian Brauner 
54*96f4c251SChristian Brauner 	dev = bdev->bd_dev;
55*96f4c251SChristian Brauner 
56*96f4c251SChristian Brauner 	info = bpf_map_lookup_elem(&verity_devices, &dev);
57*96f4c251SChristian Brauner 	if (!info) {
58*96f4c251SChristian Brauner 		bpf_map_update_elem(&verity_devices, &dev, &zero, BPF_NOEXIST);
59*96f4c251SChristian Brauner 		info = bpf_map_lookup_elem(&verity_devices, &dev);
60*96f4c251SChristian Brauner 		if (!info)
61*96f4c251SChristian Brauner 			return 0;
62*96f4c251SChristian Brauner 	}
63*96f4c251SChristian Brauner 
64*96f4c251SChristian Brauner 	if (type == LSM_INT_DMVERITY_ROOTHASH)
65*96f4c251SChristian Brauner 		info->has_roothash = 1;
66*96f4c251SChristian Brauner 	else if (type == LSM_INT_DMVERITY_SIG_VALID)
67*96f4c251SChristian Brauner 		info->sig_valid = (value != NULL);
68*96f4c251SChristian Brauner 
69*96f4c251SChristian Brauner 	__sync_fetch_and_add(&info->setintegrity_cnt, 1);
70*96f4c251SChristian Brauner 
71*96f4c251SChristian Brauner 	return 0;
72*96f4c251SChristian Brauner }
73*96f4c251SChristian Brauner 
74*96f4c251SChristian Brauner SEC("lsm/bdev_free_security")
75*96f4c251SChristian Brauner void BPF_PROG(bdev_free_security, struct block_device *bdev)
76*96f4c251SChristian Brauner {
77*96f4c251SChristian Brauner 	__u32 dev = bdev->bd_dev;
78*96f4c251SChristian Brauner 
79*96f4c251SChristian Brauner 	bpf_map_delete_elem(&verity_devices, &dev);
80*96f4c251SChristian Brauner }
81*96f4c251SChristian Brauner 
82*96f4c251SChristian Brauner SEC("lsm.s/bdev_alloc_security")
83*96f4c251SChristian Brauner int BPF_PROG(bdev_alloc_security, struct block_device *bdev)
84*96f4c251SChristian Brauner {
85*96f4c251SChristian Brauner 	char buf;
86*96f4c251SChristian Brauner 
87*96f4c251SChristian Brauner 	/*
88*96f4c251SChristian Brauner 	 * Exercise a sleepable helper to confirm the verifier
89*96f4c251SChristian Brauner 	 * allows it in this sleepable hook.
90*96f4c251SChristian Brauner 	 */
91*96f4c251SChristian Brauner 	(void)bpf_copy_from_user(&buf, sizeof(buf), NULL);
92*96f4c251SChristian Brauner 
93*96f4c251SChristian Brauner 	__sync_fetch_and_add(&alloc_count, 1);
94*96f4c251SChristian Brauner 
95*96f4c251SChristian Brauner 	return 0;
96*96f4c251SChristian Brauner }
97