analyzer: stop exploring the path after certain diagnostics [PR108830]

PR analyzer/108830 reports a situation in which there are lots of
followup -Wanalyzer-null-dereference warnings after the first access of
a NULL pointer, leading to very noisy output from -fanalyzer.

The analyzer's logic for stopping emitting multiple warnings from a
state machine doesn't quite work for NULL pointers: it attempts to
transition the malloc state machine's NULL pointer to the "stop" state,
which doesn't seem to make much sense in retrospect, and seems to get
confused over types.

Similarly, poisoned_value_diagnostic can be very noisy for uninit
variables, emitting a warning for every access to an uninitialized
variable.  In theory, region_model::check_for_poison makes some attempts
to suppress followups, but only for the symbolic value itself; if the
user's code keeps accessing the same region, we would get a warning on
each one.  For example, this showed up in Doom's s_sound.c where there
were 7 followup uninit warnings after the first uninit warning in
"S_ChangeMusic".

This patch adds an extra mechanism, giving pending diagnostics the
option of stopping the analysis of an execution path if they're saved
for emission on it, and turning this on for these warnings:
  -Wanalyzer-null-dereference
  -Wanalyzer-null-argument
  -Wanalyzer-use-after-free
  -Wanalyzer-use-of-pointer-in-stale-stack-frame
  -Wanalyzer-use-of-uninitialized-value

Doing so should hopefully reduce the cascades of diagnostics that
-fanalyzer can sometimes emit.

I added a -fno-analyzer-suppress-followups for the cases where you
really want the followup warnings (e.g. in some DejaGnu tests, and
for microbenchmarks of UB detection, such as PR analyzer/104224).

Integration testing shows this patch reduces the number of probable
false positives reported by 94, and finds one more true positive:

Comparison: 9.34% -> 10.91%
  GOOD:  66 ->  67  (+1)
   BAD: 641 -> 547 (-94)

where the affected warnings/projects are:

  -Wanalyzer-null-dereference: 0.00% GOOD: 0 BAD: 269 -> 239 (-30)
     Unclassified: 257 -> 228 (-29)
                  apr-1.7.0:  12 ->   5  (-7)
                       doom:   1 ->   0  (-1)
              haproxy-2.7.1:  47 ->  41  (-6)
       ImageMagick-7.1.0-57:  13 ->   9  (-4)
                 qemu-7.2.0: 165 -> 154 (-11)

      Known false: 7 -> 6 (-1)
                   xz-5.4.0:   4 ->   3  (-1)

  -Wanalyzer-use-of-uninitialized-value: 0.00% GOOD: 0 BAD: 143 -> 80 (-63)
      Known false: 47 -> 16 (-31)
                       doom: 42 -> 11 (-31)

     Unclassified: 96 -> 64 (-32)
              coreutils-9.1: 14 -> 10  (-4)
              haproxy-2.7.1: 29 -> 23  (-6)
                 qemu-7.2.0: 48 -> 26 (-22)

  -Wanalyzer-null-argument: 0.00% -> 2.33% GOOD: 0 -> 1 (+1) BAD: 43 -> 42 (-1)
     Unclassified: 39 -> 38 (-1)
      due to coreutils-9.1: 9 -> 8 (-1)

    True positive: 0 -> 1 (+1)
      (in haproxy-2.7.1)

gcc/analyzer/ChangeLog:
	PR analyzer/108830
	* analyzer.opt (fanalyzer-suppress-followups): New option.
	* engine.cc (impl_region_model_context::warn): Terminate the path
	if the diagnostic's terminate_path_p vfunc returns true and
	-fanalyzer-suppress-followups is true (the default).
	(impl_sm_context::warn): Likewise, for both overloads.
	* pending-diagnostic.h (pending_diagnostic::terminate_path_p): New
	vfunc.
	* program-state.cc (program_state::on_edge): Terminate the path if
	the ctxt requests it during updating the edge.
	* region-model.cc (poisoned_value_diagnostic::terminate_path_p):
	New vfunc.
	* sm-malloc.cc (null_deref::terminate_path_p): New vfunc.
	(null_arg::terminate_path_p): New vfunc.

gcc/ChangeLog:
	PR analyzer/108830
	* doc/invoke.texi: Document -fno-analyzer-suppress-followups.

gcc/testsuite/ChangeLog:
	PR analyzer/108830
	* gcc.dg/analyzer/attribute-nonnull.c: Update for
	-Wanalyzer-use-of-uninitialized-value terminating analysis along
	a path.
	* gcc.dg/analyzer/call-summaries-2.c: Likewise.
	* gcc.dg/analyzer/data-model-1.c: Likewise.
	* gcc.dg/analyzer/data-model-5.c: Likewise.
	* gcc.dg/analyzer/doom-s_sound-pr108867.c: New test.
	* gcc.dg/analyzer/memset-CVE-2017-18549-1.c: Add
	-fno-analyzer-suppress-followups.
	* gcc.dg/analyzer/null-deref-pr108830.c: New test.
	* gcc.dg/analyzer/pipe-1.c: Add -fno-analyzer-suppress-followups.
	* gcc.dg/analyzer/pipe-void-return.c: Likewise.
	* gcc.dg/analyzer/pipe2-1.c: Likewise.
	* gcc.dg/analyzer/pr101547.c: Update for
	-Wanalyzer-use-of-uninitialized-value terminating analysis along
	a path.
	* gcc.dg/analyzer/pr101875.c: Likewise.
	* gcc.dg/analyzer/pr104224-split.c: New test, based on...
	* gcc.dg/analyzer/pr104224.c: Add
	-fno-analyzer-suppress-followups.
	* gcc.dg/analyzer/realloc-2.c: Add
	-fno-analyzer-suppress-followups.
	* gcc.dg/analyzer/realloc-3.c: Likewise.
	* gcc.dg/analyzer/realloc-5.c: Likewise.
	* gcc.dg/analyzer/stdarg-1-ms_abi.c: Likewise.
	* gcc.dg/analyzer/stdarg-1-sysv_abi.c: Likewise.
	* gcc.dg/analyzer/stdarg-1.c: Likewise.
	* gcc.dg/analyzer/symbolic-1.c: Likewise.
	* gcc.dg/analyzer/symbolic-7.c: Update for
	-Wanalyzer-use-of-uninitialized-value terminating analysis along a
	path.
	* gcc.dg/analyzer/uninit-4.c: Likewise.
	* gcc.dg/analyzer/uninit-8.c: New test.
	* gcc.dg/analyzer/uninit-pr94713.c: Update for
	-Wanalyzer-use-of-uninitialized-value terminating analysis along a
	path.
	* gcc.dg/analyzer/zlib-6a.c: Add -fno-analyzer-suppress-followups.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
