From 5bbf776c34d31202322bca9eec36fe5235db9147 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 17 Dec 2013 18:45:39 +0300 Subject: [PATCH] common_functions.c: handle strlen() and strnlen() better Ideally most functions can be handled using the cross function database but strlen() is too important so I handle it in code. Signed-off-by: Dan Carpenter --- Makefile | 3 ++- check_list.h | 1 + smatch_common_functions.c | 63 +++++++++++++++++++++++++++++++++++++++++++++ smatch_data/db/fixup_all.sh | 3 ++- validation/sm_strlen2.c | 16 +++++++----- validation/sm_strlen3.c | 20 ++++++++++++++ 6 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 smatch_common_functions.c create mode 100644 validation/sm_strlen3.c diff --git a/Makefile b/Makefile index 9dc3980f..1a73da06 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,8 @@ SMATCH_FILES=smatch_flow.o smatch_conditions.o smatch_slist.o smatch_states.o \ smatch_param_set.o smatch_comparison.o smatch_local_values.o \ smatch_function_ptrs.o smatch_annotate.o smatch_string_list.o \ smatch_param_cleared.o smatch_clear_buffer.o smatch_start_states.o \ - smatch_recurse.o smatch_data_source.o smatch_type_val.o + smatch_recurse.o smatch_data_source.o smatch_type_val.o \ + smatch_common_functions.o SMATCH_CHECKS=$(shell ls check_*.c | sed -e 's/\.c/.o/') SMATCH_DATA=smatch_data/kernel.allocation_funcs smatch_data/kernel.balanced_funcs \ diff --git a/check_list.h b/check_list.h index ed4edb50..b3cf8849 100644 --- a/check_list.h +++ b/check_list.h @@ -27,6 +27,7 @@ CK(register_annotate) CK(register_start_states) CK(register_type_val) CK(register_data_source) +CK(register_common_functions) CK(check_debug) CK(check_assigned_expr) diff --git a/smatch_common_functions.c b/smatch_common_functions.c new file mode 100644 index 00000000..8788a5df --- /dev/null +++ b/smatch_common_functions.c @@ -0,0 +1,63 @@ +/* + * smatch/smatch_common_functions.c + * + * Copyright (C) 2013 Oracle. + * + * Licensed under the Open Software License version 1.1 + * + */ + +#include "scope.h" +#include "smatch.h" +#include "smatch_extra.h" + +static int match_strlen(struct expression *call, void *unused, struct range_list **rl) +{ + struct expression *str; + unsigned long max; + + str = get_argument_from_call_expr(call->args, 0); + if (get_implied_strlen(str, rl) && sval_is_positive(rl_min(*rl))) { + *rl = cast_rl(&ulong_ctype, *rl); + return 1; + } + /* smatch_strlen.c is not very complete */ + max = get_array_size_bytes_max(str); + if (max == 0) { + *rl = alloc_whole_rl(&ulong_ctype); + } else { + max--; + *rl = alloc_rl(ll_to_sval(0), ll_to_sval(max)); + } + return 1; +} + +static int match_strnlen(struct expression *call, void *unused, struct range_list **rl) +{ + struct expression *limit; + sval_t fixed; + sval_t bound; + sval_t ulong_max = sval_type_val(&ulong_ctype, ULONG_MAX); + + match_strlen(call, NULL, rl); + limit = get_argument_from_call_expr(call->args, 1); + if (!get_implied_max(limit, &bound)) + return 1; + if (sval_cmp(bound, ulong_max) == 0) + return 1; + if (rl_to_sval(*rl, &fixed) && sval_cmp(fixed, bound) >= 0) { + *rl = alloc_rl(bound, bound); + return 1; + } + + bound.value++; + *rl = remove_range(*rl, bound, ulong_max); + + return 1; +} + +void register_common_functions(int id) +{ + add_implied_return_hook("strlen", &match_strlen, NULL); + add_implied_return_hook("strnlen", &match_strnlen, NULL); +} diff --git a/smatch_data/db/fixup_all.sh b/smatch_data/db/fixup_all.sh index 6df89ad7..a54c9cdf 100755 --- a/smatch_data/db/fixup_all.sh +++ b/smatch_data/db/fixup_all.sh @@ -3,7 +3,8 @@ # mark some paramaters as coming from user space cat << EOF | sqlite3 smatch_db.sqlite -update return_states set return = '0-s32max[<=p1]' where function = 'strnlen'; +delete from return_states where function = 'strlen'; +delete from return_states where function = 'strnlen'; EOF diff --git a/validation/sm_strlen2.c b/validation/sm_strlen2.c index 02a13b1c..3af86a68 100644 --- a/validation/sm_strlen2.c +++ b/validation/sm_strlen2.c @@ -3,25 +3,27 @@ int strcpy(char *str); void func (char *input1, char *input2, char *input3) { - char buf[4]; + char buf1[4]; + char buf2[4]; + char buf3[4]; if (strlen(input1) > 4) return; - strcpy(buf, input1); + strcpy(buf1, input1); if (10 > strlen(input2)) - strcpy(buf, input2); + strcpy(buf2, input2); if (strlen(input3) <= 4) - strcpy(buf, input3); + strcpy(buf3, input3); } /* * check-name: Smatch strlen test #2 * check-command: smatch sm_strlen2.c * * check-output-start -sm_strlen2.c:10 func() error: strcpy() 'input1' too large for 'buf' (5 vs 4) -sm_strlen2.c:13 func() error: strcpy() 'input2' too large for 'buf' (10 vs 4) -sm_strlen2.c:16 func() error: strcpy() 'input3' too large for 'buf' (5 vs 4) +sm_strlen2.c:12 func() error: strcpy() 'input1' too large for 'buf1' (5 vs 4) +sm_strlen2.c:15 func() error: strcpy() 'input2' too large for 'buf2' (10 vs 4) +sm_strlen2.c:18 func() error: strcpy() 'input3' too large for 'buf3' (5 vs 4) * check-output-end */ diff --git a/validation/sm_strlen3.c b/validation/sm_strlen3.c new file mode 100644 index 00000000..b147c1a7 --- /dev/null +++ b/validation/sm_strlen3.c @@ -0,0 +1,20 @@ +#include "check_debug.h" + +int strlen(const char *str); +int strnlen(const char *str, int limit); + +int func(void) +{ + __smatch_implied(strlen("foo")); + __smatch_implied(strnlen("foo", 2)); +} + +/* + * check-name: Smatch strlen test #3 + * check-command: smatch -I.. sm_strlen3.c + * + * check-output-start +sm_strlen3.c:8 func() implied: strlen("foo") = '3' +sm_strlen3.c:9 func() implied: strnlen("foo", 2) = '2' + * check-output-end + */ -- 2.11.4.GIT