2011-03-17 11:06:07 +08:00
// RUN: %clang_cc1 -fsyntax-only -Wuninitialized -Wconditional-uninitialized -fsyntax-only -fblocks %s -verify
Automatic variable initialization
Summary:
Add an option to initialize automatic variables with either a pattern or with
zeroes. The default is still that automatic variables are uninitialized. Also
add attributes to request uninitialized on a per-variable basis, mainly to disable
initialization of large stack arrays when deemed too expensive.
This isn't meant to change the semantics of C and C++. Rather, it's meant to be
a last-resort when programmers inadvertently have some undefined behavior in
their code. This patch aims to make undefined behavior hurt less, which
security-minded people will be very happy about. Notably, this means that
there's no inadvertent information leak when:
- The compiler re-uses stack slots, and a value is used uninitialized.
- The compiler re-uses a register, and a value is used uninitialized.
- Stack structs / arrays / unions with padding are copied.
This patch only addresses stack and register information leaks. There's many
more infoleaks that we could address, and much more undefined behavior that
could be tamed. Let's keep this patch focused, and I'm happy to address related
issues elsewhere.
To keep the patch simple, only some `undef` is removed for now, see
`replaceUndef`. The padding-related infoleaks are therefore not all gone yet.
This will be addressed in a follow-up, mainly because addressing padding-related
leaks should be a stand-alone option which is implied by variable
initialization.
There are three options when it comes to automatic variable initialization:
0. Uninitialized
This is C and C++'s default. It's not changing. Depending on code
generation, a programmer who runs into undefined behavior by using an
uninialized automatic variable may observe any previous value (including
program secrets), or any value which the compiler saw fit to materialize on
the stack or in a register (this could be to synthesize an immediate, to
refer to code or data locations, to generate cookies, etc).
1. Pattern initialization
This is the recommended initialization approach. Pattern initialization's
goal is to initialize automatic variables with values which will likely
transform logic bugs into crashes down the line, are easily recognizable in
a crash dump, without being values which programmers can rely on for useful
program semantics. At the same time, pattern initialization tries to
generate code which will optimize well. You'll find the following details in
`patternFor`:
- Integers are initialized with repeated 0xAA bytes (infinite scream).
- Vectors of integers are also initialized with infinite scream.
- Pointers are initialized with infinite scream on 64-bit platforms because
it's an unmappable pointer value on architectures I'm aware of. Pointers
are initialize to 0x000000AA (small scream) on 32-bit platforms because
32-bit platforms don't consistently offer unmappable pages. When they do
it's usually the zero page. As people try this out, I expect that we'll
want to allow different platforms to customize this, let's do so later.
- Vectors of pointers are initialized the same way pointers are.
- Floating point values and vectors are initialized with a negative quiet
NaN with repeated 0xFF payload (e.g. 0xffffffff and 0xffffffffffffffff).
NaNs are nice (here, anways) because they propagate on arithmetic, making
it more likely that entire computations become NaN when a single
uninitialized value sneaks in.
- Arrays are initialized to their homogeneous elements' initialization
value, repeated. Stack-based Variable-Length Arrays (VLAs) are
runtime-initialized to the allocated size (no effort is made for negative
size, but zero-sized VLAs are untouched even if technically undefined).
- Structs are initialized to their heterogeneous element's initialization
values. Zero-size structs are initialized as 0xAA since they're allocated
a single byte.
- Unions are initialized using the initialization for the largest member of
the union.
Expect the values used for pattern initialization to change over time, as we
refine heuristics (both for performance and security). The goal is truly to
avoid injecting semantics into undefined behavior, and we should be
comfortable changing these values when there's a worthwhile point in doing
so.
Why so much infinite scream? Repeated byte patterns tend to be easy to
synthesize on most architectures, and otherwise memset is usually very
efficient. For values which aren't entirely repeated byte patterns, LLVM
will often generate code which does memset + a few stores.
2. Zero initialization
Zero initialize all values. This has the unfortunate side-effect of
providing semantics to otherwise undefined behavior, programs therefore
might start to rely on this behavior, and that's sad. However, some
programmers believe that pattern initialization is too expensive for them,
and data might show that they're right. The only way to make these
programmers wrong is to offer zero-initialization as an option, figure out
where they are right, and optimize the compiler into submission. Until the
compiler provides acceptable performance for all security-minded code, zero
initialization is a useful (if blunt) tool.
I've been asked for a fourth initialization option: user-provided byte value.
This might be useful, and can easily be added later.
Why is an out-of band initialization mecanism desired? We could instead use
-Wuninitialized! Indeed we could, but then we're forcing the programmer to
provide semantics for something which doesn't actually have any (it's
uninitialized!). It's then unclear whether `int derp = 0;` lends meaning to `0`,
or whether it's just there to shut that warning up. It's also way easier to use
a compiler flag than it is to manually and intelligently initialize all values
in a program.
Why not just rely on static analysis? Because it cannot reason about all dynamic
code paths effectively, and it has false positives. It's a great tool, could get
even better, but it's simply incapable of catching all uses of uninitialized
values.
Why not just rely on memory sanitizer? Because it's not universally available,
has a 3x performance cost, and shouldn't be deployed in production. Again, it's
a great tool, it'll find the dynamic uses of uninitialized variables that your
test coverage hits, but it won't find the ones that you encounter in production.
What's the performance like? Not too bad! Previous publications [0] have cited
2.7 to 4.5% averages. We've commmitted a few patches over the last few months to
address specific regressions, both in code size and performance. In all cases,
the optimizations are generally useful, but variable initialization benefits
from them a lot more than regular code does. We've got a handful of other
optimizations in mind, but the code is in good enough shape and has found enough
latent issues that it's a good time to get the change reviewed, checked in, and
have others kick the tires. We'll continue reducing overheads as we try this out
on diverse codebases.
Is it a good idea? Security-minded folks think so, and apparently so does the
Microsoft Visual Studio team [1] who say "Between 2017 and mid 2018, this
feature would have killed 49 MSRC cases that involved uninitialized struct data
leaking across a trust boundary. It would have also mitigated a number of bugs
involving uninitialized struct data being used directly.". They seem to use pure
zero initialization, and claim to have taken the overheads down to within noise.
Don't just trust Microsoft though, here's another relevant person asking for
this [2]. It's been proposed for GCC [3] and LLVM [4] before.
What are the caveats? A few!
- Variables declared in unreachable code, and used later, aren't initialized.
This goto, Duff's device, other objectionable uses of switch. This should
instead be a hard-error in any serious codebase.
- Volatile stack variables are still weird. That's pre-existing, it's really
the language's fault and this patch keeps it weird. We should deprecate
volatile [5].
- As noted above, padding isn't fully handled yet.
I don't think these caveats make the patch untenable because they can be
addressed separately.
Should this be on by default? Maybe, in some circumstances. It's a conversation
we can have when we've tried it out sufficiently, and we're confident that we've
eliminated enough of the overheads that most codebases would want to opt-in.
Let's keep our precious undefined behavior until that point in time.
How do I use it:
1. On the command-line:
-ftrivial-auto-var-init=uninitialized (the default)
-ftrivial-auto-var-init=pattern
-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang
2. Using an attribute:
int dont_initialize_me __attribute((uninitialized));
[0]: https://users.elis.ugent.be/~jsartor/researchDocs/OOPSLA2011Zero-submit.pdf
[1]: https://twitter.com/JosephBialek/status/1062774315098112001
[2]: https://outflux.net/slides/2018/lss/danger.pdf
[3]: https://gcc.gnu.org/ml/gcc-patches/2014-06/msg00615.html
[4]: https://github.com/AndroidHardeningArchive/platform_external_clang/commit/776a0955ef6686d23a82d2e6a3cbd4a6a882c31c
[5]: http://wg21.link/p1152
I've also posted an RFC to cfe-dev: http://lists.llvm.org/pipermail/cfe-dev/2018-November/060172.html
<rdar://problem/39131435>
Reviewers: pcc, kcc, rsmith
Subscribers: JDevlieghere, jkorous, dexonsmith, cfe-commits
Differential Revision: https://reviews.llvm.org/D54604
llvm-svn: 349442
2018-12-18 13:12:21 +08:00
// RUN: %clang_cc1 -fsyntax-only -Wuninitialized -Wconditional-uninitialized -ftrivial-auto-var-init=pattern -fsyntax-only -fblocks %s -verify
2011-01-15 10:58:47 +08:00
2011-08-24 04:30:50 +08:00
typedef __typeof ( sizeof ( int ) ) size_t ;
void * malloc ( size_t ) ;
2011-01-15 10:58:47 +08:00
int test1 ( ) {
2011-09-10 13:35:08 +08:00
int x ; // expected-note{{initialize the variable 'x' to silence this warning}}
2011-04-08 14:33:38 +08:00
return x ; // expected-warning{{variable 'x' is uninitialized when used here}}
2011-01-15 10:58:47 +08:00
}
int test2 ( ) {
int x = 0 ;
return x ; // no-warning
}
int test3 ( ) {
int x ;
x = 0 ;
return x ; // no-warning
}
int test4 ( ) {
2011-09-10 13:35:08 +08:00
int x ; // expected-note{{initialize the variable 'x' to silence this warning}}
2011-04-08 14:33:38 +08:00
+ + x ; // expected-warning{{variable 'x' is uninitialized when used here}}
2011-01-15 10:58:47 +08:00
return x ;
}
int test5 ( ) {
2011-09-10 13:35:08 +08:00
int x , y ; // expected-note{{initialize the variable 'y' to silence this warning}}
2011-04-08 14:33:38 +08:00
x = y ; // expected-warning{{variable 'y' is uninitialized when used here}}
2011-01-15 10:58:47 +08:00
return x ;
}
int test6 ( ) {
2011-09-10 13:35:08 +08:00
int x ; // expected-note{{initialize the variable 'x' to silence this warning}}
2012-07-17 09:27:33 +08:00
x + = 2 ; // expected-warning{{variable 'x' is uninitialized when used here}}
return x ;
2011-01-15 10:58:47 +08:00
}
int test7 ( int y ) {
2011-09-10 13:35:08 +08:00
int x ; // expected-note{{initialize the variable 'x' to silence this warning}}
2012-05-26 14:20:46 +08:00
if ( y ) / / expected - warning { { variable ' x ' is used uninitialized whenever ' if ' condition is false } } \
// expected-note{{remove the 'if' if its condition is always true}}
2011-01-15 10:58:47 +08:00
x = 1 ;
2012-05-26 14:20:46 +08:00
return x ; // expected-note{{uninitialized use occurs here}}
2011-01-15 10:58:47 +08:00
}
2011-10-20 02:53:03 +08:00
int test7b ( int y ) {
int x = x ; // expected-note{{variable 'x' is declared here}}
if ( y )
x = 1 ;
2012-05-25 10:17:09 +08:00
// Warn with "may be uninitialized" here (not "is sometimes uninitialized"),
// since the self-initialization is intended to suppress a -Wuninitialized
// warning.
2011-10-20 02:53:03 +08:00
return x ; // expected-warning{{variable 'x' may be uninitialized when used here}}
}
2011-01-15 10:58:47 +08:00
int test8 ( int y ) {
int x ;
if ( y )
x = 1 ;
else
x = 0 ;
2011-02-03 07:35:53 +08:00
return x ;
2011-01-15 10:58:47 +08:00
}
int test9 ( int n ) {
2011-09-10 13:35:08 +08:00
int x ; // expected-note{{initialize the variable 'x' to silence this warning}}
2011-01-15 10:58:47 +08:00
for ( unsigned i = 0 ; i < n ; + + i ) {
if ( i = = n - 1 )
break ;
2011-01-22 03:41:41 +08:00
x = 1 ;
2011-01-15 10:58:47 +08:00
}
2011-04-08 14:47:15 +08:00
return x ; // expected-warning{{variable 'x' may be uninitialized when used here}}
2011-01-15 10:58:47 +08:00
}
int test10 ( unsigned n ) {
2011-09-10 13:35:08 +08:00
int x ; // expected-note{{initialize the variable 'x' to silence this warning}}
2011-01-15 10:58:47 +08:00
for ( unsigned i = 0 ; i < n ; + + i ) {
x = 1 ;
}
2011-04-08 14:47:15 +08:00
return x ; // expected-warning{{variable 'x' may be uninitialized when used here}}
2011-01-15 10:58:47 +08:00
}
int test11 ( unsigned n ) {
2011-09-10 13:35:08 +08:00
int x ; // expected-note{{initialize the variable 'x' to silence this warning}}
2011-01-15 10:58:47 +08:00
for ( unsigned i = 0 ; i < = n ; + + i ) {
x = 1 ;
}
2011-04-08 14:47:15 +08:00
return x ; // expected-warning{{variable 'x' may be uninitialized when used here}}
2011-01-15 10:58:47 +08:00
}
void test12 ( unsigned n ) {
2011-09-10 13:35:08 +08:00
for ( unsigned i ; n ; + + i ) ; // expected-warning{{variable 'i' is uninitialized when used here}} expected-note{{initialize the variable 'i' to silence this warning}}
2011-01-15 10:58:47 +08:00
}
int test13 ( ) {
static int i ;
return i ; // no-warning
}
2011-01-18 12:53:25 +08:00
// Simply don't crash on this test case.
void test14 ( ) {
const char * p = 0 ;
for ( ; ; ) { }
}
2011-10-14 02:50:06 +08:00
void test15 ( ) {
int x = x ; // no-warning: signals intended lack of initialization.
}
int test15b ( ) {
// Warn here with the self-init, since it does result in a use of
2018-04-06 23:14:32 +08:00
// an uninitialized variable and this is the root cause.
2011-10-14 02:50:06 +08:00
int x = x ; // expected-warning {{variable 'x' is uninitialized when used within its own initialization}}
return x ;
2011-01-18 12:53:25 +08:00
}
2011-01-15 10:58:47 +08:00
2011-01-18 12:53:25 +08:00
// Don't warn in the following example; shows dataflow confluence.
char * test16_aux ( ) ;
void test16 ( ) {
char * p = test16_aux ( ) ;
for ( unsigned i = 0 ; i < 100 ; i + + )
p [ i ] = ' a ' ; // no-warning
}
2011-01-19 05:18:58 +08:00
void test17 ( ) {
// Don't warn multiple times about the same uninitialized variable
// along the same path.
2011-09-10 13:35:08 +08:00
int * x ; // expected-note{{initialize the variable 'x' to silence this warning}}
2011-04-08 14:33:38 +08:00
* x = 1 ; // expected-warning{{variable 'x' is uninitialized when used here}}
2011-01-19 05:18:58 +08:00
* x = 1 ; // no-warning
}
2011-01-21 01:37:17 +08:00
int test18 ( int x , int y ) {
int z ;
if ( x & & y & & ( z = 1 ) ) {
return z ; // no-warning
}
return 0 ;
}
int test19_aux1 ( ) ;
int test19_aux2 ( ) ;
int test19_aux3 ( int * x ) ;
int test19 ( ) {
int z ;
if ( test19_aux1 ( ) + test19_aux2 ( ) & & test19_aux1 ( ) & & test19_aux3 ( & z ) )
return z ; // no-warning
return 0 ;
}
int test20 ( ) {
2011-09-10 13:35:08 +08:00
int z ; // expected-note{{initialize the variable 'z' to silence this warning}}
2012-07-14 13:04:10 +08:00
if ( ( test19_aux1 ( ) + test19_aux2 ( ) & & test19_aux1 ( ) ) | | test19_aux3 ( & z ) ) // expected-warning {{variable 'z' is used uninitialized whenever '||' condition is true}} expected-note {{remove the '||' if its condition is always false}}
return z ; // expected-note {{uninitialized use occurs here}}
2011-01-21 01:37:17 +08:00
return 0 ;
}
int test21 ( int x , int y ) {
2011-09-10 13:35:08 +08:00
int z ; // expected-note{{initialize the variable 'z' to silence this warning}}
2012-07-14 13:04:10 +08:00
if ( ( x & & y ) | | test19_aux3 ( & z ) | | test19_aux2 ( ) ) // expected-warning {{variable 'z' is used uninitialized whenever '||' condition is true}} expected-note {{remove the '||' if its condition is always false}}
return z ; // expected-note {{uninitialized use occurs here}}
2011-01-21 01:37:17 +08:00
return 0 ;
}
int test22 ( ) {
int z ;
while ( test19_aux1 ( ) + test19_aux2 ( ) & & test19_aux1 ( ) & & test19_aux3 ( & z ) )
return z ; // no-warning
return 0 ;
}
int test23 ( ) {
int z ;
for ( ; test19_aux1 ( ) + test19_aux2 ( ) & & test19_aux1 ( ) & & test19_aux3 ( & z ) ; )
return z ; // no-warning
return 0 ;
}
// The basic uninitialized value analysis doesn't have enough path-sensitivity
// to catch initializations relying on control-dependencies spanning multiple
// conditionals. This possibly can be handled by making the CFG itself
// represent such control-dependencies, but it is a niche case.
int test24 ( int flag ) {
2011-09-10 13:35:08 +08:00
unsigned val ; // expected-note{{initialize the variable 'val' to silence this warning}}
2011-01-21 01:37:17 +08:00
if ( flag )
val = 1 ;
if ( ! flag )
val = 1 ;
2011-04-08 14:47:15 +08:00
return val ; // expected-warning{{variable 'val' may be uninitialized when used here}}
2011-01-21 01:37:17 +08:00
}
2011-01-22 06:49:49 +08:00
float test25 ( ) {
2011-09-10 13:35:08 +08:00
float x ; // expected-note{{initialize the variable 'x' to silence this warning}}
2011-04-08 14:33:38 +08:00
return x ; // expected-warning{{variable 'x' is uninitialized when used here}}
2011-01-22 06:49:49 +08:00
}
typedef int MyInt ;
MyInt test26 ( ) {
2011-09-10 13:35:08 +08:00
MyInt x ; // expected-note{{initialize the variable 'x' to silence this warning}}
2011-04-08 14:33:38 +08:00
return x ; // expected-warning{{variable 'x' is uninitialized when used here}}
2011-01-22 06:49:49 +08:00
}
2011-01-24 01:53:04 +08:00
// Test handling of sizeof().
int test27 ( ) {
struct test_27 { int x ; } * y ;
return sizeof ( y - > x ) ; // no-warning
}
int test28 ( ) {
2011-09-10 13:35:08 +08:00
int len ; // expected-note{{initialize the variable 'len' to silence this warning}}
2011-04-08 14:33:38 +08:00
return sizeof ( int [ len ] ) ; // expected-warning{{variable 'len' is uninitialized when used here}}
2011-01-24 01:53:04 +08:00
}
2011-01-26 03:13:48 +08:00
void test29 ( ) {
2011-09-10 13:35:08 +08:00
int x ; // expected-note{{initialize the variable 'x' to silence this warning}}
2011-04-08 14:33:38 +08:00
( void ) ^ { ( void ) x ; } ; // expected-warning{{variable 'x' is uninitialized when captured by block}}
2011-01-26 03:13:48 +08:00
}
void test30 ( ) {
static int x ; // no-warning
( void ) ^ { ( void ) x ; } ;
}
void test31 ( ) {
__block int x ; // no-warning
( void ) ^ { ( void ) x ; } ;
}
int test32_x ;
void test32 ( ) {
( void ) ^ { ( void ) test32_x ; } ; // no-warning
}
2011-01-26 12:49:43 +08:00
void test_33 ( ) {
int x ; // no-warning
( void ) x ;
}
int test_34 ( ) {
2011-09-10 13:35:08 +08:00
int x ; // expected-note{{initialize the variable 'x' to silence this warning}}
2011-01-26 12:49:43 +08:00
( void ) x ;
2011-04-08 14:33:38 +08:00
return x ; // expected-warning{{variable 'x' is uninitialized when used here}}
2011-01-26 12:49:43 +08:00
}
2011-01-27 10:29:34 +08:00
// Test that this case doesn't crash.
void test35 ( int x ) {
__block int y = 0 ;
^ { y = ( x = = 0 ) ; } ( ) ;
}
2011-01-28 02:51:39 +08:00
// Test handling of indirect goto.
void test36 ( )
{
2011-09-10 13:35:08 +08:00
void * * pc ; // expected-note{{initialize the variable 'pc' to silence this warning}}
2011-01-28 02:51:39 +08:00
void * dummy [ ] = { & & L1 , & & L2 } ;
L1 :
2011-07-22 13:27:52 +08:00
goto * pc ; // expected-warning{{variable 'pc' is uninitialized when used here}}
2011-01-28 02:51:39 +08:00
L2 :
goto * pc ;
}
2011-02-02 01:43:18 +08:00
// Test && nested in ||.
int test37_a ( ) ;
int test37_b ( ) ;
int test37 ( )
{
int identifier ;
if ( ( test37_a ( ) & & ( identifier = 1 ) ) | |
( test37_b ( ) & & ( identifier = 2 ) ) ) {
return identifier ; // no-warning
}
return 0 ;
}
// Test merging of path-specific dataflow values (without asserting).
int test38 ( int r , int x , int y )
{
int z ;
return ( ( r < 0 ) | | ( ( r = = 0 ) & & ( x < y ) ) ) ;
}
2011-03-15 11:17:01 +08:00
int test39 ( int x ) {
2011-09-10 13:35:08 +08:00
int y ; // expected-note{{initialize the variable 'y' to silence this warning}}
2011-04-08 14:33:38 +08:00
int z = x + y ; // expected-warning {{variable 'y' is uninitialized when used here}}
2011-03-15 11:17:01 +08:00
return z ;
}
int test40 ( int x ) {
2011-09-10 13:35:08 +08:00
int y ; // expected-note{{initialize the variable 'y' to silence this warning}}
2011-04-08 14:33:38 +08:00
return x ? 1 : y ; // expected-warning {{variable 'y' is uninitialized when used here}}
2011-03-15 11:17:01 +08:00
}
int test41 ( int x ) {
2011-09-10 13:35:08 +08:00
int y ; // expected-note{{initialize the variable 'y' to silence this warning}}
2012-05-26 14:20:46 +08:00
if ( x ) y = 1 ; / / expected - warning { { variable ' y ' is used uninitialized whenever ' if ' condition is false } } \
// expected-note{{remove the 'if' if its condition is always true}}
return y ; // expected-note{{uninitialized use occurs here}}
2011-03-15 11:17:01 +08:00
}
void test42 ( ) {
int a ;
a = 30 ; // no-warning
}
void test43_aux ( int x ) ;
void test43 ( int i ) {
2011-09-10 13:35:08 +08:00
int x ; // expected-note{{initialize the variable 'x' to silence this warning}}
2011-03-15 11:17:01 +08:00
for ( i = 0 ; i < 10 ; i + + )
2011-07-22 13:27:52 +08:00
test43_aux ( x + + ) ; // expected-warning {{variable 'x' is uninitialized when used here}}
2011-03-15 11:17:01 +08:00
}
void test44 ( int i ) {
int x = i ;
2011-09-10 13:35:08 +08:00
int y ; // expected-note{{initialize the variable 'y' to silence this warning}}
2011-03-15 11:17:01 +08:00
for ( i = 0 ; i < 10 ; i + + ) {
test43_aux ( x + + ) ; // no-warning
2011-07-22 13:27:52 +08:00
x + = y ; // expected-warning {{variable 'y' is uninitialized when used here}}
2011-03-15 11:17:01 +08:00
}
}
int test45 ( int j ) {
int x = 1 , y = x + 1 ;
if ( y ) // no-warning
return x ;
return y ;
}
void test46 ( )
{
2011-09-10 13:35:08 +08:00
int i ; // expected-note{{initialize the variable 'i' to silence this warning}}
2011-04-08 14:33:38 +08:00
int j = i ? : 1 ; // expected-warning {{variable 'i' is uninitialized when used here}}
2011-03-15 11:17:01 +08:00
}
void * test47 ( int * i )
{
return i ? : 0 ; // no-warning
}
void * test49 ( int * i )
{
int a ;
return & a ? : i ; // no-warning
}
void test50 ( )
{
char c [ 1 ? : 2 ] ; // no-warning
}
2011-04-01 06:32:41 +08:00
int test51 ( void )
{
__block int a ;
^ ( void ) {
a = 42 ;
} ( ) ;
return a ; // no-warning
}
2011-05-11 06:10:35 +08:00
// FIXME: This is a false positive, but it tests logical operations in switch statements.
int test52 ( int a , int b ) {
2011-09-10 13:35:08 +08:00
int x ; // expected-note {{initialize the variable 'x' to silence this warning}}
2011-05-11 06:10:35 +08:00
switch ( a | | b ) { // expected-warning {{switch condition has boolean value}}
case 0 :
x = 1 ;
break ;
case 1 :
x = 2 ;
break ;
}
return x ; // expected-warning {{variable 'x' may be uninitialized when used here}}
}
2011-07-17 06:27:02 +08:00
2011-07-20 04:33:49 +08:00
void test53 ( ) {
2011-09-10 13:35:08 +08:00
int x ; // expected-note {{initialize the variable 'x' to silence this warning}}
2011-07-20 05:41:51 +08:00
int y = ( x ) ; // expected-warning {{variable 'x' is uninitialized when used here}}
2011-07-20 04:33:49 +08:00
}
2011-07-17 06:27:02 +08:00
// This CFG caused the uninitialized values warning to inf-loop.
extern int PR10379_g ( ) ;
void PR10379_f ( int * len ) {
2011-09-10 13:35:08 +08:00
int new_len ; // expected-note{{initialize the variable 'new_len' to silence this warning}}
2011-07-17 06:27:02 +08:00
for ( int i = 0 ; i < 42 & & PR10379_g ( ) = = 0 ; i + + ) {
if ( PR10379_g ( ) = = 1 )
continue ;
if ( PR10379_g ( ) = = 2 )
PR10379_f ( & new_len ) ;
else if ( PR10379_g ( ) = = 3 )
PR10379_f ( & new_len ) ;
* len + = new_len ; // expected-warning {{variable 'new_len' may be uninitialized when used here}}
}
}
2011-08-24 04:30:50 +08:00
// Test that sizeof(VLA) doesn't trigger a warning.
void test_vla_sizeof ( int x ) {
double ( * memory ) [ 2 ] [ x ] = malloc ( sizeof ( * memory ) ) ; // no-warning
}
2011-09-03 03:39:26 +08:00
// Test absurd case of deadcode + use of blocks. This previously was a false positive
// due to an analysis bug.
int test_block_and_dead_code ( ) {
__block int x ;
^ { x = 1 ; } ( ) ;
if ( 0 )
return x ;
return x ; // no-warning
}
2011-10-07 08:42:48 +08:00
// This previously triggered an infinite loop in the analysis.
void PR11069 ( int a , int b ) {
unsigned long flags ;
for ( ; ; ) {
if ( a & & ! b )
break ;
}
for ( ; ; ) {
// This does not trigger a warning because it isn't a real use.
( void ) ( flags ) ; // no-warning
}
}
2011-10-07 08:52:56 +08:00
// Test uninitialized value used in loop condition.
void rdar9432305 ( float * P ) {
int i ; // expected-note {{initialize the variable 'i' to silence this warning}}
for ( ; i < 10000 ; + + i ) // expected-warning {{variable 'i' is uninitialized when used here}}
P [ i ] = 0.0f ;
}
2012-05-03 09:09:59 +08:00
// Test that fixits are not emitted inside macros.
# define UNINIT(T, x, y) T x; T y = x;
# define ASSIGN(T, x, y) T y = x;
void test54 ( ) {
UNINIT ( int , a , b ) ; / / expected - warning { { variable ' a ' is uninitialized when used here } } \
// expected-note {{variable 'a' is declared here}}
int c ; // expected-note {{initialize the variable 'c' to silence this warning}}
ASSIGN ( int , c , d ) ; // expected-warning {{variable 'c' is uninitialized when used here}}
}
2012-06-17 07:34:14 +08:00
2012-06-18 07:10:39 +08:00
// Taking the address is fine
struct { struct { void * p ; } a ; } test55 = { { & test55 . a } } ; // no-warning
struct { struct { void * p ; } a ; } test56 = { { & ( test56 . a ) } } ; // no-warning
2012-06-17 07:34:14 +08:00
void uninit_in_loop ( ) {
int produce ( void ) ;
void consume ( int ) ;
for ( int n = 0 ; n < 100 ; + + n ) {
int k ; // expected-note {{initialize}}
consume ( k ) ; // expected-warning {{variable 'k' is uninitialized}}
k = produce ( ) ;
}
}
void uninit_in_loop_goto ( ) {
int produce ( void ) ;
void consume ( int ) ;
for ( int n = 0 ; n < 100 ; + + n ) {
goto skip_decl ;
int k ; // expected-note {{initialize}}
skip_decl :
// FIXME: This should produce the 'is uninitialized' diagnostic, but we
// don't have enough information in the CFG to easily tell that the
// variable's scope has been left and re-entered.
consume ( k ) ; // expected-warning {{variable 'k' may be uninitialized}}
k = produce ( ) ;
}
}
2012-07-03 07:23:04 +08:00
typedef char jmp_buf [ 256 ] ;
extern int setjmp ( jmp_buf env ) ; // implicitly returns_twice
void do_stuff_and_longjmp ( jmp_buf env , int * result ) __attribute__ ( ( noreturn ) ) ;
int returns_twice ( ) {
int a ; // expected-note {{initialize}}
if ( ! a ) { // expected-warning {{variable 'a' is uninitialized}}
jmp_buf env ;
int b ;
if ( setjmp ( env ) = = 0 ) {
do_stuff_and_longjmp ( env , & b ) ;
} else {
a = b ; // no warning
}
}
return a ;
}
2012-07-17 08:06:14 +08:00
int compound_assign ( int * arr , int n ) {
int sum ; // expected-note {{initialize}}
for ( int i = 0 ; i < n ; + + i )
2012-07-17 09:27:33 +08:00
sum + = arr [ i ] ; // expected-warning {{variable 'sum' is uninitialized}}
return sum / n ;
2012-07-17 08:06:14 +08:00
}
2012-07-17 09:27:33 +08:00
int compound_assign_2 ( ) {
int x ; // expected-note {{initialize}}
return x + = 1 ; // expected-warning {{variable 'x' is uninitialized}}
}
int compound_assign_3 ( ) {
int x ; // expected-note {{initialize}}
x * = 0 ; // expected-warning {{variable 'x' is uninitialized}}
return x ;
2012-07-17 08:06:14 +08:00
}
2012-07-25 05:02:14 +08:00
int self_init_in_cond ( int * p ) {
int n = ( ( p & & ( 0 | | 1 ) ) & & ( n = * p ) ) ? n : - 1 ; // ok
return n ;
}
2012-09-12 13:53:43 +08:00
void test_analyzer_noreturn_aux ( ) __attribute__ ( ( analyzer_noreturn ) ) ;
void test_analyzer_noreturn ( int y ) {
int x ; // expected-note {{initialize the variable 'x' to silence this warning}}
if ( y ) {
test_analyzer_noreturn_aux ( ) ;
+ + x ; // no-warning
}
else {
+ + x ; // expected-warning {{variable 'x' is uninitialized when used here}}
}
}
void test_analyzer_noreturn_2 ( int y ) {
int x ;
if ( y ) {
test_analyzer_noreturn_aux ( ) ;
}
else {
x = 1 ;
}
+ + x ; // no-warning
}