Kernel panic after changing version string

1

Due to work needs I've expanded kernel version string and length of it from 64 to 128 characters to add build date, commit info and some other important for our work info. Kernel version is 3.4.112 with few device specific modifications. After that kernel fails to load at the end of booting with kernel panic:

[   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)

I've changed kernel source code in following places:

--- 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;    \

and

--- 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];
 };

and

--- 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;

With printk debugging I've founded code which causes kernel panic - it is piece of ASM code from arch/arm/kernel/sys_arm.c:

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");

I'm not very familiar with ASM and with kernel programming, so I 'll be very thankful for help.

c
linux
assembly
linux-kernel
asked on Stack Overflow Jul 16, 2018 by Dmitriy Vinokurov • edited Jul 17, 2018 by Dmitriy Vinokurov

1 Answer

3

You made init segfault by breaking the kernel / user ABI for the uname(2) system call (by changing the struct size/layout).

Its exit code of 0xb is 11, which is the signal number for SIGSEGV. Attempted to kill init! was the cause of the panic, not a symptom or side-effect.

As @RossRidge said in a comment:

The 1024 number for _UTSNAME_LENGTH is only used for the stub implementation of uname for systems that don't support this system call. You'll need to rebuild glibc using your modified Linux kernel headers and then rebuild the init you're using with this custom version of glibc.

Dmitry confirms that changing _UTSNAME_LENGTH from 65 to 129 in
glibc-2.11.3/sysdeps/unix/sysv/linux/bits/utsname.h
solved the problem, after rebuilding the root fs.

A less-hacky general solution is to really rebuild glibc against the updated kernel headers.

answered on Stack Overflow Jul 17, 2018 by Peter Cordes

User contributions licensed under CC BY-SA 3.0