public inbox for gcc-help@gcc.gnu.org
 help / color / mirror / Atom feed
* Re: gcc-2.95 OK, gcc-{3,4}.X not OK
       [not found] <4B3DE0EA.5060302@redhat.com>
@ 2010-01-03  4:55 ` Andris Kalnozols
  2010-01-03 11:17   ` Andrew Haley
  0 siblings, 1 reply; 5+ messages in thread
From: Andris Kalnozols @ 2010-01-03  4:55 UTC (permalink / raw)
  To: gcc-help

> [redir to gcc-help]
>
> On 01/01/2010 05:44 AM, Andris Kalnozols wrote:
> 
> > If the bug was a basic programming error, one would expect a
> > runtime failure (dereferencing a NULL pointer) no matter which
> > compiler was used.
> 
> I would not expect that, and I have no idea whay you would.  Undefined
> behaviour can happen in any way: maybe the program appears to run
> correctly, maybe it faults.

I stand corrected.  Dumb luck was hiding a programming flaw.

> > The application compiles cleanly with no warnings using "-Wall".
> > Were there any transition issues with the newer gcc compilers of
> > which I may not be aware?
> 
> No.  As you've done the obvious first stage (using -Wall) you now
> should run your program under Valgrind, assuming that it is available
> on your system.

Having already looked at Valgrind a few years ago, I revisited it per
your suggestion and studied its options this time.  With the help of
this website:

  https://computing.llnl.gov/code/memcheck/#deciphering4

the Valgrind application revealed this flaw:

  pcptr = pcptr->code = nop;

"code" can't be reached through "pcptr" if the pointer does not
yet have the proper address.

Since multiple assignments are legal and evaluated from right to left,
one could expect the following to work:

  pcptr->code = pcptr = nop;

It does *not* work, however, using the gcc 3.3 or 4.3 compilers.
To summarize:

  pcptr = pcptr->code = nop;   /* crashes with no compiler warning */
  pcptr->code = pcptr = nop;   /* crashes with the warning:
                                * operation on `pcptr' may be undefined
                                */
  pcptr->code = (pcptr = nop); /* same as above */
  pcptr = nop;  pcptr->code = nop;  /* this works */

FWIW, the HP-UX 11.31 compiler warns/not warns in the same
way but the compiled program is "luckier" at run time.

Perhaps the compiler should regard the above bootstrapping-style
multiple assignments as ambiguous and issue the warning regardless
of the order in which the targets appear.

Valgrind is an awesome tool.

------
Andris

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: gcc-2.95 OK, gcc-{3,4}.X not OK
  2010-01-03  4:55 ` gcc-2.95 OK, gcc-{3,4}.X not OK Andris Kalnozols
@ 2010-01-03 11:17   ` Andrew Haley
  2010-01-07 22:37     ` Andris Kalnozols
  0 siblings, 1 reply; 5+ messages in thread
From: Andrew Haley @ 2010-01-03 11:17 UTC (permalink / raw)
  To: Andris Kalnozols; +Cc: gcc-help

On 01/03/2010 04:54 AM, Andris Kalnozols wrote:

> Since multiple assignments are legal and evaluated from right to left,
> one could expect the following to work:
> 
>   pcptr->code = pcptr = nop;

I wouldn't.  :-)

> It does *not* work, however, using the gcc 3.3 or 4.3 compilers.
> To summarize:
> 
>   pcptr = pcptr->code = nop;   /* crashes with no compiler warning */
>   pcptr->code = pcptr = nop;   /* crashes with the warning:
>                                 * operation on `pcptr' may be undefined
>                                 */
>   pcptr->code = (pcptr = nop); /* same as above */
>   pcptr = nop;  pcptr->code = nop;  /* this works */
> 
> FWIW, the HP-UX 11.31 compiler warns/not warns in the same
> way but the compiled program is "luckier" at run time.
> 
> Perhaps the compiler should regard the above bootstrapping-style
> multiple assignments as ambiguous and issue the warning regardless
> of the order in which the targets appear.

Well, gcc doesn't necessarily know.  We do a fair bit of analysis in
an attempt to help the programmer, and we warn when we reasonably can,
but gcc isn't omniscient.

In this case, though, you were burnt by a pointlessly obscure coding
style, combined with a misunderstanding of the language.  If you write
the above code as

   pcptr = nop;
   pcptr->code = nop;

it'll always work, and its semantics are obvious.

Andrew.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: gcc-2.95 OK, gcc-{3,4}.X not OK
  2010-01-03 11:17   ` Andrew Haley
@ 2010-01-07 22:37     ` Andris Kalnozols
  2010-01-08  7:55       ` Cedric Roux
  2010-01-08 10:46       ` Andrew Haley
  0 siblings, 2 replies; 5+ messages in thread
