OpenHarmony 5.0.0Release sharefs mount报错问题分析
一、关键字: OpenHarmony 4.1Release 升级至 5.0.0Release ;sharefs ;死机重启 二、问题描述 设备型号:黄鹂 系统版本:OpenHarmony 5.0.0 代码版本:OpenHarmony-5.0.0 问题现象:将const.distributed_file_property.enabled 置为 false,重启设备后,不断死机重启,无法进入桌面。 三
一、关键字:
OpenHarmony 4.1Release 升级至 5.0.0Release ;sharefs ;死机重启
二、问题描述
设备型号:黄鹂
系统版本:OpenHarmony 5.0.0
代码版本:OpenHarmony-5.0.0
问题现象:将 const.distributed_file_property.enabled 置为 false,重启设备后,不断死机重启,无法进入桌面。
三、原因分析
3.1 正常机制
openharmony系统针对未适配hmdfs的设备提供了另一套文件管理系统的选择,即 local_mount+sharefs_mount 。
在将 const.distributed_file_property.enabled 置为 false,重启设备后,设备可以正常进入桌面并实现相关功能。
3.2 异常机制
将 const.distributed_file_property.enabled 置为 false,重启设备后,不断死机重启,无法进入桌面。
3.2.1分析报错日志
根据日志中的首行信息可知,崩溃原因是出现了空指针:
Unable to handle kernel NULL pointer dereference at virtual address 000000000000000a
调用堆栈及关键信息如下:
[ 15.310793][ T1485] pc : strchr+0x8/0x38
[ 15.310803][ T1485] lr : match_token+0x94/0x270
[ 15.327239][ T1485] Call trace:
[ 15.327241][ T1485] strchr+0x8/0x38
[ 15.327245][ T1485] sharefs_parse_options+0xc8/0x154
[ 15.327250][ T1485] sharefs_fill_super+0x11c/0x234
[ 15.337940][ T1485] mount_nodev+0x7c/0xf0
[ 15.337945][ T1485] sharefs_mount+0x4c/0x80
[ 15.337949][ T1485] legacy_get_tree+0x5c/0xc8
[ 15.337953][ T1485] vfs_get_tree+0x5c/0x134
[ 15.337955][ T1485] do_new_mount+0x164/0x380
[ 15.344516][ T1485] path_mount+0x288/0x508
[ 15.344519][ T1485] __arm64_sys_mount+0x204/0x530
[ 15.344521][ T1485] invoke_syscall+0x6c/0x15c
[ 15.344525][ T1485] el0_svc_common.llvm.16526140288913859248+0xd4/0x120
[ 15.344528][ T1485] do_el0_svc+0x34/0xac
[ 15.344531][ T1485] el0_svc+0x2c/0x94
[ 15.362689][ T1485] el0t_64_sync_handler+0x8c/0xf0
[ 15.362691][ T1485] el0t_64_sync+0x1b4/0x1b8
可见最终调用至 match_token+0x94 处时崩溃,通过反编译定位至110行。
3.2.2 添加判空及日志打印
根据前面的信息,常规思路是直接在 match_token 函数中添加对各入参的判空,如下所示:
重新编译后,系统仍无法开机,且无相关日志打印报错堆栈也未发生改变。此处笔者已有怀疑是发生了踩内存,但受限于工具和技术储备,并没有直接证据。
3.2.3 更深层调用分析
由于本身match_token()未定位到具体的空指针,因此分析 match_one()中的处理流程:
static int match_one(char *s, const char *p, substring_t args[])
{
char *meta;
int argc = 0;
if (!p)
return 1;
while(1) {
int len = -1;
meta = strchr(p, '%');
if (!meta)
return strcmp(p, s) == 0;
if (strncmp(p, s, meta-p))
return 0;
s += meta - p;
p = meta + 1;
if (isdigit(*p))
len = simple_strtoul(p, (char **) &p, 10);
else if (*p == '%') {
if (*s++ != '%')
return 0;
p++;
continue;
}
if (argc >= MAX_OPT_ARGS)
return 0;
args[argc].from = s;
switch (*p++) {
case 's': {
size_t str_len = strlen(s);
if (str_len == 0)
return 0;
if (len == -1 || len > str_len)
len = str_len;
args[argc].to = s + len;
break;
}
case 'd':
simple_strtol(s, &args[argc].to, 0);
goto num;
case 'u':
simple_strtoul(s, &args[argc].to, 0);
goto num;
case 'o':
simple_strtoul(s, &args[argc].to, 8);
goto num;
case 'x':
simple_strtoul(s, &args[argc].to, 16);
num:
if (args[argc].to == args[argc].from)
return 0;
break;
default:
return 0;
}
s = args[argc].to;
argc++;
}
}
可以看到比较有可能出现空指针情况的就是数组指针p,因此在该函数中加了大量的非空判断。
但此举并没有解决问题。且添加的error打印均未触发输出,崩溃堆栈的偏移量也没有变化。尝试在正常流程中打印无用信息来确认日志是否正常生成时,日志均未能输出。因此怀疑此段代码本身存在异常。
3.2.4 内核代码结构分析
在5.0.0解耦工作中,我们采用了两套工程分别编译的方案,用于区分开闭源代码,内核中的部分ko均在另一套工程中编译生成。
对比两套工程中的内核代码,发现内核代码本身存在较大差异,之前的修改均仅在开源工程中修改,尝试同步至闭源工程再执行测试动作。发现设备能够正常开机。
四、方案优化
此前,所有内核的修改均需要同时往开源工程和闭源工程中合入,将ko编译代码挑出来放在产品化路径下,此外,将闭源工程的内核代码直接指向开源工程(闭源工程与开源工程直接使用同一套内核代码),并合入sharefs的相关patch,可以最小化解决问题。
五、sharefs适配
若系统未曾适配hmdfs及sharefs,可参考下述pr进行适配:
https://gitee.com/openharmony/kernel_linux_6.6/pulls/6
https://gitee.com/openharmony/kernel_linux_6.6/pulls/62
更多推荐
所有评论(0)