David Malcolm 2023-02-21 16:58:36 -05:00
parent b2ef02e8cb
commit 8f63691579
33 changed files with 1159 additions and 48 deletions

View file

@ -262,6 +262,10 @@ fanalyzer-state-merge
Common Var(flag_analyzer_state_merge) Init(1)
Merge similar-enough states during analysis.
fanalyzer-suppress-followups
Common Var(flag_analyzer_suppress_followups) Init(1)
Stop exploring an execution path after certain diagnostics.
fanalyzer-transitivity
Common Var(flag_analyzer_transitivity) Init(0)
Enable transitivity of constraints during analysis.

View file

@ -125,11 +125,20 @@ impl_region_model_context::warn (std::unique_ptr<pending_diagnostic> d)
return false;
}
if (m_eg)
return m_eg->get_diagnostic_manager ().add_diagnostic
(m_enode_for_diag, m_enode_for_diag->get_supernode (),
m_stmt, m_stmt_finder, std::move (d));
else
return false;
{
bool terminate_path = d->terminate_path_p ();
if (m_eg->get_diagnostic_manager ().add_diagnostic
(m_enode_for_diag, m_enode_for_diag->get_supernode (),
m_stmt, m_stmt_finder, std::move (d)))
{
if (m_path_ctxt
&& terminate_path
&& flag_analyzer_suppress_followups)
m_path_ctxt->terminate_path ();
return true;
}
}
return false;
}
void
@ -378,9 +387,14 @@ public:
= (var
? m_old_smap->get_state (var_old_sval, m_eg.get_ext_state ())
: m_old_smap->get_global_state ());
bool terminate_path = d->terminate_path_p ();
m_eg.get_diagnostic_manager ().add_diagnostic
(&m_sm, m_enode_for_diag, snode, stmt, m_stmt_finder,
var, var_old_sval, current, std::move (d));
if (m_path_ctxt
&& terminate_path
&& flag_analyzer_suppress_followups)
m_path_ctxt->terminate_path ();
}
void warn (const supernode *snode, const gimple *stmt,
@ -393,9 +407,14 @@ public:
= (sval
? m_old_smap->get_state (sval, m_eg.get_ext_state ())
: m_old_smap->get_global_state ());
bool terminate_path = d->terminate_path_p ();
m_eg.get_diagnostic_manager ().add_diagnostic
(&m_sm, m_enode_for_diag, snode, stmt, m_stmt_finder,
NULL_TREE, sval, current, std::move (d));
if (m_path_ctxt
&& terminate_path
&& flag_analyzer_suppress_followups)
m_path_ctxt->terminate_path ();
}
/* Hook for picking more readable trees for SSA names of temporaries,

View file

@ -173,6 +173,10 @@ class pending_diagnostic
having to generate feasible execution paths for them). */
virtual int get_controlling_option () const = 0;
/* Vfunc to give the diagnostic the chance to terminate the execution
path being explored. By default, don't terminate the path. */
virtual bool terminate_path_p () const { return false; }
/* Vfunc for emitting the diagnostic. The rich_location will have been
populated with a diagnostic_path.
Return true if a diagnostic is actually emitted. */

View file

