RHEL Engineering is moving the tracking of its product development work on RHEL 6 through RHEL 9 to Red Hat Jira (issues.redhat.com). If you're a Red Hat customer, please continue to file support cases via the Red Hat customer portal. If you're not, please head to the "RHEL project" in Red Hat Jira and file new tickets here. Individual Bugzilla bugs in the statuses "NEW", "ASSIGNED", and "POST" are being migrated throughout September 2023. Bugs of Red Hat partners with an assigned Engineering Partner Manager (EPM) are migrated in late September as per pre-agreed dates. Bugs against components "kernel", "kernel-rt", and "kpatch" are only migrated if still in "NEW" or "ASSIGNED". If you cannot log in to RH Jira, please consult article #7032570. That failing, please send an e-mail to the RH Jira admins at rh-issues@redhat.com to troubleshoot your issue as a user management inquiry. The email creates a ServiceNow ticket with Red Hat. Individual Bugzilla bugs that are migrated will be moved to status "CLOSED", resolution "MIGRATED", and set with "MigratedToJIRA" in "Keywords". The link to the successor Jira issue will be found under "Links", have a little "two-footprint" icon next to it, and direct you to the "RHEL project" in Red Hat Jira (issue links are of type "https://issues.redhat.com/browse/RHEL-XXXX", where "X" is a digit). This same link will be available in a blue banner at the top of the page informing you that that bug has been migrated.
Bug 2109065 - g++ -O2 compiles buggy code with union aliasing.
Summary: g++ -O2 compiles buggy code with union aliasing.
Keywords:
Status: CLOSED UPSTREAM
Alias: None
Product: Red Hat Enterprise Linux 9
Classification: Red Hat
Component: gcc
Version: CentOS Stream
Hardware: x86_64
OS: Linux
unspecified
high
Target Milestone: rc
: ---
Assignee: Marek Polacek
QA Contact: qe-baseos-tools-bugs
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2022-07-20 11:16 UTC by Kirby Zhou
Modified: 2024-05-31 13:23 UTC (History)
7 users (show)

Fixed In Version:
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2022-07-22 13:17:43 UTC
Type: Bug
Target Upstream Version:
Embargoed:
pm-rhel: mirror+


Attachments (Terms of Use)
The buggy demo code. (4.98 KB, text/x-csrc)
2022-07-20 11:16 UTC, Kirby Zhou
no flags Details


Links
System ID Private Priority Status Summary Last Updated
GNU Compiler Collection 106389 0 P2 UNCONFIRMED [11/12/13 Regression] IPA modref breaks Safe Bitfields in C++ 2022-07-21 17:25:15 UTC
Red Hat Issue Tracker RHELPLAN-128416 0 None None None 2022-07-20 11:29:40 UTC

Description Kirby Zhou 2022-07-20 11:16:48 UTC
Created attachment 1898267 [details]
The buggy demo code.

Description of problem:

g++ -O2 compiles buggy code with union aliasing.

I extracted a code fragment to demo the bug.
The code is from https://github.com/preshing/cpp11-on-multicore which used by apache-pegasus and many other tools.


Version-Release number of selected component (if applicable):

gcc-c++-11.3.1-2.1.el9 
gcc-c++-11.2.1-9.4.el9

How reproducible:

100%


Steps to Reproduce:

1. download the attachement as bitbug.cpp
2. g++ -O2 bitbug.cpp && ./a.out 


Actual results:

good: 00000001 expect 00000001
bad : 00100001 expect 00000001

Expected results:

good: 00000001 expect 00000001
bad : 00000001 expect 00000001

Additional info:

The buggy code is:

            newStatus = oldStatus;
            newStatus.writers-=1;
            waitToRead = oldStatus.waitToRead;
            if (waitToRead > 0) {
                newStatus.waitToRead = 0;
                newStatus.readers = waitToRead;
            }
            m_status = newStatus;


The buggy assembly like:

mov    %eax,%edx
lea    -0x100000(%rax),%ecx
and    $0xfff00000,%eax
shr    $0xa,%edx
and    $0x3ff,%edx
or     %edx,%eax
test   %edx,%edx
cmove  %ecx,%eax

"lea    -0x100000(%rax),%ecx" is related to  "newStatus.writers-=1".
But %ecx is not merged to eax which related to "newStatus.readers = waitToRead".

So if waitToRead>0 then newStatus.writers will not be changed.

Comment 1 Kirby Zhou 2022-07-20 11:24:19 UTC
The bug happens at both x86_64 and aarch64

The bug wont happens at gcc-c++-10.3.1-1.2.el8_5

Comment 2 Marek Polacek 2022-07-21 01:37:17 UTC
This changed with https://gcc.gnu.org/git/?p=gcc.git;a=commitdiff;h=d119f34c952f8718fdbabc63e2f369a16e92fa07, so the old behavior can be brought by -fno-ipa-modref.

However, I think the program invokes undefined behavior.  For clarity, this is the union after
preprocessing:

union Status {
  struct Wrapper { uint32_t value; };
  Wrapper wrapper;
  Status(uint32_t v = 0) { wrapper.value = v; } 
  Status &operator=(uint32_t v) { wrapper.value = v; return *this; }
  operator uint32_t &() { return wrapper.value; }
  operator uint32_t() const { return wrapper.value; }
  typedef uint32_t StorageType;
  BitFieldMember<StorageType, 0, 10> readers;
  BitFieldMember<StorageType, 10, 10> waitToRead;
  BitFieldMember<StorageType, 20, 10> writers;
};

so this union has 4 members.  [class.union] says that "At most one of the
non-static data members of an object of union type can be active at any time,
that is, the value of at most one of the non-static data members can be stored
in a union at any time."

