Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 394181 - net-fs/nfs-utils: rpc.mountd segfaults when built as x32
Summary: net-fs/nfs-utils: rpc.mountd segfaults when built as x32
Alias: None
Product: Gentoo Linux
Classification: Unclassified
Component: Current packages (show other bugs)
Hardware: All Linux
: Normal normal (vote)
Assignee: Network Filesystems
Depends on:
Blocks: x32
  Show dependency tree
Reported: 2011-12-09 19:07 UTC by SpanKY
Modified: 2012-06-23 18:28 UTC (History)
2 users (show)

See Also:
Package list:
Runtime testing required: ---


Note You need to log in before you can comment on or make changes to this bug.
Description SpanKY gentoo-dev 2011-12-09 19:07:26 UTC
seems like it's a bug in nfs-utils, but it could be libtirpc ...

~/gdb/gdb/gdb -q rpc.mountd
Reading symbols from /usr/sbin/rpc.mountd...(no debugging symbols found)...done.
(gdb) r
Starting program: /usr/sbin/rpc.mountd 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/libx32/".

Program received signal SIGSEGV, Segmentation fault.
0xf779c78d in getnetconfig () from /libx32/
(gdb) bt
#0  0xf779c78d in getnetconfig () from /libx32/
#1  0xf77a09f8 in ?? () from /libx32/
#2  0xf77a1391 in rpcb_unset () from /libx32/
#3  0x0041087a in ?? ()
#4  0x004034c2 in ?? ()
#5  0x00402ebe in ?? ()
#6  0xf7426aeb in __libc_start_main () from /libx32/
#7  0x004032eb in ?? ()
#8  0xffffd568 in ?? ()
#9  0x0000001c in ?? ()
#10 0x00000001 in ?? ()
#11 0x00000000 in ?? ()
Comment 1 Robert Trace 2012-06-23 04:27:32 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, 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
Do you need "set solib-search-path" or "set sysroot"?
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/libx32/".

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;
302         if (parse_ncp(stringp, list->ncp) == -1) {
298         list->ncp = np;
302         if (parse_ncp(stringp, list->ncp) == -1) {
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

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

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

        /* Remove the stopset table.  */
        addq $256, %rsp

%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.
Comment 2 SpanKY gentoo-dev 2012-06-23 18:28:16 UTC
thanks ... strtok_r should be fixed now by way of bug 420951