LLVM Bugzilla is read-only and represents the historical archive of all LLVM issues filled before November 26, 2021. Use github to submit LLVM bugs

Bug 9614 - clang generates endless loop
Summary: clang generates endless loop
Status: RESOLVED FIXED
Alias: None
Product: clang
Classification: Unclassified
Component: -New Bugs (show other bugs)
Version: trunk
Hardware: PC Linux
: P normal
Assignee: Rafael Ávila de Espíndola
URL:
Keywords: regression
: 10160 10179 10207 10981 (view as bug list)
Depends on:
Blocks: 11199
  Show dependency tree
 
Reported: 2011-04-02 20:15 PDT by Alexander Kolesen
Modified: 2013-03-07 16:17 PST (History)
17 users (show)

See Also:
Fixed By Commit(s):


Attachments
glibc wchar.h (31.63 KB, text/x-chdr)
2011-04-02 20:16 PDT, Alexander Kolesen
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Alexander Kolesen 2011-04-02 20:15:12 PDT
Clang generates llvm intermediate code with an endless loop, when -O is used.
Here is source .c code:

$ cat test.c
#include <wchar.h>
int main ()
{
    return btowc ('\0');
}


And after compilation we have:

(gdb) disas main
Dump of assembler code for function main:
   0x00000000004004f0 <+0>:	jmp    0x4004f0 <main>
End of assembler dump.


Here is LLVM IR:

$ clang -S -emit-llvm test.c; cat test.s
; ModuleID = 'test.c'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
target triple = "x86_64-pc-linux-gnu"

define i32 @main() nounwind {
  %1 = alloca i32, align 4
  store i32 0, i32* %1
  %2 = call i32 @btowc(i32 0) nounwind
  ret i32 %2
}

declare i32 @btowc(i32) nounwind



$ clang -O -S -emit-llvm test.c; cat test.s
; ModuleID = 'test.c'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
target triple = "x86_64-pc-linux-gnu"

define i32 @main() nounwind readnone {
  br label %tailrecurse.i

tailrecurse.i:                                    ; preds = %tailrecurse.i, %0
  br label %tailrecurse.i
}


I've attached wchar.h just in case.
Comment 1 Alexander Kolesen 2011-04-02 20:16:37 PDT
Created attachment 6391 [details]
glibc wchar.h
Comment 2 Eli Friedman 2011-04-03 15:13:17 PDT
Testcase boils down to something like the following:

typedef __typeof(+L'a') wint_t;
extern wint_t __btowc_alias (int __c) __asm ("btowc");
__attribute((nothrow)) inline wint_t btowc (int __c) {
  return __btowc_alias (__c);
}
int f(int x) { return btowc(x); }

clang resolves the call to __btowc_alias into a call to btowc, so clang essentially sees through the trick to try and make the inline version call the out-of-line version.  The function then ends up looking like it's tail-recursive, i.e. an infinite loop.
Comment 3 Xavier 2011-05-22 05:55:49 PDT
I've found another test case with realpath. 

extern char *__realpath_alias (__const char *__restrict __name, char
                               *__restrict __resolved) __asm__ ("" "realpath");

extern  char *
realpath (__const char *__restrict __name, char *__restrict __resolved)
{
  return __realpath_alias (__name, __resolved);
}

char *f(const char *path, char *buf)
{
    return realpath(path, buf);
}
Comment 4 Eli Friedman 2011-06-23 13:53:38 PDT
*** Bug 10179 has been marked as a duplicate of this bug. ***
Comment 5 Eli Friedman 2011-06-27 18:20:31 PDT
*** Bug 10207 has been marked as a duplicate of this bug. ***
Comment 6 Elias Pipping 2011-07-17 08:11:11 PDT
I get an infinite loop for btowc() with -O2 but not -O1 as described in bug #10207.

This bug appears to have quite far reaching consequences. Not only does it make the configure scripts of such software as gnu tar, coreutils, diffutils, findutils, gettext, and grep hang. It might be the reason gnu sed, and awk hang during their tests as well (at least they hang when compiled with -O2 but not with -O1).
Comment 7 Benjamin Kramer 2011-10-06 15:05:35 PDT
*** Bug 10981 has been marked as a duplicate of this bug. ***
Comment 8 Rafael Ávila de Espíndola 2011-10-11 14:54:52 PDT
*** Bug 10160 has been marked as a duplicate of this bug. ***
Comment 9 Shea Levy 2011-10-18 23:06:39 PDT
Another testcase with getcwd, boils down to (after preprocessing, removing impossible code paths, etc)

