初始化dm的log:
[ 3.579718] md: Waiting for all devices to be available before autodetect
[ 3.586549] md: If you don't use raid, use raid=noautodetect
[ 3.594550] md: Autodetecting RAID arrays.
[ 3.598761] md: autorun ...
[ 3.601609] md: ... autorun DONE.
[ 3.604970] device-mapper: init: attempting early device configuration.
[ 3.614256] device-mapper: init: adding target '0 5255168 verity 1 /dev/mmcblk0p9 /dev/mmcblk0p9 4096 4096 656896 656896 sha256 0f8f00f5bf0797addffd383983ec81f1293ee90a0c0afb02adb2fbcdfff93ffd f4b71f5526c591e1d9fbc943b7e503846b4724a9ebf6d57820c2caf3cc631585 10 restart_on_corruption ignore_zero_blocks use_fec_from_device /dev/mmcblk0p9 fec_roots 2 fec_blocks 662070 fec_start 662070'
具体的调用函数栈如下:
系统中的初始化函数是
int verity_ctr(),调用堆栈如下所示:
[ 3.648189] CPU: 3 PID: 1 Comm: swapper/0 Not tainted 4.14.61 #1
[ 3.654207] Hardware name: Semidrive v9 REF Board (DT)
[ 3.659353] Call trace:
[ 3.661815] [
[ 3.667224] [
[ 3.672287] [
[ 3.677350] [
[ 3.682499] [
[ 3.688518] [
[ 3.693927] [
[ 3.699682] [
[ 3.705787] [
[ 3.711022] [
[ 3.727681] device-mapper: init: dm-0 is ready
根据系统传入的参数,初始化dm-verity的功能,参数的含义如下:
1)Verity target version(verity target 版本号)
版本号:1
2)Data block device(存储实际待校验数据的块设备)
存储数据的块设备:/dev/mmcblk0p9
3)Hash block device(存储校验使用到hash的块设备,一般情况跟data block device是同一个)
存储校验hash的块设备:/dev/mmcblk0p9
4)Data block size(数据块设备的每块存储size)
4096,即为4k
5)Hash block size(hash块设备的每块存储size)
4096, 即为4k
6)Num data block(数据块设备占用的块数量)
数据块数量656896, 即0-656895
7)Hash start block(hash设备在存储设备的起始位置)
656896
8)Hash algo(hash算法)
Sha256
9)root digest(对应上面说的 root hash)
0f8f00f5bf0797addffd383983ec81f1293ee90a0c0afb02adb2fbcdfff93ffd
10)Salt(用于计算hash的盐值)
f4b71f5526c591e1d9fbc943b7e503846b4724a9ebf6d57820c2caf3cc631585
在内核中加入的打印:
printk("v->hash_start = %d\n", v->hash_start);
printk("v->data_start = %d\n", v->data_start);
printk("v->data_blocks = %d\n", v->data_blocks);
printk("v->hash_start =%d\n", v->hash_start);
printk("v->hash_blocks = %d\n",v->hash_blocks);
printk("v->data_dev_block_bits=%d\n", v->data_dev_block_bits);
printk("v->hash_dev_block_bits=%d\n", v->hash_dev_block_bits);
printk("v->hash_per_block_bits=%d\n", v->hash_per_block_bits);
printk("v->levels=%d\n", v->levels);
printk("v->version =%d\n",v->version);
printk("v->digest_size=%d\n",v->digest_size);
[ 3.954883] hash level block[2]= 656896
[ 3.958759] hash level block[1]= 656897
[ 3.962625] hash level block[0]= 656938
[ 3.796016] v->hash_start = 656896
[ 3.799467] v->data_start = 0
[ 3.802485] v->data_blocks = 656896
[ 3.806021] v->hash_start =656896
[ 3.809367] v->hash_blocks = 662070 (662070-656896=5174)
[ 3.812900] v->data_dev_block_bits=12
[ 3.816608] v->hash_dev_block_bits=12
[ 3.820279] v->hash_per_block_bits=7 (4096/32 = 128, 指一个4K块上有128个哈希值)
[ 3.823888] v->levels=3
[ 3.826380] v->version =1
[ 3.829033] v->digest_size=32
数据块有656896X4X1024 (超过了2G)
Level 0 : 656896/128 = 5132个4k块
Level 1 : 5132/128= 41个4k块
Level 2: 40 /128 = 0,即1个4k块
共 5132+41+1 = 5174 个块
verity_map()中打包bio, 注册回调函数bio->bi_end_io = verity_end_io;
然后向驱动层发出请求: generic_make_request(bio);在文件系统总读完数据后,会有执行注册的回调函数,在回调函数Verity_end_io中会验证hash;
1.2.1申请文件系统访问,及注册回调函数
访问文件系统总的函数调用堆栈如下所示:
[ 17.596778] [
[ 17.606073] CPU: 0 PID: 1 Comm: systemd Not tainted 4.14.61 #1
[ 17.611963] Hardware name: Semidrive v9 REF Board (DT)
[ 17.617110] Call trace:
[ 17.619570] [
[ 17.624979] [
[ 17.630043] [
[ 17.635107] [
[ 17.640257] [
[ 17.645318] [
[ 17.652203] [
[ 17.658567] [
[ 17.664151] [
[ 17.670168] [
[ 17.675318] [
[ 17.680813] [
[ 17.685963] [
[ 17.691720] [
[ 17.697389] [
[ 17.702626] [
[ 17.707861] [
[ 17.713443] [
[ 17.718852] [
[ 17.724173] [
[ 17.729408] [
[ 17.734730] [
1.2.2 dm-verity回调函数的验证hash的过程
上图中,是判断数据块是否被验证过了,下文中会提到hash树的块被验证过了,在缓存中保留着。
打印出当前出错块的
1.want_digest,保存在hash树中的该验证块的hash值;
2.real_digest,根据实际读到的块中的数据,计算出来的hash值;
3.打印出数据块;
4.用dd命令直接把某块数据拿出来,比对3中的数据块是否正确;
根据log分析,打印出来的数据,与用dd命令直接取出来的数据块数据是一样的,可以证明送入hash计算的原始数据是正确的,不存在内存踩踏。
所以有可能是hash运算中本身出现了问题。
char *bin2hex(char *dst, const void *src, size_t count)
dd if=文件系统路径 of=输出路径 bs=4096 count=1 skip=块号
例如:dd if=/dev/sda1 of=/root/out.txt bs=4096 count=1 skip=6752256
/root/out.txt文件内容就是从块号中读到的内容 bs是块大小(这里为4K) count表示连续读几块 skip是从第几块开始读
dd if=/dev/mmcblk0p9 of=/media/user/ddtest.bin bs=4096 count=1 skip=657824