@ -1105,6 +1105,27 @@ program_state::on_edge (exploded_graph &eg,
const superedge *succ,
uncertainty_t *uncertainty)
{
class my_path_context : public path_context
{
public:
my_path_context (bool &terminated) : m_terminated (terminated) {}
void bifurcate (std::unique_ptr<custom_edge_info>) final override
{
gcc_unreachable ();
}
void terminate_path () final override
{
m_terminated = true;
}
bool terminate_path_p () const final override
{
return m_terminated;
}
bool &m_terminated;
};
/* Update state. */
const program_point &point = enode->get_point ();
const gimple *last_stmt = point.get_supernode ()->get_last_stmt ();
@ -1117,11 +1138,12 @@ program_state::on_edge (exploded_graph &eg,
Adding the relevant conditions for the edge could also trigger
sm-state transitions (e.g. transitions due to ptrs becoming known
to be NULL or non-NULL) */
bool terminated = false;
my_path_context path_ctxt (terminated);
impl_region_model_context ctxt (eg, enode,
&enode->get_state (),
this,
uncertainty, NULL,
uncertainty, &path_ctxt,
last_stmt);
if (!m_region_model->maybe_update_for_edge (*succ,
last_stmt,
@ -1134,6 +1156,8 @@ program_state::on_edge (exploded_graph &eg,
succ->m_dest->m_index);
return false;
}
if (terminated)
return false;
program_state::detect_leaks (enode->get_state (), *this,
NULL, eg.get_ext_state (),

View file

@ -505,6 +505,8 @@ public:
}
}
bool terminate_path_p () const final override { return true; }
bool emit (rich_location *rich_loc) final override
{
switch (m_pkind)

View file

@ -1150,6 +1150,8 @@ public:
return OPT_Wanalyzer_null_dereference;
}
bool terminate_path_p () const final override { return true; }
bool emit (rich_location *rich_loc) final override
{
/* CWE-476: NULL Pointer Dereference. */
@ -1203,6 +1205,8 @@ public:
return OPT_Wanalyzer_null_argument;
}
bool terminate_path_p () const final override { return true; }
bool emit (rich_location *rich_loc) final override
{
/* CWE-476: NULL Pointer Dereference. */

View file

@ -428,6 +428,7 @@ Objective-C and Objective-C++ Dialects}.
-fanalyzer-fine-grained @gol
-fno-analyzer-state-merge @gol
-fno-analyzer-state-purge @gol
-fno-analyzer-suppress-followups @gol
-fanalyzer-transitivity @gol
-fno-analyzer-undo-inlining @gol
-fanalyzer-verbose-edges @gol
@ -11012,6 +11013,30 @@ and which aren't relevant to leak analysis.
With @option{-fno-analyzer-state-purge} this purging of state can
be suppressed, for debugging state-handling issues.
@item -fno-analyzer-suppress-followups
@opindex fanalyzer-suppress-followups
@opindex fno-analyzer-suppress-followups
This option is intended for analyzer developers.
By default the analyzer will stop exploring an execution path after
encountering certain diagnostics, in order to avoid potentially issuing a
cascade of follow-up diagnostics.
The diagnostics that terminate analysis along a path are:
@itemize
@item @option{-Wanalyzer-null-argument}
@item @option{-Wanalyzer-null-dereference}
@item @option{-Wanalyzer-use-after-free}
@item @option{-Wanalyzer-use-of-pointer-in-stale-stack-frame}
@item @option{-Wanalyzer-use-of-uninitialized-value}
@end itemize
With @option{-fno-analyzer-suppress-followups} the analyzer will
continue to explore such paths even after such diagnostics, which may
be helpful for debugging issues in the analyzer, or for microbenchmarks
for detecting undefined behavior.
@item -fanalyzer-transitivity
@opindex fanalyzer-transitivity
@opindex fno-analyzer-transitivity

View file

@ -16,8 +16,6 @@ void test_1 (void *p, void *q, void *r)
foo(p, q, r);
foo(NULL, q, r); /* { dg-warning "use of NULL where non-null expected" "warning" } */
/* { dg-message "argument 1 NULL where non-null expected" "note" { target *-*-* } .-1 } */
foo(p, NULL, r);
foo(p, q, NULL); /* { dg-warning "use of NULL where non-null expected" } */
}
void test_1a (void *q, void *r)
@ -27,12 +25,29 @@ void test_1a (void *q, void *r)
/* { dg-message "argument 1 \\('p'\\) NULL where non-null expected" "note" { target *-*-* } .-1 } */
}
void test_2 (void *p, void *q, void *r)
void test_1b (void *p, void *r)
{
foo(p, NULL, r);
}
void test_1c (void *p, void *q, void *r)
{
foo(p, q, NULL); /* { dg-warning "use of NULL where non-null expected" } */
}
void test_2a (void *p, void *q, void *r)
{
bar(p, q, r);
bar(NULL, q, r); /* { dg-warning "use of NULL where non-null expected" "warning" } */
}
void test_2b (void *p, void *q, void *r)
{
bar(p, NULL, r); /* { dg-warning "use of NULL where non-null expected" "warning" } */
/* { dg-message "argument 2 NULL where non-null expected" "note" { target *-*-* } .-1 } */
}
void test_2c (void *p, void *q, void *r)
{
bar(p, q, NULL); /* { dg-warning "use of NULL where non-null expected" "warning" } */
}

View file

@ -607,17 +607,22 @@ void partially_inits (int *p, int v)
p[1] = v;
}
void test_partially_inits (int x)
void test_partially_inits_0 (int x)
{
int arr[2];
partially_inits (arr, x);
partially_inits (arr, x);
__analyzer_eval (arr[0]); /* { dg-warning "UNKNOWN" "eval" } */
/* { dg-warning "use of uninitialized value 'arr\\\[0\\\]'" "uninit" { target *-*-* } .-1 } */
__analyzer_eval (arr[0]); /* { dg-warning "use of uninitialized value 'arr\\\[0\\\]'" } */
}
__analyzer_eval (arr[1] == x); /* { dg-warning "UNKNOWN" "eval" } */
/* { dg-bogus "use of uninitialized value 'arr\\\[1\\\]'" "uninit" { xfail *-*-* } .-1 } */
void test_partially_inits_1 (int x)
{
int arr[2];
partially_inits (arr, x);
partially_inits (arr, x);
__analyzer_eval (arr[1] == x); /* { dg-bogus "use of uninitialized value 'arr\\\[1\\\]'" "uninit" { xfail *-*-* } } */
// TODO(xfail), and eval should be "TRUE"
}

View file

@ -351,9 +351,8 @@ void test_19 (void)
{
int i, j; /* { dg-message "region created on stack here" } */
/* Compare two uninitialized locals. */
__analyzer_eval (i == j); /* { dg-warning "UNKNOWN" "unknown " } */
/* { dg-warning "use of uninitialized value 'i'" "uninit i" { target *-*-* } .-1 } */
/* { dg-warning "use of uninitialized value 'j'" "uninit j" { target *-*-* } .-2 } */
__analyzer_eval (i == j); /* { dg-warning "use of uninitialized value 'i'" "uninit i" } */
/* { dg-warning "use of uninitialized value 'j'" "uninit j" { target *-*-* } .-1 } */
}
void test_20 (int i, int j)
@ -653,11 +652,6 @@ void test_29b (void)
__analyzer_eval (p[9].x == 109024); /* { dg-warning "TRUE" } */
__analyzer_eval (p[9].y == 109025); /* { dg-warning "TRUE" } */
__analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" "unknown" } */
/* { dg-warning "use of uninitialized value 'p\\\[10\\\].x'" "uninit" { target *-*-* } .-1 } */
__analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" "unknown" } */
/* { dg-warning "use of uninitialized value 'p\\\[10\\\].y'" "uninit" { target *-*-* } .-1 } */
q = &p[7];
__analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
@ -679,6 +673,8 @@ void test_29b (void)
__analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
__analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
__analyzer_eval (p[10].x == 0); /* { dg-warning "use of uninitialized value 'p\\\[10\\\].x'" } */
}
void test_29c (int len)
@ -704,11 +700,6 @@ void test_29c (int len)
__analyzer_eval (p[9].x == 109024); /* { dg-warning "TRUE" } */
__analyzer_eval (p[9].y == 109025); /* { dg-warning "TRUE" } */
__analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" "unknown" } */
/* { dg-warning "use of uninitialized value '\\*p\\\[10\\\].x'" "uninit" { target *-*-* } .-1 } */
__analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" "unknown" } */
/* { dg-warning "use of uninitialized value '\\*p\\\[10\\\].y'" "uninit" { target *-*-* } .-1 } */
q = &p[7];
__analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
@ -730,6 +721,8 @@ void test_29c (int len)
__analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
__analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
__analyzer_eval (p[10].x == 0); /* { dg-warning "use of uninitialized value '\\*p\\\[10\\\].x'" } */
}
void test_30 (void *ptr)

