Bug 90535 - Wrong branch selected if (--(s.ptr))
Summary: Wrong branch selected if (--(s.ptr))
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: 9.1.1
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-05-19 20:05 UTC by Volker Braun
Modified: 2019-05-19 20:24 UTC (History)
0 users

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Volker Braun 2019-05-19 20:05:39 UTC
Decrementing a struct member pointer is not seen in a subsequent if() branch, so tests like 

    if (--(s.ptr)) 

jump to the wrong branch. This is with gcc 9.1.1 and -O1 or higher, -O0 works correctly. The following is a simplified testcase that was extracted from a real-world reference counting implementation:

=======================================================
#include "stdio.h"

struct MyStruct {
    void *ptr;
};


int main() {
    struct MyStruct s;
    s.ptr = NULL + 1;

    // The bug only happens if we have a pointer to s, even though we don't really use it
    struct MyStruct *struct_ptr = &s; 
    printf("MyStruct *struct_ptr = %p\n", struct_ptr);    // need to use it once

    printf("------------ ptr = 0x1 ----------------------------\n");
    printf("ptr is 0x1: %p\n", s.ptr);
    if (s.ptr) {
        printf("ptr is truthy (correct!) %p\n", s.ptr);
    }
    
    printf("------------ ptr = NULL ----------------------------\n");
    (s.ptr)--;     // We should be back to 0x0 now
    // Uncomenting the following line makes the bug disappear
    // printf("ptr is null: %p\n", s.ptr);
    if (!(s.ptr)) {
        printf("ptr is falsy (correct!) %p\n", s.ptr);
    }
    if (s.ptr) {
        printf("ptr is truthy (wrong!) %p\n", s.ptr);
    }
    return 0;
}

=======================================================

$ gcc -O1 test_char_ptr.c  && ./a.out 
MyStruct *struct_ptr = 0x7ffd0b049758
------------ ptr = 0x1 ----------------------------
ptr is 0x1: 0x1
ptr is truthy (correct!) 0x1
------------ ptr = NULL ----------------------------
ptr is truthy (wrong!) (nil)

$ gcc --version
gcc (GCC) 9.1.1 20190503 (Red Hat 9.1.1-1)
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Comment 1 Andrew Pinski 2019-05-19 20:09:21 UTC
You cant go from a normal pointer to a null pointer in well defined C code.  Gcc has -fno-delete-null-checks (I might have typed it wrong as I am writing this from my phone).
Comment 2 Andrew Pinski 2019-05-19 20:24:21 UTC
-fno-delete-null-pointer-checks is the correct option.

https://gcc.gnu.org/onlinedocs/gcc-9.1.0/gcc/Optimize-Options.html#index-fdelete-null-pointer-checks