Then the program does this:

    Status status; // activates .wrapper
    status.writers = 1; // activates .writers
    status.waitToRead = 1; // activates .waitToRead
    NonRecursiveRWLock lock;
    lock.m_status = status; // invokes Status::operator uint32_t&, which uses the non-active member .wrapper, which is UB

So I think this program is invalid.

Comment 3 Kirby Zhou 2022-07-21 04:06:27 UTC
But the union is actually a union of bitfields.

union {
   int value;
   struct {
      int readers : 10;
      int waitToRead : 10;
      int writers : 10;
   };
}


And this bit-field structure pattern is widely spread in years referred as "Safe Bitfields in C++".

Such as:

https://preshing.com/20150324/safe-bitfields-in-cpp/
https://zditect.com/code/cpp/safe-bitfields-in-c.html
https://m.blog.naver.com/framkang/220414843423
https://www.reddit.com/r/programming/comments/3i6q2w/safe_bitfields_in_c/
https://www.intensedebate.com/people/UUD
https://gfjiangly.github.io/cpu_parallel/you-can-do-any-kind-of-atomic-read-modify-write-operation.html

So if gcc-11 breaks this pattern, a lot of existing code will be hurt.

Comment 4 Marek Polacek 2022-07-21 12:43:50 UTC
Ah, sorry; I missed that BitFieldMember and Wrapper actually *are* standard-layout classes, therefore the exception
about a common initial sequence (here, a uint32_t) in [class.union.general] kicks in.

I'm going to file an upstream bug, sorry again and thanks for the report.

Comment 6 Marek Polacek 2022-07-21 17:25:16 UTC
Upstream PR opened.

Comment 7 Marek Polacek 2022-07-22 13:17:43 UTC
We have to fix this upstream first.  A workaround is to use -fno-strict-aliasing.

Comment 8 Kirby Zhou 2023-06-21 09:31:31 UTC
(In reply to Marek Polacek from comment #4)
> Ah, sorry; I missed that BitFieldMember and Wrapper actually *are*
> standard-layout classes, therefore the exception
> about a common initial sequence (here, a uint32_t) in [class.union.general]
> kicks in.
> 
> I'm going to file an upstream bug, sorry again and thanks for the report.

Is there any progress in upstream?

Comment 9 Marek Polacek 2023-06-21 10:59:21 UTC
(In reply to Kirby Zhou from comment #8)
> (In reply to Marek Polacek from comment #4)
> > Ah, sorry; I missed that BitFieldMember and Wrapper actually *are*
> > standard-layout classes, therefore the exception
> > about a common initial sequence (here, a uint32_t) in [class.union.general]
> > kicks in.
> > 
> > I'm going to file an upstream bug, sorry again and thanks for the report.
> 
> Is there any progress in upstream?

It looks like the developers don't want to implement this rule; rather, users should use may_alias:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106389#c5

Comment 10 Kirby Zhou 2024-05-31 06:35:55 UTC
(In reply to Marek Polacek from comment #9)
> (In reply to Kirby Zhou from comment #8)
> > (In reply to Marek Polacek from comment #4)
> > > Ah, sorry; I missed that BitFieldMember and Wrapper actually *are*
> > > standard-layout classes, therefore the exception
> > > about a common initial sequence (here, a uint32_t) in [class.union.general]
> > > kicks in.
> > > 
> > > I'm going to file an upstream bug, sorry again and thanks for the report.
> > 
> > Is there any progress in upstream?
> 
> It looks like the developers don't want to implement this rule; rather,
> users should use may_alias:
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106389#c5

Could you please give some example to show how to use may_alias to workaround this problem?

Comment 11 Marek Polacek 2024-05-31 13:21:03 UTC
(In reply to Kirby Zhou from comment #10)
> (In reply to Marek Polacek from comment #9)
> > (In reply to Kirby Zhou from comment #8)
> > > (In reply to Marek Polacek from comment #4)
> > > > Ah, sorry; I missed that BitFieldMember and Wrapper actually *are*
> > > > standard-layout classes, therefore the exception
> > > > about a common initial sequence (here, a uint32_t) in [class.union.general]
> > > > kicks in.
> > > > 
> > > > I'm going to file an upstream bug, sorry again and thanks for the report.
> > > 
> > > Is there any progress in upstream?
> > 
> > It looks like the developers don't want to implement this rule; rather,
> > users should use may_alias:
> > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106389#c5
> 
> Could you please give some example to show how to use may_alias to
> workaround this problem?

There's an example in https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#index-may_005falias-type-attribute
I hope it works for you.

Comment 12 Florian Weimer 2024-05-31 13:23:52 UTC
There's also a recent example in the glibcsources:

commit 8d7b6b4cb27d4dec1dd5f7960298c1699275f962
Author: Florian Weimer <fweimer>
Date:   Sat May 18 09:33:19 2024 +0200

    socket: Use may_alias on sockaddr structs (bug 19622)
    
    This supports common coding patterns.  The GCC C front end before
    version 7 rejects the may_alias attribute on a struct definition
    if it was not present in a previous forward declaration, so this
    attribute can only be conditionally applied.
    
    This implements the spirit of the change in Austin Group issue 1641.
    
    Suggested-by: Marek Polacek <polacek>
    Suggested-by: Jakub Jelinek <jakub>
    Reviewed-by: Sam James <sam>
    Reviewed-by: Carlos O'Donell <carlos>

https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=8d7b6b4cb27d4dec1dd5f7960298c1699275f962


Note You need to log in before you can comment on or make changes to this bug.