View file

@ -90,10 +90,6 @@ void unref (base_obj *obj)
{
if (--obj->ob_refcnt == 0) /* { dg-bogus "dereference of uninitialized pointer 'obj'" } */
obj->ob_type->tp_dealloc (obj);
/* { dg-warning "dereference of NULL 'obj'" "deref of NULL" { target *-*-* } .-2 } */
/* FIXME: ideally we wouldn't issue this, as we've already issued a
warning about str_obj which is now in the "stop" state; the cast
confuses things. */
}
void test_1 (const char *str)

View file

@ -0,0 +1,653 @@
/* Reduced from Doom's linuxdoom-1.10/s_sound.c, which is GPLv2 or later. */
/* { dg-additional-options "-fno-analyzer-call-summaries -Wno-analyzer-too-complex" } */
typedef struct _IO_FILE FILE;
extern FILE* stderr;
extern int
fprintf(FILE* __restrict __stream, const char* __restrict __format, ...);
extern int
sprintf(char* __restrict __s, const char* __restrict __format, ...)
__attribute__((__nothrow__));
extern int
abs(int __x) __attribute__((__nothrow__, __leaf__)) __attribute__((__const__));
typedef enum
{
false,
true
} boolean;
typedef unsigned char byte;
void
I_Error(char* error, ...);
typedef enum
{
shareware,
registered,
commercial,
/* [...snip...] */
} GameMode_t;
typedef int fixed_t;
fixed_t
FixedMul(fixed_t a, fixed_t b);
extern fixed_t finesine[5 * 8192 / 4];
typedef unsigned angle_t;
typedef struct mobj_s
{
/* [...snip...] */
fixed_t x;
fixed_t y;
fixed_t z;
/* [...snip...] */
angle_t angle;
/* [...snip...] */
} mobj_t;
typedef struct player_s
{
mobj_t* mo;
/* [...snip...] */
} player_t;
extern GameMode_t gamemode;
extern int gameepisode;
extern int gamemap;
extern int consoleplayer;
extern player_t players[4];
typedef struct sfxinfo_struct sfxinfo_t;
struct sfxinfo_struct
{
/* [...snip...] */
int priority;
sfxinfo_t* link;
int pitch;
int volume;
void* data;
int usefulness;
int lumpnum;
};
typedef struct
{
char* name;
int lumpnum;
void* data;
int handle;
} musicinfo_t;
extern sfxinfo_t S_sfx[];
extern musicinfo_t S_music[];
typedef enum
{
mus_None,
mus_e1m1,
/* [...snip...] */
mus_e1m5,
/* [...snip...] */
mus_e1m9,
/* [...snip...] */
mus_e2m4,
mus_e2m5,
mus_e2m6,
mus_e2m7,
/* [...snip...] */
mus_e3m2,
mus_e3m3,
mus_e3m4,
/* [...snip...] */
mus_runnin,
/* [...snip...] */
NUMMUSIC
} musicenum_t;
typedef enum
{
/* [...snip...] */
sfx_sawup,
/* [...snip...] */
sfx_sawhit,
/* [...snip...] */
sfx_itemup,
/* [...snip...] */
sfx_tink,
/* [...snip...] */
NUMSFX
} sfxenum_t;
void
I_SetChannels();
int
I_GetSfxLumpNum(sfxinfo_t* sfxinfo);
int
I_StartSound(int id, int vol, int sep, int pitch, int priority);
void
I_StopSound(int handle);
int
I_SoundIsPlaying(int handle);
void
I_UpdateSoundParams(int handle, int vol, int sep, int pitch);
void
I_SetMusicVolume(int volume);
void
I_PauseSong(int handle);
void
I_ResumeSong(int handle);
int
I_RegisterSong(void* data);
void
I_PlaySong(int handle, int looping);
void
I_StopSong(int handle);
void
I_UnRegisterSong(int handle);
void
S_StopSound(void* origin);
void
S_ChangeMusic(int music_id, int looping);
void
S_StopMusic(void);
void
S_SetMusicVolume(int volume);
void
S_SetSfxVolume(int volume);
void*
Z_Malloc(int size, int tag, void* ptr);
void
Z_ChangeTag2(void* ptr, int tag);
typedef struct memblock_s
{
/* [...snip...] */
int id;
/* [...snip...] */
} memblock_t;
int
M_Random(void);
int
W_GetNumForName(char* name);
void*
W_CacheLumpNum(int lump, int tag);
angle_t
R_PointToAngle2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2);
typedef struct
{
sfxinfo_t* sfxinfo;
void* origin;
int handle;
} channel_t;
static channel_t* channels;
int snd_SfxVolume = 15;
int snd_MusicVolume = 15;
static boolean mus_paused;
static musicinfo_t* mus_playing = 0;
int numChannels;
static int nextcleanup;
int
S_getChannel(void* origin, sfxinfo_t* sfxinfo);
int
S_AdjustSoundParams(mobj_t* listener,
mobj_t* source,
int* vol,
int* sep,
int* pitch);
void
S_StopChannel(int cnum);
void
S_Init(int sfxVolume, int musicVolume)
{
int i;
fprintf(stderr, "S_Init: default sfx volume %d\n", sfxVolume);
I_SetChannels();
S_SetSfxVolume(sfxVolume);
S_SetMusicVolume(musicVolume);
channels = (channel_t*)Z_Malloc(numChannels * sizeof(channel_t), 1, 0);
for (i = 0; i < numChannels; i++)
channels[i].sfxinfo = 0;
mus_paused = 0;
for (i = 1; i < NUMSFX; i++)
S_sfx[i].lumpnum = S_sfx[i].usefulness = -1;
}
void
S_Start(void)
{
int cnum;
int mnum;
for (cnum = 0; cnum < numChannels; cnum++)
if (channels[cnum].sfxinfo)
S_StopChannel(cnum);
mus_paused = 0;
if (gamemode == commercial)
mnum = mus_runnin + gamemap - 1;
else {
int spmus[] = {
mus_e3m4, mus_e3m2, mus_e3m3, mus_e1m5, mus_e2m7,
mus_e2m4, mus_e2m6, mus_e2m5, mus_e1m9
};
if (gameepisode < 4)
mnum = mus_e1m1 + (gameepisode - 1) * 9 + gamemap - 1;
else
mnum = spmus[gamemap - 1];
}
S_ChangeMusic(mnum, true);
nextcleanup = 15;
}
void
S_StartSoundAtVolume(void* origin_p, int sfx_id, int volume)
{
int rc;
int sep;
int pitch;
int priority;
sfxinfo_t* sfx;
int cnum;
mobj_t* origin = (mobj_t*)origin_p;
if (sfx_id < 1 || sfx_id > NUMSFX)
I_Error("Bad sfx #: %d", sfx_id);
sfx = &S_sfx[sfx_id];
if (sfx->link) {
pitch = sfx->pitch;
priority = sfx->priority;
volume += sfx->volume;
if (volume < 1)
return;
if (volume > snd_SfxVolume)
volume = snd_SfxVolume;
} else {
pitch = 128;
priority = 64;
}
if (origin && origin != players[consoleplayer].mo) {
rc = S_AdjustSoundParams(
players[consoleplayer].mo, origin, &volume, &sep, &pitch);
if (origin->x == players[consoleplayer].mo->x &&
origin->y == players[consoleplayer].mo->y) {
sep = 128;
}
if (!rc)
return;
} else {
sep = 128;
}
if (sfx_id >= sfx_sawup && sfx_id <= sfx_sawhit) {
pitch += 8 - (M_Random() & 15);
if (pitch < 0)
pitch = 0;
else if (pitch > 255)
pitch = 255;
} else if (sfx_id != sfx_itemup && sfx_id != sfx_tink) {
pitch += 16 - (M_Random() & 31);
if (pitch < 0)
pitch = 0;
else if (pitch > 255)
pitch = 255;
}
S_StopSound(origin);
cnum = S_getChannel(origin, sfx);
if (cnum < 0)
return;
if (sfx->lumpnum < 0)
sfx->lumpnum = I_GetSfxLumpNum(sfx);
if (!sfx->data) {
fprintf(stderr, "S_StartSoundAtVolume: 16bit and not pre-cached - wtf?\n");
}
if (sfx->usefulness++ < 0)
sfx->usefulness = 1;
channels[cnum].handle = I_StartSound(sfx_id,
volume,
sep,
pitch,
priority);
}
void
S_StartSound(void* origin, int sfx_id)
{
S_StartSoundAtVolume(origin, sfx_id, snd_SfxVolume);
}
void
S_StopSound(void* origin)
{
int cnum;
for (cnum = 0; cnum < numChannels; cnum++) {
if (channels[cnum].sfxinfo && channels[cnum].origin == origin) {
S_StopChannel(cnum);
break;
}
}
}
void
S_PauseSound(void)
{
if (mus_playing && !mus_paused) {
I_PauseSong(mus_playing->handle);
mus_paused = true;
}
}
void
S_ResumeSound(void)
{
if (mus_playing && mus_paused) {
I_ResumeSong(mus_playing->handle);
mus_paused = false;
}
}
void
S_UpdateSounds(void* listener_p)
{
int audible;
int cnum;
int volume;
int sep;
int pitch;
sfxinfo_t* sfx;
channel_t* c;
mobj_t* listener = (mobj_t*)listener_p;
for (cnum = 0; cnum < numChannels; cnum++) {
c = &channels[cnum];
sfx = c->sfxinfo;
if (c->sfxinfo) {
if (I_SoundIsPlaying(c->handle)) {
volume = snd_SfxVolume;
pitch = 128;
sep = 128;
if (sfx->link) {
pitch = sfx->pitch;
volume += sfx->volume;
if (volume < 1) {
S_StopChannel(cnum);
continue;
} else if (volume > snd_SfxVolume) {
volume = snd_SfxVolume;
}
}
if (c->origin && listener_p != c->origin) {
audible =
S_AdjustSoundParams(listener, c->origin, &volume, &sep, &pitch);
if (!audible) {
S_StopChannel(cnum);
} else
I_UpdateSoundParams(c->handle, volume, sep, pitch);
}
} else {
S_StopChannel(cnum);
}
}
}
}
void
S_SetMusicVolume(int volume)
{
if (volume < 0 || volume > 127) {
I_Error("Attempt to set music volume at %d", volume);
}
I_SetMusicVolume(127);
I_SetMusicVolume(volume);
snd_MusicVolume = volume;
}
void
S_SetSfxVolume(int volume)
{
if (volume < 0 || volume > 127)
I_Error("Attempt to set sfx volume at %d", volume);
snd_SfxVolume = volume;
}
void
S_StartMusic(int m_id)
{
S_ChangeMusic(m_id, false);
}
void
S_ChangeMusic(int musicnum, int looping)
{
musicinfo_t* music;
char namebuf[9];
if ((musicnum <= mus_None) || (musicnum >= NUMMUSIC)) {
I_Error("Bad music number %d", musicnum);
} else
music = &S_music[musicnum];
/* We don't know that I_Error exits, so actually a false positive;
see PR analyzer/108867. */
if (mus_playing == music) /* { dg-warning "use of uninitialized value 'music'" } */
return;
S_StopMusic();
/* We shouldn't issue further warnings about 'music' being
uninitialized. */
if (!music->lumpnum) { /* { dg-bogus "use of uninitialized value 'music'" } */
sprintf(namebuf, "d_%s", music->name); /* { dg-bogus "use of uninitialized value 'music'" } */
music->lumpnum = W_GetNumForName(namebuf); /* { dg-bogus "use of uninitialized value 'music'" } */
}
music->data = (void*)W_CacheLumpNum(music->lumpnum, 3); /* { dg-bogus "use of uninitialized value 'music'" } */
music->handle = I_RegisterSong(music->data); /* { dg-bogus "use of uninitialized value 'music'" } */
I_PlaySong(music->handle, looping); /* { dg-bogus "use of uninitialized value 'music'" } */
mus_playing = music; /* { dg-bogus "use of uninitialized value 'music'" } */
}
void
S_StopMusic(void)
{
if (mus_playing) {
if (mus_paused)
I_ResumeSong(mus_playing->handle);
I_StopSong(mus_playing->handle);
I_UnRegisterSong(mus_playing->handle);
{
if (((memblock_t*)((byte*)(mus_playing->data) - sizeof(memblock_t)))
->id != 0x1d4a11)
I_Error("Z_CT at "
"s_sound.c"
":%i",
699);
Z_ChangeTag2(mus_playing->data, 101);
};
;
mus_playing->data = 0;
mus_playing = 0;
}
}
void
S_StopChannel(int cnum)
{
int i;
channel_t* c = &channels[cnum];
if (c->sfxinfo) {
if (I_SoundIsPlaying(c->handle)) {
I_StopSound(c->handle);
}
for (i = 0; i < numChannels; i++) {
if (cnum != i && c->sfxinfo == channels[i].sfxinfo) {
break;
}
}
c->sfxinfo->usefulness--;
c->sfxinfo = 0;
}
}
int
S_AdjustSoundParams(mobj_t* listener,
mobj_t* source,
int* vol,
int* sep,
int* pitch)
{
fixed_t approx_dist;
fixed_t adx;
fixed_t ady;
angle_t angle;
adx = abs(listener->x - source->x);
ady = abs(listener->y - source->y);
approx_dist = adx + ady - ((adx < ady ? adx : ady) >> 1);
if (gamemap != 8 && approx_dist > (1200 * 0x10000)) {
return 0;
}
angle = R_PointToAngle2(listener->x, listener->y, source->x, source->y);
if (angle > listener->angle)
angle = angle - listener->angle;
else
angle = angle + (0xffffffff - listener->angle);
angle >>= 19;
*sep = 128 - (FixedMul((96 * 0x10000), finesine[angle]) >> 16);
if (approx_dist < (160 * 0x10000)) {
*vol = snd_SfxVolume;
} else if (gamemap == 8) {
if (approx_dist > (1200 * 0x10000))
approx_dist = (1200 * 0x10000);
*vol =
15 + ((snd_SfxVolume - 15) * (((1200 * 0x10000) - approx_dist) >> 16)) /
(((1200 * 0x10000) - (160 * 0x10000)) >> 16);
} else {
*vol = (snd_SfxVolume * (((1200 * 0x10000) - approx_dist) >> 16)) /
(((1200 * 0x10000) - (160 * 0x10000)) >> 16);
}
return (*vol > 0);
}
int
S_getChannel(void* origin, sfxinfo_t* sfxinfo)
{
int cnum;
channel_t* c;
for (cnum = 0; cnum < numChannels; cnum++) {
if (!channels[cnum].sfxinfo)
break;
else if (origin && channels[cnum].origin == origin) {
S_StopChannel(cnum);
break;
}
}
if (cnum == numChannels) {
for (cnum = 0; cnum < numChannels; cnum++)
if (channels[cnum].sfxinfo->priority >= sfxinfo->priority) /* { dg-warning "dereference of NULL" } */
break;
if (cnum == numChannels) {
return -1;
} else {
S_StopChannel(cnum);
}
}
c = &channels[cnum];
c->sfxinfo = sfxinfo;
c->origin = origin;
return cnum;
}

