Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clang generates endless loop #9986

Closed
llvmbot opened this issue Apr 3, 2011 · 28 comments
Closed

clang generates endless loop #9986

llvmbot opened this issue Apr 3, 2011 · 28 comments
Labels
bugzilla Issues migrated from bugzilla clang Clang issues not falling into any other category regression

Comments

@llvmbot
Copy link
Collaborator

llvmbot commented Apr 3, 2011

Bugzilla Link 9614
Resolution FIXED
Resolved on Mar 07, 2013 16:17
Version trunk
OS Linux
Blocks llvm/llvm-bugzilla-archive#11199
Attachments glibc wchar.h
Reporter LLVM Bugzilla Contributor
CC @ahatanak,@andersk,@shining,@echristo,@efriedma-quic,@foutrelis,@ismail

Extended Description

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


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.

@efriedma-quic
Copy link
Collaborator

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.

@shining
Copy link
Mannequin

shining mannequin commented May 22, 2011

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

@efriedma-quic
Copy link
Collaborator

*** Bug llvm/llvm-bugzilla-archive#10179 has been marked as a duplicate of this bug. ***

@efriedma-quic
Copy link
Collaborator

*** Bug llvm/llvm-bugzilla-archive#10207 has been marked as a duplicate of this bug. ***

@llvmbot
Copy link
Collaborator Author

llvmbot commented Jul 17, 2011

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

@d0k
Copy link
Member

d0k commented Oct 6, 2011

*** Bug llvm/llvm-bugzilla-archive#10981 has been marked as a duplicate of this bug. ***

@llvmbot
Copy link
Collaborator Author

llvmbot commented Oct 11, 2011

*** Bug llvm/llvm-bugzilla-archive#10160 has been marked as a duplicate of this bug. ***

@llvmbot
Copy link
Collaborator Author

llvmbot commented Oct 19, 2011

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.

@llvmbot
Copy link
Collaborator Author

llvmbot commented Oct 19, 2011

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/

@llvmbot
Copy link
Collaborator Author

llvmbot commented Oct 21, 2011

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

@llvmbot
Copy link
Collaborator Author

llvmbot commented Oct 25, 2011

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
}

@ismail
Copy link
Contributor

ismail commented Oct 25, 2011

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.

@llvmbot
Copy link
Collaborator Author

llvmbot commented Oct 25, 2011

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

@ismail
Copy link
Contributor

ismail commented Oct 25, 2011

I can only reproduce the problem with my original testcase from bug 10160 , http://llvm.org/bugs/attachment.cgi?id=6754

@llvmbot
Copy link
Collaborator Author

llvmbot commented Oct 25, 2011

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.

@ismail
Copy link
Contributor

ismail commented Oct 25, 2011

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 :(

@llvmbot
Copy link
Collaborator Author

llvmbot commented Oct 26, 2011

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=, n=, s=) at bug.c:12
12 if(fread(s, 1, n, f) < (size_t)n)
(gdb) bt
#​0 fread_n_bytes (f=, n=, s=) at bug.c:12
#​1 0x00000000004005cf in main (argc=, argv=) 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.

@echristo
Copy link
Contributor

Looking like the source fortification is necessary? Maybe it's interfering with an optimization that the code expects to happen?

@ismail
Copy link
Contributor

ismail commented Oct 26, 2011

@llvmbot
Copy link
Collaborator Author

llvmbot commented Oct 26, 2011

Fixed in r143049.

@llvmbot
Copy link
Collaborator Author

llvmbot commented Oct 27, 2011

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

@ismail
Copy link
Contributor

ismail commented Oct 27, 2011

Fixed in r143049.

Problem is still producable with the testcase from bug 10160 ,
http://llvm.org/bugs/attachment.cgi?id=6754

@llvmbot
Copy link
Collaborator Author

llvmbot commented Oct 28, 2011

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();
}

@llvmbot
Copy link
Collaborator Author

llvmbot commented Oct 28, 2011

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
}

@llvmbot
Copy link
Collaborator Author

llvmbot commented Oct 28, 2011

fixed in r143222.

@llvmbot
Copy link
Collaborator Author

llvmbot commented Oct 28, 2011

fixed in r143222.

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

http://llvm.org/svn/llvm-project/cfe/branches/release_30

@llvmbot
Copy link
Collaborator Author

llvmbot commented Oct 30, 2011

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

@andersk
Copy link
Mannequin

andersk mannequin commented Mar 8, 2013

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

@llvmbot llvmbot transferred this issue from llvm/llvm-bugzilla-archive Dec 3, 2021
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugzilla Issues migrated from bugzilla clang Clang issues not falling into any other category regression
Projects
None yet
Development

No branches or pull requests

5 participants