#include "smatch.h"
#include "smatch_extra.h"
static int my_id;
#define VAR_ON_RIGHT 0
#define VAR_ON_LEFT 1
static void match_assign(struct expression *expr)
{
struct symbol *sym;
sval_t sval;
sval_t max;
sval_t min;
char *left_name, *right_name;
if (__in_fake_assign)
return;
if (expr->op == SPECIAL_AND_ASSIGN || expr->op == SPECIAL_OR_ASSIGN)
return;
sym = get_type(expr->left);
if (!sym || sym->type != SYM_BASETYPE) {
return;
}
if (type_bits(sym) < 0 || type_bits(sym) >= 32)
return;
if (!get_implied_value(expr->right, &sval))
return;
max = sval_type_max(sym);
if (sym != &bool_ctype && sym != &uchar_ctype &&
sval_cmp(max, sval) < 0 &&
!(sval.value < 256 && max.value == 127)) {
left_name = expr_to_str(expr->left);
right_name = expr_to_str(expr->right);
sm_warning("'%s' %s can't fit into %s '%s'",
right_name, sval_to_numstr(sval), sval_to_numstr(max), left_name);
free_string(left_name);
}
min = sval_type_min(sym);
if (sval_cmp_t(&llong_ctype, min, sval) > 0) {
if (min.value == 0 && sval.value == -1)
return;
if (expr->right->type == EXPR_PREOP && expr->right->op == '~')
return;
if (expr->op == SPECIAL_SUB_ASSIGN || expr->op == SPECIAL_ADD_ASSIGN)
return;
if (sval_positive_bits(sval) == 7)
return;
left_name = expr_to_str(expr->left);
if (min.value == 0) {
sm_warning("assigning %s to unsigned variable '%s'",
sval_to_str(sval), left_name);
} else {
sm_warning("value %s can't fit into %s '%s'",
sval_to_str(sval), sval_to_str(min), left_name);
}
free_string(left_name);
}
}
static int cap_gt_zero_and_lt(struct expression *expr)
{
struct expression *var = expr->left;
struct expression *tmp;
char *name1 = NULL;
char *name2 = NULL;
sval_t known;
int ret = 0;
int i;
if (!get_value(expr->right, &known) || known.value != 0)
return 0;
i = 0;
FOR_EACH_PTR_REVERSE(big_expression_stack, tmp) {
if (!i++)
continue;
if (tmp->op == SPECIAL_LOGICAL_AND) {
struct expression *right = strip_expr(tmp->right);
if (right->op != '<' &&
right->op != SPECIAL_UNSIGNED_LT &&
right->op != SPECIAL_LTE &&
right->op != SPECIAL_UNSIGNED_LTE)
return 0;
name1 = expr_to_str(var);
if (!name1)
goto free;
name2 = expr_to_str(right->left);
if (!name2)
goto free;
if (!strcmp(name1, name2))
ret = 1;
goto free;
}
return 0;
} END_FOR_EACH_PTR_REVERSE(tmp);
free:
free_string(name1);
free_string(name2);
return ret;
}
static int cap_lt_zero_or_gt(struct expression *expr)
{
struct expression *var = expr->left;
struct expression *tmp;
char *name1 = NULL;
char *name2 = NULL;
sval_t known;
int ret = 0;
int i;
if (!get_value(expr->right, &known) || known.value != 0)
return 0;
i = 0;
FOR_EACH_PTR_REVERSE(big_expression_stack, tmp) {
if (!i++)
continue;
if (tmp->op == SPECIAL_LOGICAL_OR) {
struct expression *right = strip_expr(tmp->right);
if (right->op != '>' &&
right->op != SPECIAL_UNSIGNED_GT &&
right->op != SPECIAL_GTE &&
right->op != SPECIAL_UNSIGNED_GTE)
return 0;
name1 = expr_to_str(var);
if (!name1)
goto free;
name2 = expr_to_str(right->left);
if (!name2)
goto free;
if (!strcmp(name1, name2))
ret = 1;
goto free;
}
return 0;
} END_FOR_EACH_PTR_REVERSE(tmp);
free:
free_string(name1);
free_string(name2);
return ret;
}
static int cap_both_sides(struct expression *expr)
{
switch (expr->op) {
case '<':
case SPECIAL_UNSIGNED_LT:
case SPECIAL_LTE:
case SPECIAL_UNSIGNED_LTE:
return cap_lt_zero_or_gt(expr);
case '>':
case SPECIAL_UNSIGNED_GT:
case SPECIAL_GTE:
case SPECIAL_UNSIGNED_GTE:
return cap_gt_zero_and_lt(expr);
}
return 0;
}
static int compare_against_macro(struct expression *expr)
{
sval_t known;
if (expr->op != SPECIAL_UNSIGNED_LT)
return 0;
if (!get_value(expr->right, &known) || known.value != 0)
return 0;
return !!get_macro_name(expr->right->pos);
}
static int print_unsigned_never_less_than_zero(struct expression *expr)
{
sval_t known;
char *name;
if (expr->op != SPECIAL_UNSIGNED_LT)
return 0;
if (!get_value(expr->right, &known) || known.value != 0)
return 0;
name = expr_to_str(expr->left);
sm_warning("unsigned '%s' is never less than zero.", name);
free_string(name);
return 1;
}
static void match_condition(struct expression *expr)
{
struct symbol *type;
sval_t known;
sval_t min, max;
struct range_list *rl_left_orig, *rl_right_orig;
struct range_list *rl_left, *rl_right;
if (expr->type != EXPR_COMPARE)
return;
type = get_type(expr);
if (!type)
return;
if (get_macro_name(expr->pos))
return;
if (compare_against_macro(expr))
return;
if (cap_both_sides(expr))
return;
if (print_unsigned_never_less_than_zero(expr))
return;
if (get_value(expr->left, &known)) {
if (get_value(expr->right, &known))
return;
rl_left_orig = alloc_rl(known, known);
rl_left = cast_rl(type, rl_left_orig);
min = sval_type_min(get_type(expr->right));
max = sval_type_max(get_type(expr->right));
rl_right_orig = alloc_rl(min, max);
rl_right = cast_rl(type, rl_right_orig);
} else if (get_value(expr->right, &known)) {
rl_right_orig = alloc_rl(known, known);
rl_right = cast_rl(type, rl_right_orig);
min = sval_type_min(get_type(expr->left));
max = sval_type_max(get_type(expr->left));
rl_left_orig = alloc_rl(min, max);
rl_left = cast_rl(type, rl_left_orig);
} else {
return;
}
if (!possibly_true_rl(rl_left, expr->op, rl_right)) {
char *name = expr_to_str(expr);
sm_warning("impossible condition '(%s) => (%s %s %s)'", name,
show_rl(rl_left), show_special(expr->op),
show_rl(rl_right));
free_string(name);
}
if (!possibly_false_rl(rl_left, expr->op, rl_right)) {
char *name = expr_to_str(expr);
sm_warning("always true condition '(%s) => (%s %s %s)'", name,
show_rl(rl_left_orig), show_special(expr->op),
show_rl(rl_right_orig));
free_string(name);
}
}
void check_signed(int id)
{
my_id = id;
add_hook(&match_assign, ASSIGNMENT_HOOK);
add_hook(&match_condition, CONDITION_HOOK);
}