From: Andris Kalnozols @ 2010-01-07 22:37 UTC (permalink / raw)
  To: gcc-help

> > On 01/03/2010 04:54 AM, Andris Kalnozols wrote:
> 
> > Since multiple assignments are legal and evaluated from right
> > to left, one could expect the following to work:
> > 
> >   pcptr->code = pcptr = nop;
> 
> On 01/03/2010 11:17 AM, Andrew Haley replied:
>
> I wouldn't.  :-)

OK, the problem was found, not by Valgrind nor Coverity, but the
old fashioned method of inserting debugging statements in the code.

There are no coding nor compilation errors with either

  pcptr = pcptr->code = nop;
    or
  pcptr->code = pcptr = nop;

As long as the pointers are actually pointing to something,
this is typical linked list processing.

There is a problem, however, when a global pointer structure is
the assignment target of a function which itself may modify the
global pointer.  The included test program gives the following
output when compiled with gcc {3,4}.X:


  ./gcc-bug
  Take the suspect branch [1] or stay `safe' [2]? 1
  Initialize `pcptr' with NULL [1] or malloc [2]? 1

  main:    [init] pcptr       = (nil)

  Taking branch 1.
  fnc_A:          pcptr       = 0x8fff008
                  pcptr->code = (nil)
  Segmentation fault


  ./gcc-bug
  Take the suspect branch [1] or stay `safe' [2]? 1
  Initialize `pcptr' with NULL [1] or malloc [2]? 2

  main:    [init] pcptr       = 0x8c39008

  Taking branch 1.
  fnc_A:          pcptr       = 0x8c39018
                  pcptr->code = (nil)
  main:           pcptr       = 0x8c39018 (0x8c39018 <- should be this)
                  pcptr->code = (nil)     (0x8c39018 <- should be this)


Needless to say, the nature and duration of this behavior
through the 3.X and current versions of gcc certainly seems
to go against the Principle of Least Astonishment.

------
Andris


......................................................................


#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#define PC              struct pc
typedef unsigned short  word;

PC {                    /* typical linked list structure */
    word   op;
    PC    *code;
};

PC *pcptr;              /* global structures */
PC *pcptr_copy;

int get_data(char *prompt)
{
    char buf[40];
    int result, *result_ptr;

    result_ptr = &result;
    do {
        fputs(prompt, stdout);
        fgets(buf, sizeof(buf), stdin);
        sscanf(buf, "%i", result_ptr);
    } while (result != 1 && result != 2);
    return(result);
}

PC *fnc_A(void)
{
    if ((pcptr = pcptr_copy = (PC *)malloc(sizeof(PC))) == NULL) {
        perror("malloc(3) failed");
        exit(1);
    }
    pcptr->code = NULL;
    printf("fnc_A:  \tpcptr       = %p\n", (void *)pcptr);
    printf("        \tpcptr->code = %p\n", (void *)pcptr->code);
    return(pcptr);
}

int main(int argc, char *argv[])
{
    int init_choice, branch;

    branch      = get_data("Take the suspect branch [1] or stay `safe' [2]? ");
    init_choice = get_data("Initialize `pcptr' with NULL [1] or malloc [2]? ");
    puts("");

    if (init_choice == 1)
        pcptr = NULL;
    else {
        if ((pcptr = (PC *)malloc(sizeof(PC))) == NULL) {
            perror("malloc(3) failed");
            exit(1);
        }
    }
    printf("main:    [init] pcptr       = %p\n\n", (void *)pcptr);

    printf("Taking branch %i.\n", branch);
    if (branch == 1)
        /*
         * This branch works fine with the gcc-2.95 and HP-UX compilers.
         * If compiled with gcc {3,4}.X, however, the results are:
         *
         *   > SEGFAULT if `pcptr' is initialized to NULL
         *       or
         *   > Corrupted value of `pcptr->code' if `pcptr'
         *     is initialized to a non-NULL address.
         *
         * The newer compilers appear to incorrectly handle the case
         * where the assignment target is a global pointer reference
         * (pcptr->code) and the value being assigned is returned by
         * a function which can modify the global pointer itself.
         */
        pcptr->code = fnc_A();
    else {
        /*
         * All compilers can handle this branch correctly
         * regardless of how `pcptr' is initialized.
         */
        PC *tmp_ptr;
        tmp_ptr     = fnc_A();
        pcptr->code = tmp_ptr;
    }
    printf("main:   \tpcptr       = %p", (void *)pcptr);
    printf("\t(%p <- should be this)\n", (void *)pcptr_copy);
    printf("        \tpcptr->code = %p", (void *)pcptr->code);
    printf("\t(%p <- should be this)\n", (void *)pcptr_copy);
    return(0);
}

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: gcc-2.95 OK, gcc-{3,4}.X not OK
  2010-01-07 22:37     ` Andris Kalnozols
@ 2010-01-08  7:55       ` Cedric Roux
  2010-01-08 10:46       ` Andrew Haley
  1 sibling, 0 replies; 5+ messages in thread
From: Cedric Roux @ 2010-01-08  7:55 UTC (permalink / raw)
  To: Andris Kalnozols; +Cc: gcc-help

Andris Kalnozols wrote:
> There are no coding nor compilation errors with either
> 
>   pcptr = pcptr->code = nop;
>     or
>   pcptr->code = pcptr = nop;
> 
> As long as the pointers are actually pointing to something,
> this is typical linked list processing.

Well, this is debatable.
If by:
   pcptr->code = pcptr = nop;
you mean:
   pcptr = nop;
   pcptr->code = nop;
(assuming nop == NULL)
then in the second assignment pcptr is NULL,
you dereference a NULL pointer. (And you're
lucky you get a segmentation fault, on some
architectures you have no crash at all and
left with a funky bug.)

>         pcptr->code = fnc_A();

What does that mean in your head?
fnc_A modifies pcptr, so you expect
pcptr in pcptr->code to be the value
before the call or after?

In a hypothetical assembly language, your statement could be:

   load  (pcptr), reg1
   add   #offset(code in PC), reg1
   call  fnc_A
   store regret, (reg1)

Or do you expect:

   call  fnc_A
   load  (pcptr), reg1
   add   #offset(code in PC), reg1
   store regret, (reg1)

This is quite different. I don't know what gcc does, but
I know your pattern is weird and I guess you
should avoid it in production code. It will
give some headaches to those who will need
to maintain the code.

Cédric.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: gcc-2.95 OK, gcc-{3,4}.X not OK
  2010-01-07 22:37     ` Andris Kalnozols
  2010-01-08  7:55       ` Cedric Roux
@ 2010-01-08 10:46       ` Andrew Haley
  1 sibling, 0 replies; 5+ messages in thread
