root/usr/src/tools/smatch/src/smatch_strings.c
/*
 * Copyright (C) 2015 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;

static int get_str(void *_ret, int argc, char **argv, char **azColName)
{
        char **ret = _ret;

        if (*ret)
                *ret = (void *)-1UL;
        else
                *ret = alloc_sname(argv[0]);

        return 0;
}

static char *get_string_from_mtag(mtag_t tag)
{
        char *str = NULL;

        run_sql(get_str, &str,
                "select value from mtag_data where tag = %lld and offset = 0 and type = %d;",
                tag, STRING_VALUE);

        if ((unsigned long)str == -1UL)
                return NULL;
        return str;
}

struct expression *fake_string_from_mtag(mtag_t tag)
{
        char *str;

        if (!tag)
                return NULL;
        str = get_string_from_mtag(tag);
        if (!str)
                return NULL;
        return string_expression(str);
}

static void match_strcpy(const char *fn, struct expression *expr, void *unused)
{
        struct expression *dest, *src;

        dest = get_argument_from_call_expr(expr->args, 0);
        src = get_argument_from_call_expr(expr->args, 1);
        src = strip_expr(src);
        if (src->type == EXPR_STRING)
                set_state_expr(my_id, dest, alloc_state_str(src->string->data));
}

struct state_list *get_strings(struct expression *expr)
{
        struct state_list *ret = NULL;
        struct smatch_state *state;
        struct sm_state *sm;

        expr = strip_expr(expr);
        if (expr->type == EXPR_STRING) {
                state = alloc_state_str(expr->string->data);
                sm = alloc_sm_state(my_id, expr->string->data, NULL, state);
                add_ptr_list(&ret, sm);
                return ret;
        }

        if (expr->type == EXPR_CONDITIONAL ||
            expr->type == EXPR_SELECT) {
                struct state_list *true_strings = NULL;
                struct state_list *false_strings = NULL;

                if (known_condition_true(expr->conditional))
                        return get_strings(expr->cond_true);
                if (known_condition_false(expr->conditional))
                        return get_strings(expr->cond_false);

                true_strings = get_strings(expr->cond_true);
                false_strings = get_strings(expr->cond_false);
                concat_ptr_list((struct ptr_list *)true_strings, (struct ptr_list **)&false_strings);
                free_slist(&true_strings);
                return false_strings;
        }

        sm = get_sm_state_expr(my_id, expr);
        if (!sm)
                return NULL;

        return clone_slist(sm->possible);
}

static void match_assignment(struct expression *expr)
{
        struct state_list *slist;
        struct sm_state *sm;

        if (expr->op != '=')
                return;

        slist = get_strings(strip_expr(expr->right));
        if (!slist)
                return;

        if (ptr_list_size((struct ptr_list *)slist) == 1) {
                sm = first_ptr_list((struct ptr_list *)slist);
                set_state_expr(my_id, expr->left, sm->state);
                return;
        }
}

static void match_string(struct expression *expr)
{
        mtag_t tag;

        if (expr->type != EXPR_STRING || !expr->string->data)
                return;
        if (expr->string->length > 255)
                return;

        if (!get_string_mtag(expr, &tag))
                return;

        cache_sql(NULL, NULL, "insert into mtag_data values (%lld, %d, %d, '%q');",
                  tag, 0, STRING_VALUE, escape_newlines(expr->string->data));
}

void register_strings(int id)
{
        my_id = id;

        add_function_hook("strcpy", &match_strcpy, NULL);
        add_function_hook("strlcpy", &match_strcpy, NULL);
        add_function_hook("strncpy", &match_strcpy, NULL);

        add_hook(&match_assignment, ASSIGNMENT_HOOK);
        add_hook(&match_string, STRING_HOOK);

}