root/usr/src/tools/smatch/src/check_signed.c
/*
 * Copyright (C) 2009 Dan Carpenter.
 *
 * 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
 */

/*
 * Check for things which are signed but probably should be unsigned.
 *
 * Hm...  It seems like at this point in the processing, sparse makes all
 * bitfields unsigned.  Which is logical but not what GCC does.
 *
 */

#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) {
                //sm_msg("could not get type");
                return;
        }
        if (type_bits(sym) < 0 || type_bits(sym) >= 32) /* max_val limits this */
                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) /* assigning -1 to unsigned variables is idiomatic */
                        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;

        /* screw it.  I am writing this to mark yoda code as buggy.
         * Valid comparisons between an unsigned and zero are:
         * 1) inside a macro.
         * 2) foo < LOWER_BOUND where LOWER_BOUND is a macro.
         * 3) foo < 0 || foo > X in exactly this format.  No Yoda.
         * 4) foo >= 0 && foo < X
         */
        if (get_macro_name(expr->pos))
                return;
        if (compare_against_macro(expr))
                return;
        if (cap_both_sides(expr))
                return;

        /* This is a special case for the common error */
        if (print_unsigned_never_less_than_zero(expr))
                return;

        /* check that one and only one side is known */
        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);
}