From: Andrew Haley @ 2010-01-08 10:46 UTC (permalink / raw)
  To: Andris Kalnozols; +Cc: gcc-help

On 01/07/2010 10:37 PM, Andris Kalnozols wrote:
>>> On 01/03/2010 04:54 AM, Andris Kalnozols wrote:
>>
>>> Since multiple assignments are legal and evaluated from right
>>> to left, one could expect the following to work:
>>>
>>>   pcptr->code = pcptr = nop;
>>
>> On 01/03/2010 11:17 AM, Andrew Haley replied:
>>
>> I wouldn't.  :-)
> 
> OK, the problem was found, not by Valgrind nor Coverity, but the
> old fashioned method of inserting debugging statements in the code.
> 
> There are no coding nor compilation errors with either
> 
>   pcptr = pcptr->code = nop;
>     or
>   pcptr->code = pcptr = nop;
> 
> As long as the pointers are actually pointing to something,
> this is typical linked list processing.
> 
> There is a problem, however, when a global pointer structure is
> the assignment target of a function which itself may modify the
> global pointer.  The included test program gives the following
> output when compiled with gcc {3,4}.X:
> 
> 
>   ./gcc-bug
>   Take the suspect branch [1] or stay `safe' [2]? 1
>   Initialize `pcptr' with NULL [1] or malloc [2]? 1
> 
>   main:    [init] pcptr       = (nil)
> 
>   Taking branch 1.
>   fnc_A:          pcptr       = 0x8fff008
>                   pcptr->code = (nil)
>   Segmentation fault
> 
> 
>   ./gcc-bug
>   Take the suspect branch [1] or stay `safe' [2]? 1
>   Initialize `pcptr' with NULL [1] or malloc [2]? 2
> 
>   main:    [init] pcptr       = 0x8c39008
> 
>   Taking branch 1.
>   fnc_A:          pcptr       = 0x8c39018
>                   pcptr->code = (nil)
>   main:           pcptr       = 0x8c39018 (0x8c39018 <- should be this)
>                   pcptr->code = (nil)     (0x8c39018 <- should be this)
> 
> 
> Needless to say, the nature and duration of this behavior
> through the 3.X and current versions of gcc certainly seems
> to go against the Principle of Least Astonishment.

I'm sorry, this comment is too opaque for me to understand.  gcc
doesn't seem to be doing anything wrong.  The order of evaluation of
subexpressions is unspecified, sure, but that's how C has always
worked.  What is astonishing about it?

Andrew.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2010-01-08 10:46 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <4B3DE0EA.5060302@redhat.com>
2010-01-03  4:55 ` gcc-2.95 OK, gcc-{3,4}.X not OK Andris Kalnozols
2010-01-03 11:17   ` Andrew Haley
2010-01-07 22:37     ` Andris Kalnozols
2010-01-08  7:55       ` Cedric Roux
2010-01-08 10:46       ` Andrew Haley

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).