View file

@ -5,6 +5,8 @@
It was fixed by e.g. 342ffc26693b528648bdc9377e51e4f2450b4860 on linux-4.13.y
in linux-stable. */
/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
#include "analyzer-decls.h"
#include <string.h>

View file

@ -0,0 +1,94 @@
/* Reduced from apr-1.7.0/tables/apr_hash.c: 'apr_hash_merge' */
/* { dg-additional-options "-Wno-analyzer-too-complex" } */
#define NULL ((void*)0)
typedef __SIZE_TYPE__ size_t;
extern void*
memset(void* __s, int __c, size_t __n)
__attribute__((__nothrow__, __leaf__, __nonnull__(1)));
typedef struct apr_pool_t apr_pool_t;
void*
apr_palloc(apr_pool_t* p, size_t size)
__attribute__((alloc_size(2), nonnull(1)));
typedef struct apr_hash_t apr_hash_t;
typedef struct apr_hash_index_t apr_hash_index_t;
typedef unsigned int (*apr_hashfunc_t)(const char* key, size_t* klen);
typedef struct apr_hash_entry_t apr_hash_entry_t;
struct apr_hash_entry_t
{
apr_hash_entry_t* next;
unsigned int hash;
const void* key;
size_t klen;
const void* val;
};
struct apr_hash_t
{
apr_pool_t* pool;
apr_hash_entry_t** array;
/* [...snip.../ */
unsigned int count, max, seed;
apr_hashfunc_t hash_func;
apr_hash_entry_t* free;
};
static apr_hash_entry_t**
alloc_array(apr_hash_t* ht, unsigned int max)
{
return memset(apr_palloc(ht->pool, sizeof(*ht->array) * (max + 1)),
0,
sizeof(*ht->array) * (max + 1));
}
apr_hash_t*
apr_hash_merge(apr_pool_t* p,
const apr_hash_t* overlay,
const apr_hash_t* base)
{
apr_hash_t* res;
apr_hash_entry_t* new_vals = NULL;
apr_hash_entry_t* iter;
unsigned int i, j, k;
res = apr_palloc(p, sizeof(apr_hash_t));
res->pool = p;
res->free = NULL;
res->hash_func = base->hash_func;
res->count = base->count;
res->max = (overlay->max > base->max) ? overlay->max : base->max;
if (base->count + overlay->count > res->max) {
res->max = res->max * 2 + 1;
}
res->seed = base->seed;
res->array = alloc_array(res, res->max);
if (base->count + overlay->count) {
new_vals =
apr_palloc(p, sizeof(apr_hash_entry_t) * (base->count + overlay->count));
}
j = 0;
for (k = 0; k <= base->max; k++) {
for (iter = base->array[k]; iter; iter = iter->next) {
i = iter->hash & res->max;
/* We should only warn for the first of these
(it's actually a false positive, but we don't have the
invariante to know that). */
new_vals[j].klen = iter->klen; /* { dg-warning "dereference of NULL 'new_vals'" } */
/* ...but not for subsequent ones: */
new_vals[j].key = iter->key; /* { dg-bogus "dereference of NULL 'new_vals'" "PR analyzer/108830" } */
new_vals[j].val = iter->val; /* { dg-bogus "dereference of NULL 'new_vals'" "PR analyzer/108830" } */
new_vals[j].hash = iter->hash; /* { dg-bogus "dereference of NULL 'new_vals'" "PR analyzer/108830" } */
new_vals[j].next = res->array[i]; /* { dg-bogus "dereference of NULL 'new_vals'" "PR analyzer/108830" } */
res->array[i] = &new_vals[j]; /* { dg-bogus "dereference of NULL 'new_vals'" "PR analyzer/108830" } */
j++;
}
}
/* [...snip...] */
return res;
}

