-
Notifications
You must be signed in to change notification settings - Fork 14.9k
Not planned
Labels
Description
Both Clang and GCC do not issue any warnings about the following code, even with the -Wall option.
For Clang, I used Clang-16.
The executable produced by GCC caused a segmentation fault, while Clang's always prints 1.
By using the result of the cast, it becomes possible to access a struct member that was never actually allocated, and using it can easily cause a segmentation fault.
To avoid this, I propose that casting a pointer from a pointee type with a smaller size to one with a larger size should produce a compiler warning.
#include <stdio.h>
struct X {
int x;
};
struct Y {
int x;
int y;
};
int main(void) {
struct X x = {0};
struct Y* y = (struct Y*) &x;
y->y = 1;
printf("%d", y->y);
return 0;
}
Metadata
Metadata
Assignees
Labels
Type
Projects
Milestone
Relationships
Development
Select code repository
Activity
EugeneZelenko commentedon Jun 22, 2025
Could you please try 20 or
main
branch? Please also try Clang Static Analyzer and Address Sanitizer. https://godbolt.org should be helpful.Explorer09 commentedon Jun 22, 2025
Is it just me, or does this look like a non-issue?
For the example code given, there's an explicit cast to
(struct Y*)
with which the programmer should take full responsibility with. The code already violates the strictest aliasing rule. If the violation of strictest aliasing rule doesn't inform you anything, I don't know what else can.Alcaro commentedon Jun 22, 2025
That sounds like it'd throw an obscene amount of uninteresting warnings for, for example, every function that takes a polymorphic argument (like struct sockaddr*), and every use of Linux kernel container_of.
While this specific example would be correct to complain about, it sounds difficult to tell apart from legitimate casts.
wo4mei3 commentedon Jun 23, 2025
At first, I didn’t realize that warning on every pointer cast from a smaller type to a bigger one would cause unnecessary warnings even for safe downcasts.
I think it’s correct to warn for the example I gave, but I also get that for common cases like struct sockaddr*, it would cause a lot of false positives.
After switching to clang-20, I tried running the static analyzer using scan-build, but perhaps because of the reasons mentioned above, no warnings were generated.
On the other hand, the address sanitizer did point out issues.
In response to this, I’d like to propose adding a new variable attribute, attribute((strict_size_cast)). This attribute can be applied to pointer variables or function parameters. When used, the compiler would warn or error on pointer casts from smaller-sized types to larger-sized types, helping to catch potential issues by enforcing stricter checks.
For example, in the case I gave, it would look like this.
Thank you for your consideration.
Explorer09 commentedon Jun 23, 2025
My concern is, why? Why introduce another attribute where there is no real world use case to back it up? Introducing a warning without real world uses could annoy programmers, forcing them to find a way to silence the warning when they are sure the code is correct (or when they intend to write such an umsafe code). The benefit for such a warning might not be worth it.
Another concern is, it's not just the structure size that could cause the problem. Reinterpreting a structure as another structure type would be also undefined behavior unless the structures are part of the
union
. Example:The C standard had that "strict aliasing rule" that is meant to address this, and it makes no sense to introduce another warning.
wo4mei3 commentedon Jun 23, 2025
In Linux, the -fno-strict-aliasing compiler option is deliberately specified.
The strict aliasing rule has been mentioned several times in this issue, but in practical software development, it is merely an assumption made by language specification for the sake of optimization, and is largely disconnected from reality.
Even Clang’s own documentation describes this rule as “impractically strict,” yet many people still place undue trust in it.
As is evident from Clang’s implementation, the compiler does not issue any warnings for code that violates the strict aliasing rule. Furthermore, Clang’s static analysis component—particularly the lib/Sema module—does not contain any mechanism to check whether code conforms to this rule.
While the -Wstrict-aliasing option does exist, it is maintained solely for compatibility with GCC, and in practice, it has no effect.
In fact, there are many real-world projects that disable optimizations based on the strict aliasing rule. One prominent example is the Linux kernel, which explicitly adds -fno-strict-aliasing to its KBUILD_CFLAGS.
Given this context, it would be useful to have a way to explicitly indicate the safety of operations—particularly those that involve casting from smaller to larger types, which are likely to directly cause segmentation faults.
Source code is read not only by developers, but also by various stakeholders. Users may inspect the code, and some readers may not build or execute it at all. In such cases, an annotation—such as a strict_size_cast attribute—could help clarify the intent of the code.
If such an attribute is attached to a pointer variable, it would serve as an explicit indication that all casts involving that variable are guaranteed to be safe. This allows readers to confidently interpret the code as intended. In other words, such an attribute could function as a form of static assurance, improving both the reliability and readability of the source code.
thesamesam commentedon Jun 23, 2025
My deleted comment (as I wanted to check something) is that Clang has
-fsanitize=type
now, but it doesn't detect this case unfortunately. It'd be worth filing a new bug about that.Where?
wo4mei3 commentedon Jun 23, 2025
I appreciate the additional information. While it is encouraging that -fsanitize=type is available, it’s unfortunate that this specific case is not currently detected. Submitting a new bug report sounds like a constructive next step to enhance tooling around type safety.
The phrase appears here.
llvm-project/clang/lib/CodeGen/CodeGenTBAA.cpp
Line 237 in cfcb788
Explorer09 commentedon Jun 23, 2025
As I said, your example about structure size should have been warned as a "strict aliasing" violation. And so an additional warning for this very specific use case is not gonna be helpful.
Another thing to be aware of is: There are valid uses for the cast from a smaller struct to a larger one, so adding a warning for this could produce lots of false positives that downstream programmer would have to waste time trying to silence them.
Then the bug should be about implementing
-Wstrict-aliasing
and not an additional, unrelated warning.The ideal situation is that
-Wstrict-aliasing
can take an integer option argument so that the project can decide how strict the warnings should be checked. For example3
might be pedantically strict,2
could be tolerent for some structure pointer casts as long as the fields are compatible (maybe useful for Linux kernel as well),1
might be structure size checks only and would allow reinterpretation of fields withoutunion
.The C language had the
union
for the purpose. I can't see the necessity for an additional attribute.thesamesam commentedon Jun 23, 2025
No, that's commentary about the extent of the rule that Clang chooses to implement and how it interprets it. Not user-facing documentation about the rules overall being impractical or something like that.
wo4mei3 commentedon Jun 23, 2025
Having attributes or annotations to explicitly indicate “this cast is safe” without relying on strict aliasing would be highly practical in real-world scenarios.
For projects that strictly adhere to the strict aliasing rule, using unions may indeed be the only way to guarantee safety. However, in practice, many projects disable strict aliasing, making the use of unions redundant and detrimental to readability and maintainability.
Even in projects that do not assume strict aliasing, is it really practical to define a union type every time a cast is needed? Many C projects implicitly treat struct types defined separately in different files or modules—types that are technically incompatible under the language specification—as if they had an is-a relationship. This implies that a large amount of existing code already violates the strict aliasing rule.
Moreover, casting between such types by defining ad-hoc union types locally, assigning members, and then accessing the desired member is cumbersome and goes far beyond just silencing compiler warnings.
If there were an annotation-based mechanism to explicitly indicate safety, simply adding an attribute to the pointer declaration would suffice, making the intent clearer and greatly improving code readability.
Attributes are entirely optional and will not trigger warnings unless explicitly used. If warnings do occur, they serve as appropriate feedback on misuse and do not indicate a fault in the attribute mechanism itself. In fact, using attributes clarifies that “this cast is safe,” which is superior in readability and intent declaration compared to workarounds involving unions. Avoiding the adoption of attributes is a decision that may compromise both developer convenience and code safety.
For example, projects like QEMU, which explicitly compile with -fno-strict-aliasing, frequently use the void* opaque pattern. Although the actual struct type passed is known, function accepts the argument as a void* pointer and then cast to the intended struct type inside the function—this is a common and practical design approach.
Given such real-world design practices, strictly enforcing -Wstrict-aliasing warnings in projects that do not assume strict aliasing may not necessarily be the right approach.
wo4mei3 commentedon Jun 23, 2025
I understand, I'm sorry for misunderstanding.
wo4mei3 commentedon Jun 23, 2025
I realized, even while writing it, that something felt off. Upon further reflection, I now understand that the attribute I proposed cannot handle patterns like those seen in QEMU.
It makes sense now using a union is the most correct approach, even in situations where strict aliasing is not assumed.
I finally see it clearly now, and I sincerely apologize for the confusion and trouble I may have caused. It’s unfortunate that I didn’t recognize this flaw myself before opening the issue. I’m truly grateful for your guidance.
This incident has given me a lot to reflect on, and I’m determined to be more careful and thoughtful in my future actions. I deeply appreciate the responses from everyone, especially given how slow I was to understand. I’m sorry for taking up your time.
Thank you again for your patience and for pointing me in the right direction.
Best regards,
Alcaro commentedon Jun 23, 2025
certainly is cumbersome, but you can cram it into a macro to make it slightly less bulky. https://godbolt.org/z/1PoxEb6TK
(Requires the GCC statement expression extension, but anything involving __attribute__ is equally nonstandard.)
Explorer09 commentedon Jun 23, 2025
Just a personal opinion of mine: I do believe the strict aliasing rule in C is overly strict in some situations. An example is casting between structures with common sub-structures that define "is-a" relationships between.
I believe such use would be common (my example is modeled with inspiration from PCI configuration space). And yet the warning proposed isn't about this.
By "safety" do you mean "it is safe to cast from A to B" or "it is safe to cast from A to any other pointer type"? There are semantic differences.
I think the strict aliasing rule not covering
void *
use is a mistake. GCC allowsvoid *
to alias as an extension. Not sure if this would be made into the C standard.wo4mei3 commentedon Jun 24, 2025
I searched on the internet and found many people saying that using void* for aliasing is allowed, like with char*.
But I’m not sure if this is part of the C standard or just something supported by GCC.
I understand that this doesn’t work in all situations, such as the PacketHeader case.
If the attributes given can limit type casting in some cases from smaller types to larger types, then maybe we could avoid segmentation faults caused by accessing unallocated memory.
I was surprised by the example using statement expressions.
Using typeof in macros with statement expression is shown in the GCC statement expression documentation, and it seems that using typeof in macros is a common trick
Explorer09 commentedon Jun 24, 2025
The point is, there are legitimate cases of accessing what you called "unallocated" memory. The C language was designed that programmers can do certain memory operations that are considered unsafe in more modern languages. And thus the ability to segfault like this is never a bug.
wo4mei3 commentedon Jun 24, 2025
You're right.
C was definitely designed with a lot of low-level flexibility in mind,
and that includes operations which would be considered unsafe in modern languages.
My intention was to distinguish between
"intentionally unallocated but used"
and "accidentally unallocated" memory due to logic errors or invalid casts.
The former is fine with proper care,
but the latter often indicates a deeper bug or design issue.