Powered by Linux
Re: checking for uninitialized variables — Semantic Matching Tool

Re: checking for uninitialized variables

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I added the smatch mailing list to the CC.

On Mon, Nov 17, 2014 at 06:13:51PM -0800, Jörn Engel wrote:
> Hello Dan!
> 
> I recently came across a patch that had a serious bugfix hidden inside
> it: dbcbc95cd858
> 
> inside iscsit_handle_data_out() cmd was uninitialized and
> iscsit_check_dataout_hdr() wouldn't always initialize it - so we had
> code paths using an uninitialized variable and should have seen compiler
> warnings.
> 
> - Gcc versions 4.4 through 4.9 didn't warn.  I tried many different
>   options, none resulted in a warning.
> - clang gave no warning.
> - clang static checker gave no warning.
> - sparse gave no warning.
> - smatch gave no warning.  The README file in the git tree is for
>   sparse, so I may have used it wrong.  But with either default
>   parameters of -Wuninitialized there is no warning.

Sorry for that.  I've pushed some changes so hopefully the documentation
to build and run is easier to find now.  It's under Documentation/.

> 
> Not a single compiler or code checker is finding this bug.  That scares
> me, as we likely have a dozen more such bugs hidden inside the kernel.
> 
> As a testcase, I can reduce the code down to nine lines:
> 
> void callee(int *arg);
> int caller(void)
> {
> 	int arg;
> 	callee(&arg);
> 	if (!arg)
> 		return 1;
> 	return 0;
> }
> 
> Is this something you could cook up a test for?  I would like need two
> weeks to work myself through the source code before I can even get
> started.

That test case will drive you nuts.  You need to have cross function
analysis for there to be a manageable number of false positives.

Smatch does cross function analysis, but playing with this I see that I
need to make some improvements here.  There are still way way too many
false positives.  I have some ideas here.

Anyway, just for laughs, I've attached the check I'm working on.  You'll
need to add it check_list.h and recompile.

regards,
dan carpenter
/*
 * Copyright (C) 2014 Oracle.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
 */

#include "smatch.h"
#include "smatch_slist.h"
#include "smatch_extra.h"

static int my_id;

STATE(uninitialized);
STATE(initialized);

static void match_declarations(struct symbol *sym)
{
	if (sym->initializer)
		return;

	/* Smatch is crap at tracking arrays */
	if ((get_base_type(sym))->type == SYM_ARRAY)
		return;
	if (sym->ctype.modifiers & MOD_STATIC)
		return;

	if (!sym->ident)
		return;
	set_state(my_id, sym->ident->name, sym, &uninitialized);
}

static void extra_mod_hook(const char *name, struct symbol *sym, struct smatch_state *state)
{
	if (!sym->ident)
		return;
	if (strcmp(name, sym->ident->name) != 0)
		return;
	set_state(my_id, name, sym, &initialized);
}

static void match_assign(struct expression *expr)
{
	struct expression *right;

	set_state_expr(my_id, expr->left, &initialized);

	right = strip_expr(expr->right);
	if (right->type == EXPR_PREOP && right->op == '&')
		set_state_expr(my_id, right->unop, &initialized);
}

static int is_initialized(struct expression *expr)
{
	struct sm_state *sm;

	expr = strip_expr(expr);
	if (expr->type != EXPR_SYMBOL)
		return 1;
	sm = get_sm_state_expr(my_id, expr);
	if (!sm)
		return 1;
	if (!slist_has_state(sm->possible, &uninitialized))
		return 1;
	return 0;
}

static void match_dereferences(struct expression *expr)
{
	char *name;

	if (expr->type != EXPR_PREOP)
		return;
	if (is_initialized(expr->unop))
		return;

	name = expr_to_str(expr->unop);
	sm_msg("error: XXX potentially dereferencing uninitialized '%s'.", name);
	free_string(name);

	set_state_expr(my_id, expr->unop, &initialized);
}


static void match_condition(struct expression *expr)
{
	char *name;

	if (is_initialized(expr))
		return;

	name = expr_to_str(expr);
	sm_msg("error: XXX potentially using uninitialized '%s'.", name);
	free_string(name);

	set_state_expr(my_id, expr, &initialized);
}

static void match_call(struct expression *expr)
{
	struct expression *arg;
	char *name;

	FOR_EACH_PTR(expr->args, arg) {
		if (is_initialized(arg))
			continue;

		name = expr_to_str(arg);
		sm_msg("warn: XXX passing uninitialized '%s'", name);
		free_string(name);

		set_state_expr(my_id, arg, &initialized);
	} END_FOR_EACH_PTR(arg);
}

void check_uninitialized(int id)
{
	my_id = id;

	add_hook(&match_declarations, DECLARATION_HOOK);
	add_extra_mod_hook(&extra_mod_hook);
	add_hook(&match_assign, ASSIGNMENT_HOOK);

	add_hook(&match_dereferences, DEREF_HOOK);
	add_hook(&match_condition, CONDITION_HOOK);
	add_hook(&match_call, FUNCTION_CALL_HOOK);
}

[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux