From owner-freebsd-bugs@FreeBSD.ORG Thu Aug 15 15:30:00 2013 Return-Path: Delivered-To: freebsd-bugs@smarthost.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id D1F216F5 for ; Thu, 15 Aug 2013 15:30:00 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:1900:2254:206c::16:87]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id B0F702750 for ; Thu, 15 Aug 2013 15:30:00 +0000 (UTC) Received: from freefall.freebsd.org (localhost [127.0.0.1]) by freefall.freebsd.org (8.14.7/8.14.7) with ESMTP id r7FFU0bq039009 for ; Thu, 15 Aug 2013 15:30:00 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.7/8.14.7/Submit) id r7FFU0A1039008; Thu, 15 Aug 2013 15:30:00 GMT (envelope-from gnats) Resent-Date: Thu, 15 Aug 2013 15:30:00 GMT Resent-Message-Id: <201308151530.r7FFU0A1039008@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Patrik Nyblom Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id 593AF6D5 for ; Thu, 15 Aug 2013 15:28:17 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from oldred.freebsd.org (oldred.freebsd.org [8.8.178.121]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 457C2273E for ; Thu, 15 Aug 2013 15:28:17 +0000 (UTC) Received: from oldred.freebsd.org ([127.0.1.6]) by oldred.freebsd.org (8.14.5/8.14.7) with ESMTP id r7FFSGoN093463 for ; Thu, 15 Aug 2013 15:28:16 GMT (envelope-from nobody@oldred.freebsd.org) Received: (from nobody@localhost) by oldred.freebsd.org (8.14.5/8.14.5/Submit) id r7FFSGBt093462; Thu, 15 Aug 2013 15:28:16 GMT (envelope-from nobody) Message-Id: <201308151528.r7FFSGBt093462@oldred.freebsd.org> Date: Thu, 15 Aug 2013 15:28:16 GMT From: Patrik Nyblom To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-3.1 Subject: gnu/181328: GCC 4.2.1 20070831 may delay initialization of automatic struct variable too much X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 15 Aug 2013 15:30:01 -0000 >Number: 181328 >Category: gnu >Synopsis: GCC 4.2.1 20070831 may delay initialization of automatic struct variable too much >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Thu Aug 15 15:30:00 UTC 2013 >Closed-Date: >Last-Modified: >Originator: Patrik Nyblom >Release: 9.1-RELEASE-p4 >Organization: Ericsson AB >Environment: FreeBSD machine 9.1-RELEASE-p4 FreeBSD 9.1-RELEASE-p4 #0: Mon Jun 17 11:42:37 UTC 2013 root@amd64-builder.daemonology.net:/usr/obj/usr/src/sys/GENERIC amd64 >Description: This problem was found on FreeBSD 9.1-RELEASE-p4 when building Erlang/OTP. An automatic struct initialization is delayed beyond the place where it's used. The code looks like this: Eterm build_exec_return(Process *p, int rc, RestartContext *restartp, Eterm orig_subject) { Eterm res; Eterm *hp; if (rc <= 0) { .... } else { ReturnInfo *ri = restartp->ret_info; ReturnInfo defri = {RetNone,0,{0}}; if (ri == NULL) { ri = &defri; } if (ri->type == RetNone) { ... } else if (ri->type == RetIndex){ ... } else { ... } ... } I.e. if restartp->ret_info == NULL, ri will be set to &defri, which is initialized to {RetNone,0,{0}}. What happened was that the first branch ( else if (ri->type == RetNone)) was *not* taken when restartp->ret_info was null. Instead the default branch, i.e. the last else branch was taken. This happens when -O2 or -O3 optimization is on. It goes away with the slightest code change, so it's really hard to boil it down to a smaller example. I have prepares two source files, one which is basically gcc -E of the source file from the Erlang VM, and one that is a main program and a lot of stubs to make a runnable program. Here is a gdb session which shows the problem: $ gcc -Werror=return-type -g -O3 -fomit-frame-pointer -Wall -Wstrict-prototypes -Wmissing-prototypes -Wdeclaration-after-statement -o t a.c b.c -lpthread $ gdb t GNU gdb 6.1.1 [FreeBSD] Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "amd64-marcel-freebsd"... (gdb) break build_exec_return Breakpoint 1 at 0x4012e0: file a.c, line 16411. (gdb) run Starting program: /ldisk/pan/report/t [New LWP 5286071] [New Thread 801007400 (LWP 5286071/t)] [Switching to Thread 801007400 (LWP 5286071/t)] Breakpoint 1, build_exec_return (p=0x0, rc=1, restartp=0x7fffffffd970, orig_subject=0) at a.c:16411 16411 { (gdb) n 16414 if (rc <= 0) { (gdb) 16411 { (gdb) 16414 if (rc <= 0) { (gdb) 16429 ReturnInfo *ri = restartp->ret_info; (gdb) 16431 if (ri == ((void *)0)) { (gdb) 16434 if (ri->type == RetNone) { (gdb) 16431 if (ri == ((void *)0)) { (gdb) 16434 if (ri->type == RetNone) { (gdb) 16436 } else if (ri->type == RetIndex){ (gdb) 16430 ReturnInfo defri = {RetNone,0,{0}}; (gdb) 16436 } else if (ri->type == RetIndex){ (gdb) 16501 if (restartp->flags & 0x1) { (gdb) quit As can be seen, the initialization of defri is done after the comparision of ri->type, so, a little bit depending on what was in the memory where defri resides, it will take any branc. The else branch beeing the most likely. Looking at the assembler instructions: 0x00000000004012e0 : push %r15 0x00000000004012e2 : mov %rdx,%r15 0x00000000004012e5 : push %r14 0x00000000004012e7 : push %r13 0x00000000004012e9 : push %r12 0x00000000004012eb : push %rbp 0x00000000004012ec : push %rbx 0x00000000004012ed : sub $0x98,%rsp 0x00000000004012f4 : test %esi,%esi 0x00000000004012f6 : mov %rdi,0x10(%rsp) 0x00000000004012fb : mov %esi,0xc(%rsp) 0x00000000004012ff : jle 0x4016e5 0x0000000000401305 : mov 0x88(%rdx),%rbp 0x000000000040130c : lea 0x70(%rsp),%rax 0x0000000000401311 : mov $0x490b,%ebx 0x0000000000401316 : test %rbp,%rbp 0x0000000000401319 : cmove %rax,%rbp 0x000000000040131d : mov 0x0(%rbp),%eax 0x0000000000401320 : cmp $0x3,%eax 0x0000000000401323 : je 0x401500 0x0000000000401329 : test %eax,%eax 0x000000000040132b : movl $0x3,0x70(%rsp) 0x0000000000401333 : movl $0x0,0x74(%rsp) 0x000000000040133b : movl $0x0,0x78(%rsp) 0x0000000000401343 : jne 0x401515 We see the three instructions initializing the struct (0x000000000040132b, 0x0000000000401333 and 0x000000000040133b) happening after the cmp, but before the jne, which seems to me the wrong place. Doing even the slightest change, like adding an exit(1); to the else branch, will instead give the following: > gdb ./t GNU gdb 6.1.1 [FreeBSD] Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "amd64-marcel-freebsd"... (gdb) break build_exec_return Breakpoint 1 at 0x4012e0: file a.c, line 16411. (gdb) run Starting program: /ldisk/pan/report/t [New LWP 5285172] [New Thread 801007400 (LWP 5285172/t)] [Switching to Thread 801007400 (LWP 5285172/t)] Breakpoint 1, build_exec_return (p=0x0, rc=1, restartp=0x7fffffffd970, orig_subject=0) at a.c:16411 16411 { (gdb) n 16414 if (rc <= 0) { (gdb) 16411 { (gdb) 16414 if (rc <= 0) { (gdb) 16429 ReturnInfo *ri = restartp->ret_info; (gdb) 16431 if (ri == ((void *)0)) { (gdb) 16430 ReturnInfo defri = {RetNone,0,{0}}; (gdb) 16434 if (ri->type == RetNone) { (gdb) 16431 if (ri == ((void *)0)) { (gdb) 16434 if (ri->type == RetNone) { (gdb) p *ri $1 = {type = RetNone, num_spec = -126, v = {0}} Here the type field is initialized before the comparision (the other fields are also initialized, but only just before they are used). This is as it should be. The assembler looks like this now: 0x00000000004012e0 : push %r15 0x00000000004012e2 : push %r14 0x00000000004012e4 : mov %rdi,%r14 0x00000000004012e7 : push %r13 0x00000000004012e9 : push %r12 0x00000000004012eb : push %rbp 0x00000000004012ec : push %rbx 0x00000000004012ed : sub $0x98,%rsp 0x00000000004012f4 : test %esi,%esi 0x00000000004012f6 : mov %esi,0x14(%rsp) 0x00000000004012fa : mov %rdx,0x8(%rsp) 0x00000000004012ff : jle 0x401642 0x0000000000401305 : mov 0x88(%rdx),%rbp 0x000000000040130c : lea 0x70(%rsp),%rax 0x0000000000401311 : movl $0x3,0x70(%rsp) 0x0000000000401319 : mov $0x490b,%ebx 0x000000000040131e : test %rbp,%rbp 0x0000000000401321 : cmove %rax,%rbp 0x0000000000401325 : mov 0x0(%rbp),%eax 0x0000000000401328 : cmp $0x3,%eax 0x000000000040132b : je 0x4014b1 0x0000000000401331 : test %eax,%eax 0x0000000000401333 : movl $0x0,0x74(%rsp) 0x000000000040133b : movl $0x0,0x78(%rsp) 0x0000000000401343 : jne 0x4014c6 The type field is initialized (0x0000000000401311) before the first cmp (0x0000000000401328), so everything is fine. The rest is initialized when needed. As i mentioned, the bug is very elusive. I've prepared a boiled down set of files, including a make file, which can be used to repeat the first gdb session. The program dums core when the wrong branch is taken, but prints out a message if the program behaves as expected. The tar file with the two c files and a Makefile can be fetched from http://erlang.org/~pan/BSD_GCC_trouble_report.tar The set of files is too large to be accepted as a shar attachment to the trouble report. Sorry for the bulky trouble report, that I cannot be more specific and that I cannot find a smaller example! Cheers, Patrik >How-To-Repeat: A tar file with the test program can be fetched from: http://erlang.org/~pan/BSD_GCC_trouble_report.tar Untar, make and run the program with gdb, break in build_exec_return, run and step through the function, as in the description above. The defri struct will not be initialized before ri is dereferenced and the program will most probably take the wrong branch. Just running the program gives segmentation fault. >Fix: >Release-Note: >Audit-Trail: >Unformatted: