Gcc became recently (4.4) very bad regarding false positive type-punned warnings. In previous versions, the warnings could be suppressed by casting to (void *), in 3.x and 4.1 it worked perfectly, in 4.3 it still worked somehow (except in -Wstrict-aliasing=3 mode), in 4.4 there are cases where it doesn't work at all. I don't want to completely disable the warnings with -Wno-strict-aliasing (this could leave bugs unnotified), but I need a method to disable them on case-by-case basis once I verified that the code in question is correct. Simple example, compile with -O2 -Wall: extern int c; int a(void) { return *(short *)(void *)&c; } In 4.4 the warning can't be disabled at all! The (void *) cast doesn't suppress the warning and none of three options to -Wstrict-aliasing helps. In 4.3 the cast to (void *) suppressed the warning in -Wstrict-aliasing 1,2 modes (and didn't suppress it in the default mode 3), in 4.4 the warning can't be suppressed at all. Gcc developers tried to made these warnings more "intelligent" with less false positives, but unfortunatelly they completely broke the method to disable them in the specific case. For me, false positives are not a major problem --- when I get a false positive, I just read the code, check it and if I conclude that it's OK, I disable the warning with (void *). But if there's no way to disable false positives, it makes the warnings completely useless.
Created attachment 18145 [details] a bug in -Wstrict-aliasing=3 This is an example of a flaw in -Wstrict-aliasing=3 (this mode is very bad, produces many false positives on my project and I'm wondering why is it default?) Gcc man page says that -Wstrict-aliasing=3 produces less false positives than -Wstrict-aliasing=2. This is counterexample, it produces "type-punned" warning in -Wstrict-aliasing=3 mode and doesn't warn in -Wstrict-aliasing=2. I added (void *) casts everywhere, but they don't quash the warning.
Created attachment 18146 [details] a bug in -Wstrict-aliasing=3 This is an example of a flaw in -Wstrict-aliasing=3 (this mode is very bad, produces many false positives on my project and I'm wondering why is it default?) Gcc man page says that -Wstrict-aliasing=3 produces less false positives than -Wstrict-aliasing=2. This is counterexample, it produces "type-punned" warning in -Wstrict-aliasing=3 mode and doesn't warn in -Wstrict-aliasing=2. I added (void *) casts everywhere, but they don't quash the warning.
Subject: Re: New: dereferencing type-punned pointer warnings cannot be disabled Sent from my iPhone On Jul 6, 2009, at 6:12 PM, "mikulas at artax dot karlin dot mff dot cuni dot cz" <gcc-bugzilla@gcc.gnu.org> wrote: > Gcc became recently (4.4) very bad regarding false positive type- > punned > warnings. In previous versions, the warnings could be suppressed by > casting to > (void *), in 3.x and 4.1 it worked perfectly, in 4.3 it still worked > somehow > (except in -Wstrict-aliasing=3 mode), in 4.4 there are cases where > it doesn't > work at all. > > I don't want to completely disable the warnings with -Wno-strict- > aliasing (this > could leave bugs unnotified), but I need a method to disable them on > case-by-case basis once I verified that the code in question is > correct. > > Simple example, compile with -O2 -Wall: > > extern int c; > int a(void) > { > return *(short *)(void *)&c; > } This is a very bad example of a false positive as you are acessing an int as a short; that is undefined. I will look at your code later on, my laptop for home is currently broken. > > In 4.4 the warning can't be disabled at all! The (void *) cast > doesn't suppress > the warning and none of three options to -Wstrict-aliasing helps. In > 4.3 the > cast to (void *) suppressed the warning in -Wstrict-aliasing 1,2 > modes (and > didn't suppress it in the default mode 3), in 4.4 the warning can't be > suppressed at all. > > Gcc developers tried to made these warnings more "intelligent" with > less false > positives, but unfortunatelly they completely broke the method to > disable them > in the specific case. For me, false positives are not a major > problem --- when > I get a false positive, I just read the code, check it and if I > conclude that > it's OK, I disable the warning with (void *). > > But if there's no way to disable false positives, it makes the > warnings > completely useless. > > > -- > Summary: dereferencing type-punned pointer warnings cannot > be > disabled > Product: gcc > Version: 4.4.1 > Status: UNCONFIRMED > Severity: major > Priority: P3 > Component: regression > AssignedTo: unassigned at gcc dot gnu dot org > ReportedBy: mikulas at artax dot karlin dot mff dot cuni dot cz > GCC build triplet: i686-pc-linux-gnu > GCC host triplet: i686-pc-linux-gnu > GCC target triplet: i686-pc-linux-gnu > > > http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40665 >
Subject: Re: dereferencing type-punned pointer warnings cannot be disabled Sent from my iPhone On Jul 6, 2009, at 6:34 PM, "mikulas at artax dot karlin dot mff dot cuni dot cz" <gcc-bugzilla@gcc.gnu.org> wrote: > > > ------- Comment #2 from mikulas at artax dot karlin dot mff dot cuni > dot cz 2009-07-07 01:34 ------- > Created an attachment (id=18146) > --> (http://gcc.gnu.org/bugzilla/attachment.cgi?id=18146&action=view) > a bug in -Wstrict-aliasing=3 > > This is an example of a flaw in -Wstrict-aliasing=3 (this mode is > very bad, > produces many false positives on my project and I'm wondering why is > it > default?) > > Gcc man page says that -Wstrict-aliasing=3 produces less false > positives than > -Wstrict-aliasing=2. This is counterexample, it produces "type- > punned" warning > in -Wstrict-aliasing=3 mode and doesn't warn in -Wstrict-aliasing=2. > I added > (void *) casts everywhere, but they don't quash the warning. > > > -- > > > http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40665 >
Subject: Re: dereferencing type-punned pointer warnings cannot be disabled Thus code is undefined you have an acess of a char array as a struct. Yes you are only taking the address of an element but it is still considered an acess by the standards. Sent from my iPhone On Jul 6, 2009, at 6:34 PM, "mikulas at artax dot karlin dot mff dot cuni dot cz" <gcc-bugzilla@gcc.gnu.org> wrote: > > > ------- Comment #2 from mikulas at artax dot karlin dot mff dot cuni > dot cz 2009-07-07 01:34 ------- > Created an attachment (id=18146) > --> (http://gcc.gnu.org/bugzilla/attachment.cgi?id=18146&action=view) > a bug in -Wstrict-aliasing=3 > > This is an example of a flaw in -Wstrict-aliasing=3 (this mode is > very bad, > produces many false positives on my project and I'm wondering why is > it > default?) > > Gcc man page says that -Wstrict-aliasing=3 produces less false > positives than > -Wstrict-aliasing=2. This is counterexample, it produces "type- > punned" warning > in -Wstrict-aliasing=3 mode and doesn't warn in -Wstrict-aliasing=2. > I added > (void *) casts everywhere, but they don't quash the warning. > > > -- > > > http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40665 >
(In reply to comment #5) > > Thus code is undefined you have an acess of a char array as a struct. > Yes you are only taking the address of an element but it is still > considered an acess by the standards. I see that users repeatedly fall for this. Could we detect what type is accessed as what and provide an informative note? Something like: warning: dereferencing type-punned pointer might break strict-aliasing rules note: accessing 'char *' as 'structure *' is undefined Andrew, do you think this would be difficult to implement?
> > extern int c; > > int a(void) > > { > > return *(short *)(void *)&c; > > } > > This is a very bad example of a false positive as you are acessing an > int as a short; that is undefined. I will look at your code later on, > my laptop for home is currently broken. Whether it is a false positive or not depends on the context. For example, if I call function a() from function b(): int b(void) { c = 0x12345678; __asm__ volatile ("":::"memory"); return a(); } the code is valid and the function b() must return a fixed value depending on the endianity of the machine. That __asm__ statement works as a barrier that prevents the compiler from reordering two accesses to "c". So I am not against the warning. The warning is good. The problem is that there is no way to shut up the warning.
> Thus code is undefined you have an acess of a char array as a struct. > Yes you are only taking the address of an element but it is still > considered an acess by the standards. Why is it undefined? "An object shall have its store value accessed only by an lvalue that has one of the following types ... * a character type" So you say that converting the char * pointer to struct * pointer is understood as "accessing the stored value" by the standard? The only possible problem could be some hypothetical computer that cannot hold misaligned pointers to structs (common computers allow unaligned pointers and trap only on dereferencing unaligned pointers, not on generating them). But once I know that I have a computer that allows unaligned pointers to structs, there should be a method how to shut the warning up (with (void *) cast).
>So you say that converting the char * pointer to struct * pointer is understood as "accessing the stored value" by the standard? No. Let's look at the code: char buffer[512]; (void *)&((struct structure *)(void *)buffer)->x You are accessing a character array via a "struct structure" and then taking the address. This why it is undefined. Even though it does not look like an access in the assembly as it is &a->b is an addition, it is an access according to the C/C++ standard. Also aliasing is not transitive, that is you can access anything via a character type but you cannot access a character by everything (only character types themselves). >the code is valid and the function b() must return a fixed value depending on the endianity of the machine. That __asm__ statement works as a barrier that prevents the compiler from reordering two accesses to "c". Actually even with the memory barrier is still undefined according to the C/C++ standards, it does not mean we won't do what you expect but it means we don't have to do what you expect it to do. This is why we warn about it.
So you mean that that ->x operator is invalid and break the standard? Anyway the standard means "if you write your code according to the standard => the code will run correctly", but the inverse implication doesn't apply. It is sometimes required to break the C standard. For example, you write "unsigned char *framebuffer = vga_getgraphmem();" and now you want to access the framebuffer. According to the standard, you could only do it by bytes. But that means one bus cycle to the videocard for every byte transfered. So people understand that common processors allow aligned accesses to 2, 4 or 8 bytes and that common videocards have their framebuffer base address aligned on same larger boundary --- and they simply cast the pointer to u_int32_t or u_int64_t and access the videoram faster. Each time you watch some video, remember those undefined memory accesses that are hapenning for you to get faster performance :) Another example --- the C standard says how it's not allowed to even produce a pointer that points before the allocated array or more than one entry after the last entry of the array and how it's not allowed to subtract two pointers from different arrays. It is perfectly rational --- unless you are writing the memory allocator itself! Then you inevitably must do some operations that are considered "undefined" by the standard. Regarding that int * to short * cast --- obviously, the code may run on some computer that has tagged memory and traps if access with invalid type is done. But common computers don't have tagged memory and the programmer should be allowed to do such casts if he understands the implications (for example, if its done only in arch-specific part of an operating system, it is perfectly legal). So there should be a method to silence the warning.
(In reply to comment #10) > For example, you write "unsigned char *framebuffer = vga_getgraphmem();" and > now you want to access the framebuffer. According to the standard, you could > only do it by bytes. But that means one bus cycle to the videocard for every > byte transfered. So people understand that common processors allow aligned > accesses to 2, 4 or 8 bytes and that common videocards have their framebuffer > base address aligned on same larger boundary --- and they simply cast the > pointer to u_int32_t or u_int64_t and access the videoram faster. Each time you > watch some video, remember those undefined memory accesses that are hapenning > for you to get faster performance :) Not fully, you missed part of the standard that takes about the effective type. Basically if there is no declared type for an object, the effective type is the type which is used for storing and then only that can be read back with that type or character type or the other cases the standard mentions. The effective type changes when another store happens. In both of original cases in this bug report, there is a declared type which is also the effective type. And it is not about the cast between the pointer types which causes it to be undefined but rather the accesses.
So if there was char *buffer = malloc(512) instead of char buffer[512], would it be correct to cast it to the pointer to structure? > And it is not about the cast between the pointer types which causes it to be > undefined but rather the accesses. What is considered the "access" in that code? Is it the "->" operator? "&" operator? "=" operator? Cast operator? Or anything else?
(In reply to comment #12) > So if there was char *buffer = malloc(512) instead of char buffer[512], would > it be correct to cast it to the pointer to structure? Yes. > > And it is not about the cast between the pointer types which causes it to be > > undefined but rather the accesses. > > What is considered the "access" in that code? Is it the "->" operator? "&" > operator? "=" operator? Cast operator? Or anything else? The following three operators are causes an access to happen even if & is used afterwards: "->", ".", and "*" Thanks, Andrew PInski