dm-verify
Block devices' integrity is important because it holds bootloader, system files, etc., and heavily used in Android and Linux kernel.
Let's play with it.
Create a virtual block device
upgautam@amd:~/Desktop$ dd if=/dev/zero of=~/Desktop/verity_test.img bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB, 100 MiB) copied, 0.00858481 s, 12.2 GB/s
upgautam@amd:~/Desktop$ ls
DummyApp filtering pack.txt 'QEMU modification' verity_test.img
Explanation:
of: output file
if: input file
/dev/zero is sepcial input file that has 0x00 (all zero bytes)
bs: block size
Create a loopback device
upgautam@amd:~/Desktop$ sudo losetup /dev/loop133 ~/Desktop/verity_test.img
You can find all used loopback devices using losetup -a command, and don't use those. For example, in my case loop133 is not being used.
Create a filesystem for the device
upgautam@amd:~/Desktop$ sudo mkfs.ext4 /dev/loop133
mke2fs 1.47.0 (5-Feb-2023)
Discarding device blocks: done
Creating filesystem with 25600 4k blocks and 25600 inodes
Allocating group tables: done
Writing inode tables: done
Creating journal (1024 blocks): done
Writing superblocks and filesystem accounting information: done
Mount the device
sudo mount /dev/loop133 ~/Desktop/mount_point_loop_dm_verity
Now, we repeat most of the steps above to create and store hash tree.
upgautam@amd:~/Desktop$ dd if=/dev/zero of=~/Desktop/verity_hash.img bs=1M count=10
10+0 records in
10+0 records out
10485760 bytes (10 MB, 10 MiB) copied, 0.00141643 s, 7.4 GB/s
upgautam@amd:~/Desktop$ sudo losetup /dev/loop135 ~/Desktop/verity_hash.img
If not already installed, install the required tools.
sudo apt install cryptsetup-bin
Kernel config,
upgautam@amd:~/Desktop$ grep DM_VERITY /boot/config-$(uname -r)
CONFIG_DM_VERITY=m
CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y
CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG_SECONDARY_KEYRING=y
# CONFIG_DM_VERITY_FEC is not set
sudo modprobe dm-verity
Now, we can format those devices and then create vroot using the same hash value that we got during format.
upgautam@amd:~/Desktop$ sudo veritysetup format /dev/loop133 /dev/loop135
VERITY header information for /dev/loop135
UUID: 7a5d78ac-caad-4ce7-b0a4-ca1b2c9b77d4
Hash type: 1
Data blocks: 25600
Data block size: 4096
Hash blocks: 203
Hash block size: 4096
Hash algorithm: sha256
Salt: bd25e6c7da025b83ae5e1cf9750228a7e3f01649e8454adedd9e32d9fac7cadd
Root hash: 3de39aa81b1c32508dd559ae7e3d1fab35d86f2daa03854ffcdf644ba3fd438a
Hash device size: 835584 [bytes]
upgautam@amd:~/Desktop$ sudo veritysetup create vroot /dev/loop133 /dev/loop135 3de39aa81b1c32508dd559ae7e3d1fab35d86f2daa03854ffcdf644ba3fd438a
upgautam@amd:~/Desktop$ sudo dmsetup status
vroot: 0 204800 verity V
The command sudo veritysetup create vroot /dev/loop133 /dev/loop135 creates a vroot block device that maps the data device (/dev/loop133) and its hash device (/dev/loop135). You can then mount it with sudo mount /dev/mapper/vroot /mnt. Because dm-verity exposes a read-only verified device, writes to /dev/mapper/vroot are rejected. The goal is to ensure that only valid data (data matching the hash tree) is readable. Any corruption or tampering is detected when blocks are read. In other words, vroot is a read-only verified view of /dev/loop133. You can mount loop135 to inspect hashes and write test data to loop133; when that data is later read from /mnt through vroot, dm-verity checks the hashes on demand.
When a block is accessed from /dev/mapper/vroot (which is the verity device created using veritysetup), the kernel performs a hash check on the corresponding block in the data device (e.g., /dev/loop133). This is all on-demand.
Some troubleshootings
upgautam@amd:~/Desktop$ sudo umount /dev/loop133
upgautam@amd:~/Desktop$ sudo veritysetup close vroot
upgautam@amd:~/Desktop$ sudo fsck /dev/loop135
fsck from util-linux 2.39.3
upgautam@amd:~/Desktop$ sudo fsck /dev/loop133
fsck from util-linux 2.39.3
e2fsck 1.47.0 (5-Feb-2023)
/dev/loop133: clean, 16/25600 files, 2653/25600 blocks
upgautam@amd:~/Desktop$ sudo umount /dev/mapper/vroot
umount: /mnt: target is busy.
upgautam@amd:~/Desktop$ sudo veritysetup close vroot
Device vroot is still in use.
upgautam@amd:~/Desktop$ sudo lsof /dev/mapper/vroot
lsof: WARNING: can't stat() fuse.portal file system /run/user/1000/doc
Output information may be incomplete.
lsof: WARNING: can't stat() fuse.gvfsd-fuse file system /run/user/1000/gvfs
Output information may be incomplete.
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nautilus 33410 upgautam 46r DIR 252,0 4096 2 /mnt
upgautam@amd:~/Desktop$ sudo lsof /mnt
lsof: WARNING: can't stat() fuse.portal file system /run/user/1000/doc
Output information may be incomplete.
lsof: WARNING: can't stat() fuse.gvfsd-fuse file system /run/user/1000/gvfs
Output information may be incomplete.
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nautilus 33410 upgautam 46r DIR 252,0 4096 2 /mnt
upgautam@amd:~/Desktop$ sudo kill -9 33410
upgautam@amd:~/Desktop$ sudo veritysetup close vroot
Device vroot is still in use.
upgautam@amd:~/Desktop$ sudo umount /dev/mapper/vroot
upgautam@amd:~/Desktop$ sudo veritysetup close vroot
Reflection
Any two .img files (e.g., boot.img, userdata.img etc.) can be verified for their data integrity via dm-verify. In Android system, it is implemented by many vendors including Samsung. You will see dm-verify options in Recovery mode.