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