[LLVMbugs] [Bug 7369] New: Miscompilation with -O1 and above

bugzilla-daemon at llvm.org bugzilla-daemon at llvm.org
Sun Jun 13 08:17:44 CDT 2010


http://llvm.org/bugs/show_bug.cgi?id=7369

           Summary: Miscompilation with -O1 and above
           Product: clang
           Version: trunk
          Platform: PC
        OS/Version: Linux
            Status: NEW
          Severity: normal
          Priority: P
         Component: -New Bugs
        AssignedTo: unassignedclangbugs at nondot.org
        ReportedBy: pipping at exherbo.org
                CC: llvmbugs at cs.uiuc.edu


I've hit a test failure with libsigsegv that I get with clang but not gcc 4.4.
Its main author, Bruno Haible, has been so kind to analyse this problem. Here
is what he wrote to me:

--------------------

I have clang version 1.0 installed:
$ clang --version
clang version 1.0 (http://llvm.org/svn/llvm-project/cfe/trunk 81046)
Target: x86_64-pc-linux-gnu
Thread model: posix

and what I find is that 'clang' is miscompiling the 'switch' statement of
tests/stackoverflow2.c when -O1 or higher is enabled.

How to reproduce:
- Fetch libsigsegv-2.8.tar.gz from ftp.gnu.org,
- Configure it with "mkdir build; cd build; ../configure CC=clang"
- "make"
- "make check"

When the stackoverflow2 program is compiled like this:

  clang -DHAVE_CONFIG_H -I. -I../../tests -I.. -I../src    -g -c
../../tests/stackoverflow2.c \
  && /bin/sh ../libtool --tag=CC   --mode=link clang  -g   -o stackoverflow2
stackoverflow2.o ../src/libsigsegv.la

it works fine.

When it is compiled like this:

  clang -DHAVE_CONFIG_H -I. -I../../tests -I.. -I../src    -g -O1 -c
../../tests/stackoverflow2.c \
  && /bin/sh ../libtool --tag=CC   --mode=link clang  -g -O1   -o
stackoverflow2 stackoverflow2.o ../src/libsigsegv.la

it runs into the abort() call.

It happens when setjmp() returns with a value of 3.

The source code is:

===============================================================================
int
main ()
{
  ...

  /* Provoke two stack overflows in a row.  */
  switch (setjmp (mainloop))
    {
    case -1:
      printf ("emergency exit\n"); exit (1);
    case 0: case 1:
      printf ("Starting recursion pass %d.\n", pass + 1);
      recurse (0);
      printf ("no endless recursion?!\n"); exit (1);
    case 2:
      *(volatile int *) (page + 0x678) = 42;
      break;
    case 3:
      *(volatile int *) 0 = 42;
      break;
    case 4:
      break;
    default:
      abort ();
    }

  printf ("Test passed.\n");
  exit (0);
}
===============================================================================

In the case that works, without optimization, the code looks like this:

===============================================================================

  401042:    e8 b9 f9 ff ff           callq  400a00 <_setjmp at plt>
  401047:    89 c1                    mov    %eax,%ecx
  401049:    ff c1                    inc    %ecx
  40104b:    89 ca                    mov    %ecx,%edx
  40104d:    83 f9 05                 cmp    $0x5,%ecx
  401050:    48 89 95 50 ff ff ff     mov    %rdx,-0xb0(%rbp)
  401057:    0f 87 9d 00 00 00        ja     4010fa <main+0x25a>
  40105d:    48 8b 85 50 ff ff ff     mov    -0xb0(%rbp),%rax
  401064:    48 89 c1                 mov    %rax,%rcx
  401067:    48 8b 0c cd 08 1c 40     mov    0x401c08(,%rcx,8),%rcx

0x401c08:
(gdb) print *(void*(*)[6]) 0x401c08
$1 = {0x401071, 0x40108c, 0x40108c, 0x4010d2, 0x4010e6, 0x4010f8}

  401071:    30 c0                    xor    %al,%al
  401073:    b9 68 1d 40 00           mov    $0x401d68,%ecx
  401078:    48 89 cf                 mov    %rcx,%rdi
  40107b:    e8 40 f9 ff ff           callq  4009c0 <printf at plt>
  401080:    b8 01 00 00 00           mov    $0x1,%eax
  401085:    89 c7                    mov    %eax,%edi
  401087:    e8 84 f9 ff ff           callq  400a10 <exit at plt>

  40108c:    8b 04 25 e8 30 60 00     mov    0x6030e8,%eax
  401093:    83 c0 01                 add    $0x1,%eax
  401096:    30 c9                    xor    %cl,%cl
  401098:    ba 30 1d 40 00           mov    $0x401d30,%edx
  40109d:    48 89 d7                 mov    %rdx,%rdi
  4010a0:    89 c6                    mov    %eax,%esi
  4010a2:    88 c8                    mov    %cl,%al
  4010a4:    e8 17 f9 ff ff           callq  4009c0 <printf at plt>
  4010a9:    b8 00 00 00 00           mov    $0x0,%eax
  4010ae:    89 c7                    mov    %eax,%edi
  4010b0:    e8 bb fd ff ff           callq  400e70 <recurse>
  4010b5:    30 c9                    xor    %cl,%cl
  4010b7:    ba 50 1d 40 00           mov    $0x401d50,%edx
  4010bc:    48 89 d7                 mov    %rdx,%rdi
  4010bf:    88 c8                    mov    %cl,%al
  4010c1:    e8 fa f8 ff ff           callq  4009c0 <printf at plt>
  4010c6:    b8 01 00 00 00           mov    $0x1,%eax
  4010cb:    89 c7                    mov    %eax,%edi
  4010cd:    e8 3e f9 ff ff           callq  400a10 <exit at plt>

  4010d2:    48 8b 04 25 20 31 60     mov    0x603120,%rax
  4010d9:    00 
  4010da:    c7 80 78 06 00 00 2a     movl   $0x2a,0x678(%rax)
  4010e1:    00 00 00 
  4010e4:    eb 19                    jmp    4010ff <main+0x25f>

  4010e6:    48 b8 00 00 00 00 00     mov    $0x0,%rax
  4010ed:    00 00 00 
  4010f0:    c7 00 2a 00 00 00        movl   $0x2a,(%rax)
  4010f6:    eb 07                    jmp    4010ff <main+0x25f>

  4010f8:    eb 05                    jmp    4010ff <main+0x25f>

  4010fa:    e8 e1 f8 ff ff           callq  4009e0 <abort at plt>

===============================================================================

All right.

With -O1, the code looks like this:

===============================================================================

  400ee5:    e8 5e fb ff ff           callq  400a48 <_setjmp at plt>
  400eea:    ff c0                    inc    %eax
  400eec:    83 f8 05                 cmp    $0x5,%eax
  400eef:    77 78                    ja     400f69 <main+0x189>
  400ef1:    ff 24 c5 08 1a 40 00     jmpq   *0x401a08(,%rax,8)

(gdb) print *(void*(*)[6]) 0x401a08
$1 = {0x400ef8, 0x400f0c, 0x400f0c, 0x400f3b, 0x400f69, 0x400f4c}

  400ef8:    bf 15 1c 40 00           mov    $0x401c15,%edi
  400efd:    e8 36 fb ff ff           callq  400a38 <puts at plt>
  400f02:    bf 01 00 00 00           mov    $0x1,%edi
  400f07:    e8 4c fb ff ff           callq  400a58 <exit at plt>

  400f0c:    8b 35 d6 21 20 00        mov    0x2021d6(%rip),%esi        #
6030e8 <pass>
  400f12:    ff c6                    inc    %esi
  400f14:    bf 30 1b 40 00           mov    $0x401b30,%edi
  400f19:    30 c0                    xor    %al,%al
  400f1b:    e8 d8 fa ff ff           callq  4009f8 <printf at plt>
  400f20:    31 ff                    xor    %edi,%edi
  400f22:    e8 99 fe ff ff           callq  400dc0 <recurse>
  400f27:    bf e0 1b 40 00           mov    $0x401be0,%edi
  400f2c:    e8 07 fb ff ff           callq  400a38 <puts at plt>
  400f31:    bf 01 00 00 00           mov    $0x1,%edi
  400f36:    e8 1d fb ff ff           callq  400a58 <exit at plt>

  400f3b:    48 8b 05 de 21 20 00     mov    0x2021de(%rip),%rax        #
603120 <page>
  400f42:    c7 80 78 06 00 00 2a     movl   $0x2a,0x678(%rax)
  400f49:    00 00 00 
  400f4c:    bf 24 1c 40 00           mov    $0x401c24,%edi
  400f51:    e8 e2 fa ff ff           callq  400a38 <puts at plt>
  400f56:    31 ff                    xor    %edi,%edi
  400f58:    e8 fb fa ff ff           callq  400a58 <exit at plt>
  400f5d:    31 c0                    xor    %eax,%eax
  400f5f:    48 81 c4 98 00 00 00     add    $0x98,%rsp
  400f66:    5b                       pop    %rbx
  400f67:    5d                       pop    %rbp
  400f68:    c3                       retq   

  400f69:    e8 aa fa ff ff           callq  400a18 <abort at plt>

===============================================================================

Here you can see, the entry 0x401a08[4] - which is used when setjmp's return
value is 3, is 0x400f69, which is the 'default:' case. You can see that the
compiler eliminated this part of the switch statement:

    case 3:
      *(volatile int *) 0 = 42;
      break;

This may be valid for ISO C 99 programs (because a NULL pointer access is
invalid in ISO C 99), but in a POSIX program it is not valid to remove this
- because the program may rely on the fact that a signal handler is called.
Which is the case here.

---------------------

The problem is the same with clang version 2.0 (trunk 105896).

-- 
Configure bugmail: http://llvm.org/bugs/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are on the CC list for the bug.


More information about the LLVMbugs mailing list