View file

@ -1,3 +1,5 @@
/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
#include "analyzer-decls.h"
extern int pipe(int pipefd[2]);

View file

@ -1,3 +1,5 @@
/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
extern void pipe(int pipefd[2]);
extern int close(int fd);

View file

@ -1,3 +1,5 @@
/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
#include "analyzer-decls.h"
extern int pipe2(int pipefd[2], int flags);

View file

@ -2,10 +2,18 @@ char *
fopen (const char *restrict, const char *restrict);
void
k2 (void)
k2_uninit (void)
{
char *setfiles[1];
int i; /* { dg-message "region created on stack here" } */
setfiles[i] = fopen ("", ""); /* { dg-warning "use of uninitialized value 'i'" } */
}
void
k2_leak (int i)
{
char *setfiles[1];
setfiles[i] = fopen ("", "");
} /* { dg-warning "leak of FILE" } */

View file

@ -5,12 +5,22 @@ void
err (void);
void
k2 (void)
k2_uninit (void)
{
char *setfiles[1];
int i; /* { dg-message "region created on stack here" } */
setfiles[i] = fopen("", ""); /* { dg-warning "use of uninitialized value 'i'" } */
if (!setfiles[i]) /* { dg-warning "use of uninitialized value 'i'" } */
if (!setfiles[i])
err ();
}
void
k2_leak (int i)
{
char *setfiles[1];
setfiles[i] = fopen("", "");
if (!setfiles[i])
err ();
} /* { dg-warning "leak of FILE" } */

