gcc-2.95 OK, gcc-{3,4}.X not OK

Andris Kalnozols andris@hpl.hp.com
Thu Jan 7 22:37:00 GMT 2010


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



More information about the Gcc-help mailing list