C-sparse

 view release on metacpan or  search on metacpan

src/sparse-0.4.4/sparse.c  view on Meta::CPAN

		}
		sum += val;
	} END_FOR_EACH_PTR(insn);
	return sum;
}

static int imbalance(SCTX_ struct entrypoint *ep, struct basic_block *bb, int entry, int exit, const char *why)
{
	if (sctxp Wcontext) {
		struct symbol *sym = ep->name;
		warning(sctx_ bb->pos->pos, "context imbalance in '%s' - %s", show_ident(sctx_ sym->ident), why);
	}
	return -1;
}

static int check_bb_context(SCTX_ struct entrypoint *ep, struct basic_block *bb, int entry, int exit);

static int check_children(SCTX_ struct entrypoint *ep, struct basic_block *bb, int entry, int exit)
{
	struct instruction *insn;
	struct basic_block *child;

	insn = last_instruction(sctx_ bb->insns);
	if (!insn)
		return 0;
	if (insn->opcode == OP_RET)
		return entry != exit ? imbalance(sctx_ ep, bb, entry, exit, "wrong count at exit") : 0;

	FOR_EACH_PTR(bb->children, child) {
		if (check_bb_context(sctx_ ep, child, entry, exit))
			return -1;
	} END_FOR_EACH_PTR(child);
	return 0;
}

static int check_bb_context(SCTX_ struct entrypoint *ep, struct basic_block *bb, int entry, int exit)
{
	if (!bb)
		return 0;
	if (bb->context == entry)
		return 0;

	/* Now that's not good.. */
	if (bb->context >= 0)
		return imbalance(sctx_ ep, bb, entry, bb->context, "different lock contexts for basic block");

	bb->context = entry;
	entry += context_increase(sctx_ bb, entry);
	if (entry < 0)
		return imbalance(sctx_ ep, bb, entry, exit, "unexpected unlock");

	return check_children(sctx_ ep, bb, entry, exit);
}

static void check_cast_instruction(SCTX_ struct instruction *insn)
{
	struct symbol *orig_type = insn->orig_type;
	if (orig_type) {
		int old = orig_type->bit_size;
		int new = insn->size;
		int oldsigned = (orig_type->ctype.modifiers & MOD_SIGNED) != 0;
		int newsigned = insn->opcode == OP_SCAST;

		if (new > old) {
			if (oldsigned == newsigned)
				return;
			if (newsigned)
				return;
			warning(sctx_ insn->pos, "cast loses sign");
			return;
		}
		if (new < old) {
			warning(sctx_ insn->pos, "cast drops bits");
			return;
		}
		if (oldsigned == newsigned) {
			warning(sctx_ insn->pos, "cast wasn't removed");
			return;
		}
		warning(sctx_ insn->pos, "cast changes sign");
	}
}

static void check_range_instruction(SCTX_ struct instruction *insn)
{
	warning(sctx_ insn->pos, "value out of range");
}

static void check_byte_count(SCTX_ struct instruction *insn, pseudo_t count)
{
	if (!count)
		return;
	if (count->type == PSEUDO_VAL) {
		long long val = count->value;
		if (val <= 0 || val > 100000)
			warning(sctx_ insn->pos, "%s with byte count of %lld",
				show_ident(sctx_ insn->func->sym->ident), val);
		return;
	}
	/* OK, we could try to do the range analysis here */
}

static pseudo_t argument(SCTX_ struct instruction *call, unsigned int argno)
{
	pseudo_t args[8];
	struct ptr_list *arg_list = (struct ptr_list *) call->arguments;

	argno--;
	if (linearize_ptr_list(sctx_ arg_list, (void *)args, 8) > argno)
		return args[argno];
	return NULL;
}

static void check_memset(SCTX_ struct instruction *insn)
{
	check_byte_count(sctx_ insn, argument(sctx_ insn, 3));
}

#define check_memcpy check_memset
#define check_ctu check_memset
#define check_cfu check_memset

struct checkfn {
	struct ident *id;
	void (*check)(SCTX_ struct instruction *insn);
};

static void check_call_instruction(SCTX_ struct instruction *insn)
{
	pseudo_t fn = insn->func;
	struct ident *ident;
	/*static*/ const struct checkfn check_fn[] = {
		{ (struct ident *)&sctxp memset_ident, check_memset },
		{ (struct ident *)&sctxp memcpy_ident, check_memcpy },
		{ (struct ident *)&sctxp copy_to_user_ident, check_ctu },
		{ (struct ident *)&sctxp copy_from_user_ident, check_cfu },



( run in 0.440 second using v1.01-cache-2.11-cpan-71847e10f99 )