View file

@ -0,0 +1,136 @@
#include <stdio.h>
struct test {
int one;
int two;
};
void func2(const struct test *t)
{
if (t->one == 0)
printf("init func2\n");
if (t->two == 0) /* { dg-warning "uninitialized" } */
printf("uninit func2\n");
}
void func1(struct test *t)
{
t->one = 1;
func2(t);
}
int func3(int num)
{
if (num)
return num;
else
return 0;
}
void func4(int *a, int max)
{
int i;
// skip the first
for (i=1; i<max; i++)
a[i] = 0;
}
void func5(const int *a, int max)
{
/* a[0] is uninitialized, but the rest of the array is initialized. */
int i;
for (i=0; i<max; i++) {
if (a[i]) /* { dg-warning "uninitialized" "" { xfail *-*-* } } */
printf("func5: %d\n", i);
}
}
int func6(const int *num)
{
if (*num) /* { dg-warning "uninitialized" } */
return *num;
else
return 0;
}
int j;
int func7(void)
{
return j; /* { dg-bogus "uninitialized" } */
}
void func8(const int *a, int max)
{
int i;
for (i=0; i<max; i++) {
if (a[i]) /* { dg-warning "uninitialized" } */
printf("func8: %d\n", i);
}
}
enum {RED, AMBER, GREEN, BLACK};
int test_1 (void)
{
struct test t; /* { dg-message "region created on stack here" } */
func1(&t);
return 0;
}
int test_2 (void)
{
int num; /* { dg-message "region created on stack here" } */
func3(num); /* { dg-warning "use of uninitialized value 'num'" } */
return 0;
}
int test_3 (void)
{
int arry[10];
func4(arry, 10);
func5(arry, 10);
return 0;
}
int test_4 (void)
{
int num; /* { dg-message "region created on stack here" } */
func6(&num);
return 0;
}
int test_5 (void)
{
int arry_2[10]; /* { dg-message "region created on stack here" } */
printf("func7: %d\n", func7());
func8(arry_2, 10);
return 0;
}
int test_6 (void)
{
int go; /* { dg-message "region created on stack here" } */
int color = BLACK;
switch (color) {
case RED:
case AMBER:
go = 0;
break;
case GREEN:
go = 1;
break;
}
printf("go :%d\n", go); /* { dg-warning "use of uninitialized value 'go'" } */
return 0;
}

View file

