Whenever a bit is set or cleared using bitwise operations inside a funciton body, the states are set using database entries. Signed-off-by: Harshvardhan Jha <harshvardhan.jha@xxxxxxxxxx> --- Makefile | 2 + check_list.h | 2 + smatch.h | 21 +++++++++ smatch_bits.c | 69 ++++++++++++++++++++++++---- smatch_extra.c | 26 +++++++---- smatch_param_bits_clear.c | 96 +++++++++++++++++++++++++++++++++++++++ smatch_param_bits_set.c | 76 +++++++++++++++++++++++++++++++ 7 files changed, 272 insertions(+), 20 deletions(-) create mode 100644 smatch_param_bits_clear.c create mode 100644 smatch_param_bits_set.c diff --git a/Makefile b/Makefile index 8855c974..3169b49e 100644 --- a/Makefile +++ b/Makefile @@ -290,6 +290,8 @@ SMATCH_OBJS += smatch_mtag_data.o SMATCH_OBJS += smatch_mtag_map.o SMATCH_OBJS += smatch_mtag.o SMATCH_OBJS += smatch_nul_terminator.o +SMATCH_OBJS += smatch_param_bits_set.o +SMATCH_OBJS += smatch_param_bits_clear.o SMATCH_OBJS += smatch_param_cleared.o SMATCH_OBJS += smatch_param_compare_limit.o SMATCH_OBJS += smatch_parameter_names.o diff --git a/check_list.h b/check_list.h index fd205269..a57715ea 100644 --- a/check_list.h +++ b/check_list.h @@ -169,6 +169,8 @@ CK(check_atomic_test) CK(check_checking_for_null_instead_of_err_ptr) CK(check_syscall_arg_type) CK(check_trinity_generator) +CK(register_param_bits_set) +CK(register_param_bits_clear) /* <- your test goes here */ /* CK(register_template) */ diff --git a/smatch.h b/smatch.h index 025ad1a8..e7957ef9 100644 --- a/smatch.h +++ b/smatch.h @@ -848,6 +848,10 @@ enum info_type { FRESH_ALLOC = 1044, ALLOCATOR = 1045, FUNC_TIME = 1047, + BIT_SET = 1051, + BIT_CLEAR = 1052, + BIT_IS_SET = 1053, + BIT_IS_CLEAR = 1054, /* put random temporary stuff in the 7000-7999 range for testing */ USER_DATA = 8017, @@ -1311,10 +1315,27 @@ int get_mtag_sval(struct expression *expr, sval_t *sval); /* Trinity fuzzer stuff */ const char *get_syscall_arg_type(struct symbol *sym); +/* smatch_bits.c */ +int binfo_equiv(struct bit_info *one, struct bit_info *two); +struct bit_info *alloc_bit_info(unsigned long long set, unsigned long long possible); +struct smatch_state *alloc_bstate(unsigned long long set, unsigned long long possible); +struct smatch_state *merge_bstates(struct smatch_state *one_state, struct smatch_state *two_state); + +/* smatch_param_bits_set.c */ +void __set_param_modified_helper(struct expression *expr, struct smatch_state *state); +void __set_param_modified_helper_sym(const char *name, struct symbol *sym, + struct smatch_state *state); + +/* smatch_param_bits_clear.c */ +void __set_param_modified_helper_clear(struct expression *expr, struct smatch_state *state); +void __set_param_modified_helper_sym_clear(const char *name, struct symbol *sym, + struct smatch_state *state); + /* smatch_bit_info.c */ struct bit_info *rl_to_binfo(struct range_list *rl); struct bit_info *get_bit_info(struct expression *expr); struct bit_info *get_bit_info_var_sym(const char *name, struct symbol *sym); + /* smatch_mem_tracker.c */ extern int option_mem; unsigned long get_mem_kb(void); diff --git a/smatch_bits.c b/smatch_bits.c index 5e310a13..6608d712 100644 --- a/smatch_bits.c +++ b/smatch_bits.c @@ -31,7 +31,7 @@ static const struct bit_info unknown_bit_info = { }; ALLOCATOR(bit_info, "bit data"); -static struct bit_info *alloc_bit_info(unsigned long long set, unsigned long long possible) +struct bit_info *alloc_bit_info(unsigned long long set, unsigned long long possible) { struct bit_info *bit_info = __alloc_bit_info(0); @@ -41,7 +41,18 @@ static struct bit_info *alloc_bit_info(unsigned long long set, unsigned long lon return bit_info; } -static struct smatch_state *alloc_bstate(unsigned long long set, unsigned long long possible) +void set_bits_modified_expr(struct expression *expr, struct smatch_state *state) +{ + __set_param_modified_helper(expr, state); + set_state_expr(my_id, expr, state); +} + +void set_bits_modified_expr_sym(const char *name, struct symbol *sym, struct smatch_state *state) +{ + __set_param_modified_helper_sym(name, sym, state); + set_state(my_id, name, sym, state); +} +struct smatch_state *alloc_bstate(unsigned long long set, unsigned long long possible) { struct smatch_state *state; char buf[64]; @@ -151,10 +162,11 @@ static void match_modify(struct sm_state *sm, struct expression *mod_expr) if (handled_by_assign_hook(mod_expr)) return; - set_state(my_id, sm->name, sm->sym, alloc_bstate(0, -1ULL)); + + set_bits_modified_expr_sym(sm->name, sm->sym, alloc_bstate(0, -1ULL)); } -static int binfo_equiv(struct bit_info *one, struct bit_info *two) +int binfo_equiv(struct bit_info *one, struct bit_info *two) { if (one->set == two->set && one->possible == two->possible) @@ -162,7 +174,7 @@ static int binfo_equiv(struct bit_info *one, struct bit_info *two) return 0; } -static struct smatch_state *merge_bstates(struct smatch_state *one_state, struct smatch_state *two_state) +struct smatch_state *merge_bstates(struct smatch_state *one_state, struct smatch_state *two_state) { struct bit_info *one, *two; @@ -333,16 +345,15 @@ static void match_assign(struct expression *expr) if (expr->op == '=') { if (is_unknown_binfo(get_type(expr->left), binfo)) return; - - set_state_expr(my_id, expr->left, alloc_bstate(binfo->set, binfo->possible)); + set_bits_modified_expr(expr->left, alloc_bstate(binfo->set, binfo->possible)); } else if (expr->op == SPECIAL_OR_ASSIGN) { start = get_bit_info(expr->left); new = alloc_bstate(start->set | binfo->set, start->possible | binfo->possible); - set_state_expr(my_id, expr->left, new); + set_bits_modified_expr(expr->left, new); } else if (expr->op == SPECIAL_AND_ASSIGN) { start = get_bit_info(expr->left); new = alloc_bstate(start->set & binfo->set, start->possible & binfo->possible); - set_state_expr(my_id, expr->left, new); + set_bits_modified_expr(expr->left, new); } } @@ -454,7 +465,42 @@ static void set_param_bits(const char *name, struct symbol *sym, char *key, char value++; possible = strtoull(value, &value, 16); - set_state(my_id, fullname, sym, alloc_bstate(set, possible)); + set_bits_modified_expr_sym(fullname, sym, alloc_bstate(set, possible)); +} + +static void returns_bit_set(struct expression *expr, int param, char *key, char *value) +{ + char *name; + struct symbol *sym; + unsigned long long set; + char *pEnd; + + name = get_name_sym_from_key(expr, param, key, &sym); + + if (!name) + return; + + set = strtoull(value, &pEnd, 16); + set_state(my_id, name, sym, alloc_bstate(set, -1ULL)); +} + +static void returns_bit_clear(struct expression *expr, int param, char *key, char *value) +{ + char *name; + struct symbol *sym; + unsigned long long possible; + char *pEnd; + struct bit_info *binfo; + + name = get_name_sym_from_key(expr, param, key, &sym); + + if (!name) + return; + + binfo = get_bit_info(expr); + possible = strtoull(value, &pEnd, 16); + set_state(my_id, name, sym, alloc_bstate(possible & binfo->set, + possible & binfo->possible)); } void register_bits(int id) @@ -474,4 +520,7 @@ void register_bits(int id) add_hook(&match_call_info, FUNCTION_CALL_HOOK); add_member_info_callback(my_id, struct_member_callback); select_caller_info_hook(set_param_bits, BIT_INFO); + + select_return_states_hook(BIT_SET, &returns_bit_set); + select_return_states_hook(BIT_CLEAR, &returns_bit_clear); } diff --git a/smatch_extra.c b/smatch_extra.c index 25c1c990..c2e2735c 100644 --- a/smatch_extra.c +++ b/smatch_extra.c @@ -2059,7 +2059,7 @@ static sval_t get_high_mask(sval_t known) static bool handle_bit_test(struct expression *expr) { - struct range_list *orig_rl, *rl; + struct range_list *orig_rl, *rlt, *rlf, *true_rl, *false_rl; struct expression *shift, *mask, *var; struct bit_info *bit_info; sval_t sval; @@ -2081,23 +2081,29 @@ static bool handle_bit_test(struct expression *expr) bit_info = get_bit_info(mask); if (!bit_info) return false; - if (!bit_info->possible) + if (!bit_info->possible){ + set_true_false_states_expr(my_id, var, alloc_estate_empty(), NULL); return false; + } get_absolute_rl(var, &orig_rl); if (sval_is_negative(rl_min(orig_rl)) || rl_max(orig_rl).uvalue > type_bits(get_type(shift->left))) return false; - low.value = ffsll(bit_info->possible); - high.value = sm_fls64(bit_info->possible); - rl = alloc_rl(low, high); - rl = cast_rl(get_type(var), rl); - rl = rl_intersection(orig_rl, rl); - if (!rl) - return false; + low.value = ffsll(bit_info->possible) - 1; + high.value = sm_fls64(bit_info->possible) - 1; + rlt = alloc_rl(low, high); + rlt = cast_rl(get_type(var), rlt); + true_rl = rl_intersection(orig_rl, rlt); + + low.value = ffsll(bit_info->set) - 1; + high.value = sm_fls64(bit_info->set) - 1; + rlf = alloc_rl(low, high); + rlf = cast_rl(get_type(var), rlf); + false_rl = rl_filter(orig_rl, rlf); - set_extra_expr_true_false(shift->right, alloc_estate_rl(rl), NULL); + set_extra_expr_true_false(shift->right, alloc_estate_rl(true_rl), alloc_estate_rl(false_rl)); return true; } diff --git a/smatch_param_bits_clear.c b/smatch_param_bits_clear.c new file mode 100644 index 00000000..6632df81 --- /dev/null +++ b/smatch_param_bits_clear.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2021 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 "scope.h" +#include "smatch.h" +#include "smatch_slist.h" +#include "smatch_extra.h" + +static int my_id; + +static struct smatch_state *unmatched_state(struct sm_state *sm) +{ + return alloc_bstate(-1ULL, -1ULL); +} + +static void match_assign(struct expression *expr) +{ + sval_t sval; + + if (expr->type != EXPR_ASSIGNMENT) + return; + + if (expr->op != SPECIAL_AND_ASSIGN) + return; + + if (!get_implied_value(expr->right, &sval)) + return; + + set_state_expr(my_id, expr->left, alloc_bstate(0, sval.uvalue)); +} + +static void return_info_callback(int return_id, char *return_ranges, + struct expression *returned_expr, + int param, + const char *printed_name, + struct sm_state *sm) +{ + unsigned long long bits_clear; + char buffer[64]; + struct smatch_state *estate; + struct bit_info *binfo; + sval_t sval; + + estate = get_state(SMATCH_EXTRA, sm->name, sm->sym); + if (estate_get_single_value(estate, &sval)) + return; + + binfo = sm->state->data; + bits_clear = binfo->possible; + + if (bits_clear == 0) + return; + + sprintf(buffer, "0x%llx", bits_clear); + sql_insert_return_states(return_id, return_ranges, BIT_CLEAR, param, printed_name, buffer); +} + +struct smatch_state *merge_bstates_clear(struct smatch_state *one_state, + struct smatch_state *two_state) +{ + struct bit_info *one, *two; + + one = one_state->data; + two = two_state->data; + + if (binfo_equiv(one, two)) + return one_state; + + return alloc_bstate(0, one->possible | two->possible); +} + +void register_param_bits_clear(int id) +{ + my_id = id; + + set_dynamic_states(my_id); + add_hook(&match_assign, ASSIGNMENT_HOOK); + add_unmatched_state_hook(my_id, &unmatched_state); + add_merge_hook(my_id, &merge_bstates); + + add_return_info_callback(my_id, return_info_callback); +} diff --git a/smatch_param_bits_set.c b/smatch_param_bits_set.c new file mode 100644 index 00000000..32920490 --- /dev/null +++ b/smatch_param_bits_set.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2021 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 "scope.h" +#include "smatch.h" +#include "smatch_slist.h" +#include "smatch_extra.h" + +static int my_id; + +void __set_param_modified_helper(struct expression *expr, struct smatch_state *state) +{ + set_state_expr(my_id, expr, state); +} + +void __set_param_modified_helper_sym(const char *name, struct symbol *sym, + struct smatch_state *state) +{ + set_state(my_id, name, sym, state); +} + +static struct smatch_state *unmatched_state(struct sm_state *sm) +{ + return alloc_bstate(0, 0); +} + +static void return_info_callback(int return_id, char *return_ranges, + struct expression *returned_expr, + int param, + const char *printed_name, + struct sm_state *sm) +{ + unsigned long long bits_set; + char buffer[64]; + struct smatch_state *estate; + struct bit_info *binfo; + sval_t sval; + + estate = get_state(SMATCH_EXTRA, sm->name, sm->sym); + if (estate_get_single_value(estate, &sval)) + return; + + binfo = sm->state->data; + bits_set = binfo->set; + + if (bits_set == 0) + return; + + sprintf(buffer, "0x%llx", bits_set); + sql_insert_return_states(return_id, return_ranges, BIT_SET, param, printed_name, buffer); +} + +void register_param_bits_set(int id) +{ + my_id = id; + + set_dynamic_states(my_id); + + add_unmatched_state_hook(my_id, &unmatched_state); + add_merge_hook(my_id, &merge_bstates); + + add_return_info_callback(my_id, return_info_callback); +} -- 2.32.0