Summary: | net-fs/nfs-utils: rpc.mountd segfaults when built as x32 | ||
---|---|---|---|
Product: | Gentoo Linux | Reporter: | SpanKY <vapier> |
Component: | Current packages | Assignee: | Network Filesystems <net-fs> |
Status: | RESOLVED FIXED | ||
Severity: | normal | CC: | bugzilla-gentoo, timmy |
Priority: | Normal | ||
Version: | unspecified | ||
Hardware: | All | ||
OS: | Linux | ||
Whiteboard: | |||
Package list: | Runtime testing required: | --- | |
Bug Depends on: | |||
Bug Blocks: | 393673 |
Description
SpanKY
2011-12-09 19:07:26 UTC
This is a bug in strtok_r(). Specifically, strtok_r is causing stack corruption and is squishing a stored register. Starting in getnetconfig() from libtirpc.so, parse_ncp() on line 302 corrupts ncp: # gdb -q `which rpc.mountd` Reading symbols from /usr/sbin/rpc.mountd...done. (gdb) b getnetconfig Breakpoint 1 at 0x402570 (gdb) run Starting program: /usr/sbin/rpc.mountd warning: Could not load shared library symbols for linux-vdso.so.1. Do you need "set solib-search-path" or "set sysroot"? [Thread debugging using libthread_db enabled] Using host libthread_db library "/libx32/libthread_db.so.1". Breakpoint 1, getnetconfig (handlep=handlep@entry=0x653030) at getnetconfig.c:219 219 { (gdb) b 302 Breakpoint 2, getnetconfig (handlep=handlep@entry=0x653030) at getnetconfig.c:302 302 if (parse_ncp(stringp, list->ncp) == -1) { (gdb) p ncp $1 = (struct netconfig_vars *) 0x653030 (gdb) n 300 list->ncp->nc_lookups = NULL; (gdb) 302 if (parse_ncp(stringp, list->ncp) == -1) { (gdb) 298 list->ncp = np; (gdb) 302 if (parse_ncp(stringp, list->ncp) == -1) { (gdb) 315 if (ni.head == NULL) { /* first entry */ (gdb) p ncp $2 = (struct netconfig_vars *) 0x0 This causes our segfault a few lines later as we deference ncp: (gdb) c Continuing. Program received signal SIGSEGV, Segmentation fault. getnetconfig (handlep=handlep@entry=0x653030) at getnetconfig.c:322 322 ncp->nc_configs = ni.tail; (gdb) disass $rip,$rip+0x10 Dump of assembler code from 0xf7598288 to 0xf7598298: => 0xf7598288 <getnetconfig+376>: mov %ebp,0x8(%ebx) ncp is is in %ebx (walking through the assembly shows this to be true throughout the function, %ebx isn't reloaded anywhere). As it should, parse_ncp() saves %ebx/%rbx on the stack before modifying it: (gdb) disass parse_ncp Dump of assembler code for function parse_ncp: => 0xf7597e20 <+0>: push %r14 0xf7597e22 <+2>: push %r13 0xf7597e24 <+4>: push %r12 0xf7597e26 <+6>: mov %rdi,%r12 0xf7597e29 <+9>: push %rbp 0xf7597e2a <+10>: lea 0xeba0(%rip),%ebp # 0xf75a69d0 0xf7597e30 <+16>: push %rbx 0xf7597e31 <+17>: mov %rsi,%rbx ... After the save, %rbx is on the stack @ 0xffffcbd0 (gdb) p $rbx $2 = 0x653030 (gdb) x/g $rsp 0xffffcbd0: 0x653030 Now we let strtok_r() run once (it's called @ 0xf7597e63, so we'll break there). Notice our stored %rbx is still on the stack. (gdb) disass $rip,$rip+10 Dump of assembler code from 0xf7597e63 to 0xf7597e6d: => 0xf7597e63 <parse_ncp+67>: callq 0xf7591f10 <__strtok_r@plt> 0xf7597e68 <parse_ncp+72>: test %rax,%rax 0xf7597e6b <parse_ncp+75>: mov %eax,(%ebx) End of assembler dump. (gdb) x/g 0xffffcbd0 0xffffcbd0: 0x0000000000653030 (gdb) nexti 0xf7597e68 528 if ((ncp->nc_netid = strtok_r(stringp, "\t ", &lasts)) == NULL) { (gdb) x/g 0xffffcbd0 0xffffcbd0: 0x0000000000000000 (gdb) ... and afterwards it's corrupted. The part of strtok_r() that actually corrupts the memory location is the "mov %rdx,(%r9)": 81d91: 48 8d 4a 01 lea 0x1(%rdx),%rcx 81d95: 48 0f 45 d1 cmovne %rcx,%rdx => 81d99: 49 89 11 mov %rdx,(%r9) 81d9c: 48 81 c4 00 01 00 00 add $0x100,%rsp 81da3: c3 retq 81da4: 31 c0 xor %eax,%eax 81da6: 49 89 11 mov %rdx,(%r9) 81da9: eb f1 jmp 81d9c <__strtok_r+0xdc> which corresponds to line 197 in glibc-2.15-r2/sysdeps/x86_64/strtok.S: /* Are we at end of string? */ cmpb $0, %cl leaq 1(%rdx), %rcx cmovne %rcx, %rdx /* Store the pointer to the next character. */ ==> mov %RDX_LP, SAVE_PTR L(epilogue): /* Remove the stopset table. */ addq $256, %rsp cfi_adjust_cfa_offset(-256) retq %r9 in this case points to the 32-bit address of strtok_r(..,.., char** saveptr) (defined on the stack as char *last in parse_ncp). So I think the problem is that we're trying to save a 64-bit register (%RDX_LP) into a 32-bit location on the stack and we're corrupting the adjacent 32-bits, which happens to contain our saved %rbx. My x86 assembly and glibc-fu isn't good enough to suggest a solution yet. thanks ... strtok_r should be fixed now by way of bug 420951 |