@ -1,3 +1,5 @@
/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
#include <stdio.h>
struct test {

View file

@ -1,3 +1,5 @@
/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
#include "analyzer-decls.h"
typedef __SIZE_TYPE__ size_t;

View file

@ -1,3 +1,5 @@
/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
#include "analyzer-decls.h"
typedef __SIZE_TYPE__ size_t;

View file

@ -1,3 +1,5 @@
/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
#include "analyzer-decls.h"
typedef __SIZE_TYPE__ size_t;

View file

@ -1,5 +1,6 @@
/* As per stdarg-1.c, but using the ms_abi versions of the builtins. */
/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
/* { dg-do compile { target { x86_64-*-* && lp64 } } } */
#include "analyzer-decls.h"

View file

@ -1,5 +1,6 @@
/* As per stdarg-1.c, but using the sysv_abi versions of the builtins. */
/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
/* { dg-do compile { target { x86_64-*-* && lp64 } } } */
#include "analyzer-decls.h"

View file

@ -1,3 +1,5 @@
/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
#include "analyzer-decls.h"
/* Unpacking a va_list. */

View file

@ -1,3 +1,5 @@
/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
#include "analyzer-decls.h"
/* The example from store.h */

View file

@ -31,16 +31,20 @@ void test_2 (int i)
__analyzer_eval (arr[i] == 42); /* { dg-warning "UNKNOWN" } */
}
void test_3 (int i)
void test_3_concrete_read (int i)
{
/* An array that can't have been touched. */
int arr[2];
/* Concrete reads. */
__analyzer_eval (arr[0] == 42); /* { dg-warning "UNKNOWN" "unknown" } */
/* { dg-warning "use of uninitialized value 'arr\\\[0\\\]'" "uninit" { target *-*-* } .-1 } */
/* Symbolic read. */
__analyzer_eval (arr[i] == 42); /* { dg-warning "UNKNOWN" "unknown" } */
/* { dg-warning "use of uninitialized value 'arr\\\[i\\\]'" "uninit" { target *-*-* } .-1 } */
__analyzer_eval (arr[0] == 42); /* { dg-warning "use of uninitialized value 'arr\\\[0\\\]'" } */
}
void test_3_symbolic_read (int i)
{
/* An array that can't have been touched. */
int arr[2];
/* Symbolic read. */
__analyzer_eval (arr[i] == 42); /* { dg-warning "use of uninitialized value 'arr\\\[i\\\]'" } */
}

View file

@ -22,7 +22,7 @@ alloc_foo (int a, int b)
return p;
}
void test (int x, int y, int z)
void test_access_inited_fields (int x, int y, int z)
{
struct foo *p = alloc_foo (x, z);
if (!p)
@ -30,10 +30,20 @@ void test (int x, int y, int z)
__analyzer_eval (p->i == x); /* { dg-warning "TRUE" } */
__analyzer_eval (p->j == y); /* { dg-warning "UNKNOWN" "unknown" } */
/* { dg-warning "use of uninitialized value '\\*p\\.j'" "uninit" { target *-*-* } .-1 } */
__analyzer_eval (p->k == z); /* { dg-warning "TRUE" } */
free (p);
}
void test_stop_after_accessing_uninit (int x, int y, int z)
{
struct foo *p = alloc_foo (x, z);
if (!p)
return;
__analyzer_eval (p->i == x); /* { dg-warning "TRUE" } */
__analyzer_eval (p->j == y); /* { dg-warning "use of uninitialized value '\\*p\\.j'" } */
__analyzer_dump_path (); /* { dg-bogus "path" } */
}

View file

@ -0,0 +1,73 @@
struct st
{
int a, b, c, d, e;
};
int
test_1 (int flag, struct st *p)
{
struct st *q;
int result = 0;
if (flag)
q = p;
/* We should only warn about the first use of uninit for 'q': */
result += q->a; /* { dg-warning "use of uninitialized value 'q'" } */
/* ...and not for these: */
result += q->b; /* { dg-bogus "use of uninitialized value 'q'" } */
result += q->c; /* { dg-bogus "use of uninitialized value 'q'" } */
result += q->d; /* { dg-bogus "use of uninitialized value 'q'" } */
result += q->e; /* { dg-bogus "use of uninitialized value 'q'" } */
return result;
}
int
test_2 (int flag, struct st *p, struct st *r)
{
struct st *q;
int result = 0;
if (flag)
q = p;
/* We should only warn about the first use of uninit for 'q': */
if (q == r) /* { dg-warning "use of uninitialized value 'q'" } */
result += 1;
/* ...and not for these, after a conditional: */
result += q->b; /* { dg-bogus "use of uninitialized value 'q'" } */
result += q->c; /* { dg-bogus "use of uninitialized value 'q'" } */
result += q->d; /* { dg-bogus "use of uninitialized value 'q'" } */
result += q->e; /* { dg-bogus "use of uninitialized value 'q'" } */
return result;
}
int
test_3 (int flag, int val)
{
int result = 0;
int idx;
if (flag)
idx = val;
switch (idx) /* { dg-warning "use of uninitialized value 'idx'" } */
{
case 0:
result = 3;
break;
case 1:
result = 4;
break;
default:
result = 5;
break;
}
switch (idx) /* { dg-bogus "use of uninitialized value 'idx'" } */
{
case 0:
result += 3;
break;
case 1:
result += 4;
break;
default:
result += 5;
break;
}
return result;
}

View file

@ -1,11 +1,19 @@
void f1 (int *);
void f2 (int);
int foo (void)
int test_1 (void)
{
int *p; /* { dg-message "region created on stack here" } */
f1 (p); /* { dg-warning "use of uninitialized value 'p'" } */
f1 (p); /* { dg-bogus "use of uninitialized value 'p'" "no followup warnings" } */
return 0;
}
int test_2 (void)
{
int *p; /* { dg-message "region created on stack here" } */
f2 (p[0]); /* { dg-warning "use of uninitialized value 'p'" } */
return 0;
}

View file

@ -1,3 +1,5 @@
/* { dg-additional-options "-fno-analyzer-suppress-followups" } */
typedef unsigned char Byte;
typedef unsigned int uInt;
typedef unsigned long uLong;