更改版本字符串后内核崩溃

时间:2018-07-16 05:31:35

标签: c linux assembly linux-kernel

由于工作需要,我将内核版本字符串及其长度从64个字符扩展到了128个字符,以添加构建日期,提交信息和一些其他对我们工作信息很重要的信息。内核版本为3.4.112,仅对设备进行了一些修改。此后,在内核崩溃的引导结束时,内核无法加载:

[   38.181594] IP-Config: Complete:
[   38.184853]      device=eth0, addr=192.168.7.2, mask=255.255.248.0, gw=255.255.255.255
[   38.192849]      host=192.168.7.2, domain=, nis-domain=(none)
[   38.198636]      bootserver=192.168.3.12, rootserver=192.168.3.12, rootpath=
[   38.206047] 
[   38.207554] initramfs file system not in use. details in main.c
[   38.215749] VFS: Mounted root (ext2 filesystem) readonly on device 31:2.
[   38.227813] Freeing init memory: 140K
[   38.370775] mv643xx_eth_port mv643xx_eth_port.0: eth0: link up, 1000 Mb/s, full duplex, flow control disabled
[   38.429568] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
[   38.429581] 
[   38.438796] [<c000dfc4>] (unwind_backtrace+0x0/0x108) from [<c03412cc>] (panic+0x9c/0x1f8)
[   38.447126] [<c03412cc>] (panic+0x9c/0x1f8) from [<c005f940>] (do_exit+0x7ec/0x818)
[   38.454836] [<c005f940>] (do_exit+0x7ec/0x818) from [<c005f9b4>] (do_group_exit+0x48/0xd8)
[   38.463161] [<c005f9b4>] (do_group_exit+0x48/0xd8) from [<c006c4b0>] (get_signal_to_deliver+0x344/0x5d4)
[   38.472705] [<c006c4b0>] (get_signal_to_deliver+0x344/0x5d4) from [<c000b154>] (do_notify_resume+0x88/0x520)
[   38.482599] [<c000b154>] (do_notify_resume+0x88/0x520) from [<c0009014>] (work_pending+0x24/0x28)

我在以下位置更改了内核源代码:

--- a/apps/linux-3.4/src/Makefile
+++ b/apps/linux-3.4/src/Makefile
@@ -995,7 +995,7 @@ prepare: prepare0
 # KERNELRELEASE can change from a few different places, meaning version.h
 # needs to be updated, so this check is forced on all builds

-uts_len := 64
+uts_len := 128
 define filechk_utsrelease.h
        if [ `echo -n "$(KERNELRELEASE)" | wc -c ` -gt $(uts_len) ]; then \
          echo '"$(KERNELRELEASE)" exceeds $(uts_len) characters' >&2;    \

--- a/apps/linux-3.4/src/include/linux/utsname.h
+++ b/apps/linux-3.4/src/include/linux/utsname.h
@@ -11,14 +11,15 @@ struct oldold_utsname {
        char machine[9];
 };

+#define __OLD_UTS_LEN 128
-#define __NEW_UTS_LEN 64
+#define __NEW_UTS_LEN 128

 struct old_utsname {
-       char sysname[65];
-       char nodename[65];
-       char release[65];
-       char version[65];
-       char machine[65];
+       char sysname[__OLD_UTS_LEN + 1];
+       char nodename[__OLD_UTS_LEN + 1];
+       char release[__OLD_UTS_LEN + 1];
+       char version[__OLD_UTS_LEN + 1];
+       char machine[__OLD_UTS_LEN + 1];
 };

--- a/apps/linux-3.4/src/kernel/sys.c
+++ b/apps/linux-3.4/src/kernel/sys.c
@@ -1210,7 +1210,7 @@ static int override_release(char __user *release, size_t len)

        if (current->personality & UNAME26) {
                const char *rest = UTS_RELEASE;
-               char buf[65] = { 0 };
+               char buf[__NEW_UTS_LEN + 1] = { 0 };
                int ndots = 0;
                unsigned v;
                size_t copy;

通过printk调试,我建立了引起内核恐慌的代码-它是arch/arm/kernel/sys_arm.c中的ASM代码:

asm(    "add    r0, %0, %1\n\t"
    "mov    r1, %2\n\t"
    "mov    r2, %3\n\t"
    "bl memmove\n\t"    /* copy regs to top of stack */
    "mov    r8, #0\n\t" /* not a syscall */
    "mov    r9, %0\n\t" /* thread structure */
    "mov    sp, r0\n\t" /* reposition stack pointer */
    "b  ret_to_user"
    :
    : "r" (current_thread_info()),
      "Ir" (THREAD_START_SP - sizeof(regs)),
      "r" (&regs),
      "Ir" (sizeof(regs))
    : "r0", "r1", "r2", "r3", "r8", "r9", "ip", "lr", "memory");

我对ASM和内核编程不是很熟悉,因此非常感谢您的帮助。

1 个答案:

答案 0 :(得分:3)

您通过中断init系统调用的内核/用户ABI(通过更改结构大小/布局)来进行uname(2)段错误。

其退出代码0xb为11,这是SIGSEGV的信号编号。 Attempted to kill init!是引起恐慌的原因,而不是症状或副作用。

@RossRidge说in a comment

  

_UTSNAME_LENGTH的1024数字仅用于uname的存根实现,用于不支持此系统调用的系统。您需要使用修改后的Linux内核标头重建glibc,然后使用此自定义版本的glibc重建正在使用的init。

Dmitry确认_UTSNAME_LENGTH从65更改为129
glibc-2.11.3/sysdeps/unix/sysv/linux/bits/utsname.h
在重建根fs之后解决了该问题。

一种比较宽松的通用解决方案是针对更新后的内核标头真正重建glibc。