[llvm-dev] broken C code only when optimized "-O2"

Adrian Moreno via llvm-dev llvm-dev at lists.llvm.org
Tue Dec 21 08:30:04 PST 2021


Hello,

I need some help understanding what might be wrong with a piece of code from the 
openvswitch project. By ${subject} I'm not suggesting there's a problem in 
clang, gcc also shows the same behavior so it's likely our code is broken. I am 
kindly asking for help to understand/troubleshoot the problem.

Summary: It seems that certain interaction between two main openvswitch data 
structures, when optimized ("-O2 -flto=auto") is broken.
The two data structures are:

hmap: https://github.com/openvswitch/ovs/blob/master/include/openvswitch/hmap.h
list: https://github.com/openvswitch/ovs/blob/master/include/openvswitch/list.h

I've reproduced the problem outside of openvswitch daemon using a short C 
program (attached)

Code snippet:

struct bond {
     struct hmap members;
};

struct member {
     struct hmap_node hmap_node;
     int order;
     struct ovs_list elem;
};

int main() {
     int ret = 0;
     struct member *member, *member1, *member2;
     struct bond *bond;
     struct ovs_list start = {0};

     bond = malloc(sizeof *bond);
     memset(bond, 0, sizeof (struct bond));
     hmap_init(&bond->members);

     member1 = malloc(sizeof *member1);
     member2 = malloc(sizeof *member2);
     memset(member1, 0, sizeof (struct member));
     memset(member2, 0, sizeof (struct member));

     member1->order = 3;
     member2->order = 2;

     hmap_insert(&bond->members, &member1->hmap_node, (uint32_t)(uintptr_t)member1);
     hmap_insert(&bond->members, &member2->hmap_node, (uint32_t)(uintptr_t)member2);

     ovs_list_init(&start);
     HMAP_FOR_EACH (member, hmap_node, &bond->members) {
        /*
         * Insert member in start (sorted)
         * */
        struct member *pos;
        LIST_FOR_EACH (pos, elem, &start) {
            if (member->order > pos->order) {
                break;
            }
        }
        // TESTED: If I add this printf, the problem disappears
        //printf("Inserting member: %p\n", member);
        ovs_list_insert(&pos->elem, &member->elem);
     }

     /* I've inserted two members into the 'start' list.
      *   first and last have to be either member1 or member2
      * */
     if ((first != member1 && first != member2) || (last != member1 && last != 
member2)) {
         printf("list is broken!\n");
     }

}


What I know for now:
* -fno-strict-aliasing does not fix it
* Only happens with "-O2 -flto=auto"
* If I define 'ovs_list *start' and change the code to use the pointer directly 
and not '&start' the problem disappears. It seems that the LIST_FOR_EACH macros 
prefer an lvalue rather than "&" but I don't get why.
* I'm not able to reproduce without using hmap _and_ ovs_list.
* If I add a compiler barrier (or a call to an external function) after the 
loop, the problem disappears (e.g printf), the problem disappears.
* If I add -fsanitize=undefined the problem disappears!

I'd really appreciate any hint or idea to try to understand this problem.

Thanks in advanced.

-- 
Adrián Moreno
-------------- next part --------------
A non-text attachment was scrubbed...
Name: example.c
Type: text/x-csrc
Size: 3876 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20211221/65316a4a/attachment.c>


More information about the llvm-dev mailing list