烩:遇上 Assert、空指针异常
前段时间装 Gentoo 用 parted 分区时,执行“name 1 grub”时遇上 assert 报错:
一条经验:
遇到空指针、assert 错误、NullPointerException,诸如此类,均属意外错误。
这类错误太广,遇到后我建议先下载最新版本或者拉最新代码来编译,确认 bug 是否修复。如果没有修复,再用 Google 搜索,如能从 Google 搜索到邮件列表归档或者 issue 列表相应主题,说明有人提交 bug 了,可以持续关注处理进展。
前面两步都失败,就只有看源码了。
恰好这个 parted 的 bug 当时没有找到相关信息,最新源码中也存在问题。根据异常信息,定位到报错点:
PedPartition* ped_disk_get_partition (const PedDisk* disk, int num) { PedPartition* walk; PED_ASSERT (disk != NULL); // 触发点在这里 for (walk = disk->part_list; walk; walk = ped_disk_next_partition (disk, walk)) { if (walk->num == num && !(walk->type & PED_PARTITION_FREESPACE)) return walk; } return NULL; }
disk 参数指向分区信息,所以产生 bug 的原因是我忘记对新磁盘进行分区,这也是没认真看手册导致的——也说明不按流程来可能会意外收获漏洞或者 bug。
虽然 bug 已定位出来,但我还是给官方提交了一个 patch 过去,patch 很简单,如果 disk 为 NULL 就打印提示信息并 return:
--- a/libparted/disk.c +++ b/libparted/disk.c @@ -1594,7 +1594,10 @@ ped_disk_get_partition (const PedDisk* disk, int num) { PedPartition* walk; - PED_ASSERT (disk != NULL); + if (disk == NULL) { + fprintf(stderr, "you must specify partition."); + return NULL; + } for (walk = disk->part_list; walk; walk = ped_disk_next_partition (disk, walk)) {
但是我犯了一个错,提 patch 时没关注 parted 架构,parted 底层操作是调用 libparted 库完成的,disk.c 来自 libparted 目录。因此这里用 assert 本没有错,错的是 parted 本身没处理 NULL 的情况,而不是库的问题。
库函数不要屏蔽错误细节,把错误细节暴露给调用端。
所以开发者回复我:
The patch is not correct because as a library, libparted can not simply write directly to stderr; it must throw an exception instead. In all probability the bug lies in parted itself, which should not be calling ped_disk_get_partition on a NULL disk pointer. I'll work on a proper fix for this.
最后官方再次回复说已经有人修正了这个 bug:
It seems this was already fixed: commit f5c628dd51c7d77ff939554425159ab6e8aef1c0 Author: Brian C. Lane <[email protected]> Date: Mon Jul 13 16:43:11 2015 -0700 parted: Fix crash with name command and no disklabel (#1226067) A typo (the last I think) from commit 7eac058 wasn't properly checking the result of ped_disk_new so it could crash if there was no disklabel on the device.
剩下的就是等 Gentoo 更新,或者自己手动更新有 bug 的软件。