typedef __typeof__(sizeof(int)) size_t;
extern char *__getcwd_alias (char *__buf, size_t __size) __asm__ ("" "getcwd");
extern char *getcwd (char *__buf, size_t __size)
{ 
	return __getcwd_alias (__buf, __size);
}
char *test(char *buf, int len)
{ 
	return getcwd((char *)buf, len);
}

This bug clang build a hanging vim on Linux unless optimizations are explicitly disabled.
Comment 10 Shea Levy 2011-10-18 23:08:06 PDT
(In reply to comment #9)
> Another testcase with getcwd, boils down to (after preprocessing, removing
> impossible code paths, etc)
> 
> typedef __typeof__(sizeof(int)) size_t;
> extern char *__getcwd_alias (char *__buf, size_t __size) __asm__ ("" "getcwd");
> extern char *getcwd (char *__buf, size_t __size)
> { 
>     return __getcwd_alias (__buf, __size);
> }
> char *test(char *buf, int len)
> { 
>     return getcwd((char *)buf, len);
> }
> 
> This bug clang build a hanging vim on Linux unless optimizations are explicitly
> disabled.

s/clang/causes clang to/
Comment 11 Duncan Sands 2011-10-21 06:01:34 PDT
Dragonegg turns this into

define i32 @btowc(i32 %__c) nounwind uwtable inlinehint {
entry:
  %0 = tail call i32 @"\01btowc"(i32 %__c) nounwind
  ret i32 %0
}

declare i32 @"\01btowc"(i32)

define i32 @f(i32 %x) nounwind uwtable {
entry:
  %0 = tail call i32 @"\01btowc"(i32 %x) nounwind
  ret i32 %0
}

which results in f being codegened as

        jmp     btowc
Comment 12 Bill Wendling 2011-10-25 02:04:58 PDT
Is this still a problem for clang? This is what I'm getting now:

$ cat test.c
#include <wchar.h>
int main () {
    return btowc ('\0');
}

$ clang test.c -o - -S -emit-llvm -O
; ModuleID = 'test.c'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.7.2"

define i32 @main() nounwind uwtable ssp {
entry:
  %call = tail call i32 @btowc(i32 0) nounwind
  ret i32 %call
}

declare i32 @btowc(i32)


$ cat > test.c
typedef __typeof(+L'a') wint_t;
extern wint_t __btowc_alias (int __c) __asm ("btowc");
__attribute((nothrow)) inline wint_t btowc (int __c) {
  return __btowc_alias (__c);
}
int f(int x) { return btowc(x); }

$ clang test.c -o - -S -emit-llvm -O
; ModuleID = 'test.c'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.7.2"

define i32 @f(i32 %x) nounwind uwtable ssp {
entry:
  %call.i = tail call i32 @"\01btowc"(i32 %x) nounwind
  ret i32 %call.i
}

declare i32 @"\01btowc"(i32)


$ cat > test.c
extern char *__realpath_alias (__const char *__restrict __name, char
                               *__restrict __resolved) __asm__ ("" "realpath");

extern  char *
realpath (__const char *__restrict __name, char *__restrict __resolved) {
  return __realpath_alias (__name, __resolved);
}

char *f(const char *path, char *buf) {
    return realpath(path, buf);
}

$ clang test.c -o - -S -emit-llvm -O
; ModuleID = 'test.c'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.7.2"

define i8* @realpath(i8* noalias %__name, i8* noalias %__resolved) nounwind uwtable ssp {
entry:
  %call = tail call i8* @"\01realpath"(i8* %__name, i8* %__resolved) nounwind
  ret i8* %call
}

declare i8* @"\01realpath"(i8*, i8*)

define i8* @f(i8* %path, i8* %buf) nounwind uwtable ssp {
entry:
  %call.i = tail call i8* @"\01realpath"(i8* %path, i8* %buf) nounwind
  ret i8* %call.i
}


$ cat > test.c
typedef __typeof__(sizeof(int)) size_t;
extern char *__getcwd_alias (char *__buf, size_t __size) __asm__ ("" "getcwd");
extern char *getcwd (char *__buf, size_t __size) { 
    return __getcwd_alias (__buf, __size);
}
char *test(char *buf, int len) { 
    return getcwd((char *)buf, len);
}

$ clang test.c -o - -S -O -emit-llvm
; ModuleID = 'test.c'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.7.2"

define i8* @getcwd(i8* %__buf, i64 %__size) nounwind uwtable ssp {
entry:
  %call = tail call i8* @"\01getcwd"(i8* %__buf, i64 %__size) nounwind
  ret i8* %call
}

declare i8* @"\01getcwd"(i8*, i64)

define i8* @test(i8* %buf, i32 %len) nounwind uwtable ssp {
entry:
  %conv = sext i32 %len to i64
  %call.i = tail call i8* @"\01getcwd"(i8* %buf, i64 %conv) nounwind
  ret i8* %call.i
}
Comment 13 İsmail Dönmez 2011-10-25 02:08:19 PDT
(In reply to comment #12)
> Is this still a problem for clang? This is what I'm getting now:

Still reproducable on Linux with

[/kutu/stuff]> clang -v
SUSE Linux clang version 3.0 (branches/release_30 142341) (based on LLVM 3.0)
Target: x86_64-unknown-linux-gnu
Thread model: posix

And AFAIK it only affects Linux.
Comment 14 Bill Wendling 2011-10-25 02:18:57 PDT
(In reply to comment #13)
> (In reply to comment #12)
> > Is this still a problem for clang? This is what I'm getting now:
> 
> Still reproducable on Linux with
> 
> [/kutu/stuff]> clang -v
> SUSE Linux clang version 3.0 (branches/release_30 142341) (based on LLVM 3.0)
> Target: x86_64-unknown-linux-gnu
> Thread model: posix
> 
> And AFAIK it only affects Linux.

Interesting. When I took your  IR example from the fist comment and ran it through 'opt' it generated IR which looked just fine. And the result was a 'jmp btowc' command, like Duncan's.

Can you investigate further? I don't have easy access to a Linux box..
Comment 15 İsmail Dönmez 2011-10-25 02:24:22 PDT
I can only reproduce the problem with my original testcase from bug 10160 , http://llvm.org/bugs/attachment.cgi?id=6754
Comment 16 Bill Wendling 2011-10-25 02:33:54 PDT
(In reply to comment #14)
> Interesting. When I took your  IR example from the fist comment and ran it
> through 'opt' it generated IR which looked just fine. And the result was a 'jmp
> btowc' command, like Duncan's.
> 
> Can you investigate further? I don't have easy access to a Linux box..

I just remembered that I *do* have access to a Linux box. All of the tests I ran give the result as "ret i8* undef" (or some variation on that) with `-O'. Almost certainly not what you're looking for.
Comment 17 İsmail Dönmez 2011-10-25 02:36:16 PDT
(In reply to comment #16)
> (In reply to comment #14)
> > Interesting. When I took your  IR example from the fist comment and ran it
> > through 'opt' it generated IR which looked just fine. And the result was a 'jmp
> > btowc' command, like Duncan's.
> > 
> > Can you investigate further? I don't have easy access to a Linux box..
> 
> I just remembered that I *do* have access to a Linux box. All of the tests I
> ran give the result as "ret i8* undef" (or some variation on that) with `-O'.
> Almost certainly not what you're looking for.

Yes, please try with http://llvm.org/bugs/attachment.cgi?id=6754 and you need to do

clang -O2 -D_FORTIFY_SOURCE=2 mmap.c

since the definition of _FORTIFY_SOURCE=2 along with -O2 triggers the problem. bug 10160 had a nicer discussion of this bug but its marked as a duplicate :(
Comment 18 Lionel Debroux 2011-10-26 13:00:10 PDT
Another reduced testcase for which clang, when passed -D_FORTIFY_SOURCE=2, turns into an infinite loop, on current 64-bit Debian testing, in both 64-bit and 32-bit mode:

$ cat bug.c
#include <stdint.h>
#include <stdio.h>

int fread_n_bytes(FILE * f, int n, uint8_t *s)
{
  int i;

  if (s == NULL) 
    for (i = 0; i < n; i++)
      fgetc(f);
  else 
    if(fread(s, 1, n, f) < (size_t)n) // infinite loop here.
      return -1;

  return 0;
}

int main(int argc, char * argv[]) {
    uint8_t buf[8];
    return fread_n_bytes((FILE *)0x12345678, 8, buf);
}


clang invocations that produce bad programs:
$ clang -D_FORTIFY_SOURCE=2 -Os -g3 -Wall -W -Wno-unused-parameter -Wshadow -Wwrite-strings -Wredundant-decls -Wdeclaration-after-statement -fstack-protector-all -Wstack-protector -o bad1 bug.c
$ clang -m32 -D_FORTIFY_SOURCE=2 -Os -g3 -Wall -W -Wno-unused-parameter -Wshadow -Wwrite-strings -Wredundant-decls -Wdeclaration-after-statement -fstack-protector-all -Wstack-protector -o bad2 bug.c
$ clang -D_FORTIFY_SOURCE=2 -Os -g3 -Wall -W -Wno-unused-parameter -Wshadow -Wwrite-strings -Wredundant-decls -Wdeclaration-after-statement -o bad3 bug.c

Invocations that produce good programs:
$ clang -Os -g3 -Wall -W -Wno-unused-parameter -Wshadow -Wwrite-strings -Wredundant-decls -Wdeclaration-after-statement -o good1 bug.c
$ clang -Os -g3 -Wall -W -Wno-unused-parameter -Wshadow -Wwrite-strings -Wredundant-decls -Wdeclaration-after-statement -fstack-protector-all -Wstack-protector -o good2 bug.c


$ gdb bad1
GNU gdb (GDB) 7.3-debian
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/a515273/essai_clang/bad1...done.
(gdb) run
Starting program: /home/a515273/essai_clang/bad1 
^C
Program received signal SIGINT, Interrupt.
fread_n_bytes (f=<optimized out>, n=<optimized out>, s=<optimized out>) at bug.c:12
12          if(fread(s, 1, n, f) < (size_t)n)
(gdb) bt
#0  fread_n_bytes (f=<optimized out>, n=<optimized out>, s=<optimized out>) at bug.c:12
#1  0x00000000004005cf in main (argc=<optimized out>, argv=<optimized out>) at bug.c:20
(gdb) disassemble
Dump of assembler code for function fread_n_bytes:
   0x0000000000400554 <+0>:     push   %rbp
   0x0000000000400555 <+1>:     mov    %rsp,%rbp
   0x0000000000400558 <+4>:     push   %r14
   0x000000000040055a <+6>:     push   %rbx
   0x000000000040055b <+7>:     sub    $0x10,%rsp
   0x000000000040055f <+11>:    mov    %esi,%ebx
   0x0000000000400561 <+13>:    mov    %rdi,%r14
   0x0000000000400564 <+16>:    mov    %fs:0x28,%rax
   0x000000000040056d <+25>:    mov    %rax,-0x18(%rbp)
   0x0000000000400571 <+29>:    test   %rdx,%rdx
   0x0000000000400574 <+32>:    jne    0x40059a <fread_n_bytes+70>
   0x0000000000400576 <+34>:    test   %ebx,%ebx
   0x0000000000400578 <+36>:    jle    0x400586 <fread_n_bytes+50>
   0x000000000040057a <+38>:    mov    %r14,%rdi
   0x000000000040057d <+41>:    callq  0x400450 <fgetc@plt>
   0x0000000000400582 <+46>:    dec    %ebx
   0x0000000000400584 <+48>:    jne    0x40057a <fread_n_bytes+38>
   0x0000000000400586 <+50>:    mov    %fs:0x28,%rax
   0x000000000040058f <+59>:    cmp    -0x18(%rbp),%rax
   0x0000000000400593 <+63>:    je     0x40059c <fread_n_bytes+72>
   0x0000000000400595 <+65>:    callq  0x400440 <__stack_chk_fail@plt>
=> 0x000000000040059a <+70>:    jmp    0x40059a <fread_n_bytes+70>
   0x000000000040059c <+72>:    xor    %eax,%eax
   0x000000000040059e <+74>:    add    $0x10,%rsp
   0x00000000004005a2 <+78>:    pop    %rbx
   0x00000000004005a3 <+79>:    pop    %r14
   0x00000000004005a5 <+81>:    pop    %rbp
   0x00000000004005a6 <+82>:    retq   
End of assembler dump.
Comment 19 Eric Christopher 2011-10-26 13:23:51 PDT
Looking like the source fortification is necessary? Maybe it's interfering with an optimization that the code expects to happen?
Comment 20 İsmail Dönmez 2011-10-26 13:34:33 PDT
Potential fix here: http://comments.gmane.org/gmane.comp.compilers.clang.scm/40901
Comment 21 Rafael Ávila de Espíndola 2011-10-26 15:56:56 PDT
Fixed in r143049.
Comment 22 Lionel Debroux 2011-10-27 00:56:12 PDT
Well, the testcase I posted in comment #18 still fails (infinite loops) for me after r143049, when -D_FORTIFY_SOURCE=2 is used:

$ clang --version
clang version 3.1 (trunk 143096)
Target: x86_64-unknown-linux-gnu
Thread model: posix
a515273@aofr42198:~/essai_clang$ rm bad* good*
a515273@aofr42198:~/essai_clang$ sh build.sh 
a515273@aofr42198:~/essai_clang$ ls -1
bad1
bad2
bad3
bug.c
build.sh
good1
good2
$ ./good1
Segmentation fault
$ ./good2
Segmentation fault
$ ./bad1
^C
$ ./bad2
^C
$ ./bad3
^C

$ cat build.sh
#!/bin/sh
# The base invocation, reduced from my original testcase.
clang -D_FORTIFY_SOURCE=2 -Os -g3 -Wall -W -Wno-unused-parameter -Wshadow -Wwrite-strings -Wredundant-decls -Wdeclaration-after-statement -fstack-protector-all -Wstack-protector -o bad1 bug.c
# It ain't better in 32-bit mode.
clang -m32 -D_FORTIFY_SOURCE=2 -Os -g3 -Wall -W -Wno-unused-parameter -Wshadow -Wwrite-strings -Wredundant-decls -Wdeclaration-after-statement -fstack-protector-all -Wstack-protector -o bad2 bug.c
# Disabling stack protector switches doesn't help.
clang -D_FORTIFY_SOURCE=2 -Os -g3 -Wall -W -Wno-unused-parameter -Wshadow -Wwrite-strings -Wredundant-decls -Wdeclaration-after-statement -o bad3 bug.c

# Disabling FORTIFY_SOURCE helps.
clang -Os -g3 -Wall -W -Wno-unused-parameter -Wshadow -Wwrite-strings -Wredundant-decls -Wdeclaration-after-statement -o good1 bug.c
# No problem if stack protector switches are activated but FORTIFY_SOURCE is not set.
clang -Os -g3 -Wall -W -Wno-unused-parameter -Wshadow -Wwrite-strings -Wredundant-decls -Wdeclaration-after-statement -fstack-protector-all -Wstack-protector -o good2 bug.c

$ cat bug.c
#include <stdint.h>
#include <stdio.h>

int fread_n_bytes(FILE * f, int n, uint8_t *s)
{
  int i;

  if (s == NULL) 
    for (i = 0; i < n; i++)
      fgetc(f);
  else 
    if(fread(s, 1, n, f) < (size_t)n)
      return -1;

  return 0;
}

int main(int argc, char * argv[]) {
    uint8_t buf[8];
    return fread_n_bytes((FILE *)0x12345678, 8, buf);
}
Comment 23 İsmail Dönmez 2011-10-27 04:06:42 PDT
(In reply to comment #21)
> Fixed in r143049.

Problem is still producable with the testcase from bug 10160 ,
http://llvm.org/bugs/attachment.cgi?id=6754
Comment 24 Rafael Ávila de Espíndola 2011-10-28 15:20:06 PDT
Reduced testcase that still fails:

extern int foo_alias (void) __asm__ ("foo");
inline __attribute__ ((__always_inline__))  int foo (void) {
  return foo_alias ();
}
int bar() {
  foo();
}
Comment 25 Duncan Sands 2011-10-28 15:29:04 PDT
Dragonegg produces this for the testcase in comment 24:

define i32 @foo() nounwind uwtable inlinehint alwaysinline {
entry:
  %0 = tail call i32 @"\01foo"() nounwind
  ret i32 %0
}

declare i32 @"\01foo"()

define i32 @bar(...) nounwind uwtable {
entry:
  %0 = call i32 @"\01foo"() nounwind
  ret i32 undef
}
Comment 26 Rafael Ávila de Espíndola 2011-10-28 15:45:42 PDT
fixed in r143222.
Comment 27 Bill Wendling 2011-10-28 16:23:07 PDT
(In reply to comment #26)
> fixed in r143222.

This doesn't apply cleanly to the 3.0 release branch:

http://llvm.org/svn/llvm-project/cfe/branches/release_30
Comment 28 Lionel Debroux 2011-10-30 05:05:21 PDT
With clang r143304, the reduced testcase I posted in comments #18 and #22 now segfaults (which is the expected behaviour) even when compiled with -D_FORTIFY_SOURCE=2, and the original application from which I extracted it doesn't contain an infinite loop anymore, either.
Thanks :)
Comment 29 Anders Kaseorg 2013-03-07 16:17:36 PST
This fix doesn’t work in C++ mode, where the fortify wrappers use __attribute__ ((__always_inline__)) but not __attribute__ ((__gnu_inline__)):

http://llvm.org/bugs/show_bug.cgi?id=10276#c9