https://gcc.gnu.org/g:e33409a83251d057eb03b22e8f95f3ad7bf822e1
commit r16-1775-ge33409a83251d057eb03b22e8f95f3ad7bf822e1 Author: Jakub Jelinek <ja...@redhat.com> Date: Mon Jun 30 11:08:16 2025 +0200 Extend nonnull_if_nonzero attribute [PR120520] C2Y voted in the https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3466.pdf paper, which clarifies some of the conditional nonnull cases. For strncat/__strncat_chk no changes are necessary, we already use __attribute__((nonnull (1), nonnull_if_nonzero (2, 3))) attributes on the builtin and glibc can do the same too, meaning that first argument must be nonnull always and second must be nonnull if the third one is nonzero. The problem is with the fread/fwrite changes, where the paper adds: If size or nmemb is zero, +ptr may be a null pointer, fread returns zero and the contents of the array and the state of the stream remain unchanged. and ditto for fwrite, so the two argument nonnull_if_nonzero attribute isn't usable to express that, because whether the pointer can be null depends on 2 integral arguments rather than one. The following patch extends the nonnull_if_nonzero attribute, so that instead of requiring 2 arguments it allows 2 or 3, the first one is still the pointer argument index which sometimes must not be null and the other one or two are integral arguments, if there are 2, the invalid case is only if pointer is null and both the integral arguments are nonzero. 2025-06-30 Jakub Jelinek <ja...@redhat.com> PR c/120520 PR c/117023 gcc/ * builtin-attrs.def (DEF_LIST_INT_INT_INT): Define it and use for 1,2,3. (ATTR_NONNULL_IF123_LIST): New DEF_ATTR_TREE_LIST. (ATTR_NONNULL_4_IF123_LIST): Likewise. * builtins.def (BUILT_IN_FWRITE): Use ATTR_NONNULL_4_IF123_LIST instead of ATTR_NONNULL_LIST. (BUILT_IN_FWRITE_UNLOCKED): Likewise. * gimple.h (infer_nonnull_range_by_attribute): Add another optional tree * argument defaulted to NULL. * gimple.cc (infer_nonnull_range_by_attribute): Add OP3 argument, handle 3 argument nonnull_if_nonzero attribute. * builtins.cc (validate_arglist): Handle 3 argument nonnull_if_nonzero attribute. * tree-ssa-ccp.cc (pass_post_ipa_warn::execute): Likewise. * ubsan.cc (instrument_nonnull_arg): Adjust infer_nonnull_range_by_attribute caller, handle 3 argument nonnull_if_nonzero attribute. * gimple-range-infer.cc (gimple_infer_range::gimple_infer_range): Handle 3 argument nonnull_if_nonzero attribute. * doc/extend.texi (nonnull_if_nonzero): Document 3 argument version of the attribute. gcc/c-family/ * c-attribs.cc (c_common_gnu_attributes): Allow 2 or 3 arguments for nonnull_if_nonzero attribute instead of only 2. (handle_nonnull_if_nonzero_attribute): Handle 3 argument nonnull_if_nonzero. * c-common.cc (struct nonnull_arg_ctx): Rename other member to other1, add other2 member. (check_function_nonnull): Clear a if nonnull attribute has an argument. Adjust for nonnull_arg_ctx changes. Handle 3 argument nonnull_if_nonzero attribute. (check_nonnull_arg): Adjust for nonnull_arg_ctx changes, emit different diagnostics for 3 argument nonnull_if_nonzero attributes. (check_function_arguments): Adjust ctx var initialization. gcc/analyzer/ * sm-malloc.cc (malloc_state_machine::on_stmt): Handle 3 argument nonnull_if_nonzero attribute. gcc/testsuite/ * gcc.dg/nonnull-9.c: Tweak for 3 argument nonnull_if_nonzero attribute support, add further tests. * gcc.dg/nonnull-12.c: New test. * gcc.dg/nonnull-13.c: New test. * gcc.dg/nonnull-14.c: New test. * c-c++-common/ubsan/nonnull-8.c: New test. * c-c++-common/ubsan/nonnull-9.c: New test. Diff: --- gcc/analyzer/sm-malloc.cc | 16 +- gcc/builtin-attrs.def | 14 ++ gcc/builtins.cc | 8 +- gcc/builtins.def | 4 +- gcc/c-family/c-attribs.cc | 13 +- gcc/c-family/c-common.cc | 40 +++-- gcc/doc/extend.texi | 22 ++- gcc/gimple-range-infer.cc | 26 +++- gcc/gimple.cc | 35 +++-- gcc/gimple.h | 3 +- gcc/testsuite/c-c++-common/ubsan/nonnull-8.c | 32 ++++ gcc/testsuite/c-c++-common/ubsan/nonnull-9.c | 41 ++++++ gcc/testsuite/gcc.dg/nonnull-12.c | 73 ++++++++++ gcc/testsuite/gcc.dg/nonnull-13.c | 210 +++++++++++++++++++++++++++ gcc/testsuite/gcc.dg/nonnull-14.c | 23 +++ gcc/testsuite/gcc.dg/nonnull-9.c | 36 +++-- gcc/tree-ssa-ccp.cc | 42 +++++- gcc/ubsan.cc | 25 +++- 18 files changed, 605 insertions(+), 58 deletions(-) diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc index ee60b963b2b7..b9563b935887 100644 --- a/gcc/analyzer/sm-malloc.cc +++ b/gcc/analyzer/sm-malloc.cc @@ -2196,19 +2196,27 @@ malloc_state_machine::on_stmt (sm_context &sm_ctxt, unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; unsigned int idx2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1; + unsigned int idx3 = idx2; + if (tree chain2 = TREE_CHAIN (TREE_CHAIN (args))) + idx3 = TREE_INT_CST_LOW (TREE_VALUE (chain2)) - 1; if (idx < gimple_call_num_args (stmt) - && idx2 < gimple_call_num_args (stmt)) + && idx2 < gimple_call_num_args (stmt) + && idx3 < gimple_call_num_args (stmt)) { tree arg = gimple_call_arg (stmt, idx); tree arg2 = gimple_call_arg (stmt, idx2); + tree arg3 = gimple_call_arg (stmt, idx3); if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE || !INTEGRAL_TYPE_P (TREE_TYPE (arg2)) - || integer_zerop (arg2)) + || !INTEGRAL_TYPE_P (TREE_TYPE (arg3)) + || integer_zerop (arg2) + || integer_zerop (arg3)) continue; - if (integer_nonzerop (arg2)) + if (integer_nonzerop (arg2) && integer_nonzerop (arg3)) ; else - /* FIXME: Use ranger here to query arg2 range? */ + /* FIXME: Use ranger here to query arg2 and arg3 + ranges? */ continue; handle_nonnull (sm_ctxt, node, stmt, fndecl, arg, idx); } diff --git a/gcc/builtin-attrs.def b/gcc/builtin-attrs.def index 850efea11ca0..2b82fc2326e0 100644 --- a/gcc/builtin-attrs.def +++ b/gcc/builtin-attrs.def @@ -90,6 +90,14 @@ DEF_LIST_INT_INT (5,0) DEF_LIST_INT_INT (5,6) #undef DEF_LIST_INT_INT +/* Construct a tree for a list of three integers. */ +#define DEF_LIST_INT_INT_INT(VALUE1, VALUE2, VALUE3) \ + DEF_ATTR_TREE_LIST (ATTR_LIST_##VALUE1##_##VALUE2##_##VALUE3, \ + ATTR_NULL, ATTR_##VALUE1, \ + ATTR_LIST_##VALUE2##_##VALUE3) +DEF_LIST_INT_INT_INT (1,2,3) +#undef DEF_LIST_INT_INT_INT + /* Construct trees for identifiers used in built-in function attributes. The construction contributes to startup costs so only attributes that are used to define built-ins should be defined here. */ @@ -209,6 +217,12 @@ DEF_ATTR_TREE_LIST (ATTR_NONNULL_1, ATTR_NONNULL, ATTR_LIST_1, ATTR_NULL) DEF_ATTR_TREE_LIST (ATTR_NONNULL_2, ATTR_NONNULL, ATTR_LIST_2, ATTR_NULL) /* Functions whose third parameter is a nonnull pointer. */ DEF_ATTR_TREE_LIST (ATTR_NONNULL_3, ATTR_NONNULL, ATTR_LIST_3, ATTR_NULL) +/* Functions whose selected pointer parameter(s) are conditionally + nonnull. */ +DEF_ATTR_TREE_LIST (ATTR_NONNULL_IF123_LIST, ATTR_NONNULL_IF_NONZERO, \ + ATTR_LIST_1_2_3, ATTR_NULL) +DEF_ATTR_TREE_LIST (ATTR_NONNULL_4_IF123_LIST, ATTR_NONNULL, \ + ATTR_LIST_4, ATTR_NONNULL_IF123_LIST) /* Nothrow functions with the sentinel(1) attribute. */ DEF_ATTR_TREE_LIST (ATTR_NOTHROW_SENTINEL_1, ATTR_SENTINEL, ATTR_LIST_1, \ ATTR_NOTHROW_LIST) diff --git a/gcc/builtins.cc b/gcc/builtins.cc index 3064bff1ae63..a2ce37268107 100644 --- a/gcc/builtins.cc +++ b/gcc/builtins.cc @@ -1158,12 +1158,18 @@ validate_arglist (const_tree callexpr, ...) unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; unsigned int idx2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1; + unsigned int idx3 = idx2; + if (tree chain2 = TREE_CHAIN (TREE_CHAIN (args))) + idx3 = TREE_INT_CST_LOW (TREE_VALUE (chain2)) - 1; if (idx < (unsigned) call_expr_nargs (callexpr) && idx2 < (unsigned) call_expr_nargs (callexpr) + && idx3 < (unsigned) call_expr_nargs (callexpr) && POINTER_TYPE_P (TREE_TYPE (CALL_EXPR_ARG (callexpr, idx))) && integer_zerop (CALL_EXPR_ARG (callexpr, idx)) && INTEGRAL_TYPE_P (TREE_TYPE (CALL_EXPR_ARG (callexpr, idx2))) - && integer_nonzerop (CALL_EXPR_ARG (callexpr, idx2))) + && integer_nonzerop (CALL_EXPR_ARG (callexpr, idx2)) + && INTEGRAL_TYPE_P (TREE_TYPE (CALL_EXPR_ARG (callexpr, idx3))) + && integer_nonzerop (CALL_EXPR_ARG (callexpr, idx3))) return false; } diff --git a/gcc/builtins.def b/gcc/builtins.def index 59a43a10058a..d7b2894bcfa0 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -924,8 +924,8 @@ DEF_EXT_LIB_BUILTIN (BUILT_IN_FPUTC_UNLOCKED, "fputc_unlocked", BT_FN_INT_INT DEF_LIB_BUILTIN (BUILT_IN_FPUTS, "fputs", BT_FN_INT_CONST_STRING_FILEPTR, ATTR_NONNULL_LIST) DEF_EXT_LIB_BUILTIN (BUILT_IN_FPUTS_UNLOCKED, "fputs_unlocked", BT_FN_INT_CONST_STRING_FILEPTR, ATTR_NONNULL_LIST) DEF_LIB_BUILTIN (BUILT_IN_FSCANF, "fscanf", BT_FN_INT_FILEPTR_CONST_STRING_VAR, ATTR_FORMAT_SCANF_2_3) -DEF_LIB_BUILTIN (BUILT_IN_FWRITE, "fwrite", BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR, ATTR_NONNULL_LIST) -DEF_EXT_LIB_BUILTIN (BUILT_IN_FWRITE_UNLOCKED, "fwrite_unlocked", BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR, ATTR_NONNULL_LIST) +DEF_LIB_BUILTIN (BUILT_IN_FWRITE, "fwrite", BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR, ATTR_NONNULL_4_IF123_LIST) +DEF_EXT_LIB_BUILTIN (BUILT_IN_FWRITE_UNLOCKED, "fwrite_unlocked", BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR, ATTR_NONNULL_4_IF123_LIST) DEF_LIB_BUILTIN (BUILT_IN_PRINTF, "printf", BT_FN_INT_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_1_2) DEF_EXT_LIB_BUILTIN (BUILT_IN_PRINTF_UNLOCKED, "printf_unlocked", BT_FN_INT_CONST_STRING_VAR, ATTR_NONNULL_1_FORMAT_PRINTF_1_2) DEF_LIB_BUILTIN (BUILT_IN_PUTCHAR, "putchar", BT_FN_INT_INT, ATTR_NULL) diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc index 5a0e3d328ba7..5d7a31fd99b6 100644 --- a/gcc/c-family/c-attribs.cc +++ b/gcc/c-family/c-attribs.cc @@ -489,7 +489,7 @@ const struct attribute_spec c_common_gnu_attributes[] = handle_tls_model_attribute, NULL }, { "nonnull", 0, -1, false, true, true, false, handle_nonnull_attribute, NULL }, - { "nonnull_if_nonzero", 2, 2, false, true, true, false, + { "nonnull_if_nonzero", 2, 3, false, true, true, false, handle_nonnull_if_nonzero_attribute, NULL }, { "nonstring", 0, 0, true, false, false, false, handle_nonstring_attribute, NULL }, @@ -5034,12 +5034,21 @@ handle_nonnull_if_nonzero_attribute (tree *node, tree name, tree type = *node; tree pos = TREE_VALUE (args); tree pos2 = TREE_VALUE (TREE_CHAIN (args)); + tree chain2 = TREE_CHAIN (TREE_CHAIN (args)); + tree pos3 = NULL_TREE; + if (chain2) + pos3 = TREE_VALUE (chain2); tree val = positional_argument (type, name, pos, POINTER_TYPE, 1); tree val2 = positional_argument (type, name, pos2, INTEGER_TYPE, 2); - if (val && val2) + tree val3 = NULL_TREE; + if (chain2) + val3 = positional_argument (type, name, pos3, INTEGER_TYPE, 3); + if (val && val2 && (!chain2 || val3)) { TREE_VALUE (args) = val; TREE_VALUE (TREE_CHAIN (args)) = val2; + if (chain2) + TREE_VALUE (chain2) = val3; } else *no_add_attrs = true; diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc index f71cb2652d5a..9d6929824dcc 100644 --- a/gcc/c-family/c-common.cc +++ b/gcc/c-family/c-common.cc @@ -5749,8 +5749,8 @@ struct nonnull_arg_ctx /* The function whose arguments are being checked and its type (used for calls through function pointers). */ const_tree fndecl, fntype; - /* For nonnull_if_nonzero, index of the other argument. */ - unsigned HOST_WIDE_INT other; + /* For nonnull_if_nonzero, index of the other arguments. */ + unsigned HOST_WIDE_INT other1, other2; /* True if a warning has been issued. */ bool warned_p; }; @@ -5818,6 +5818,7 @@ check_function_nonnull (nonnull_arg_ctx &ctx, int nargs, tree *argarray) check_function_arguments_recurse (check_nonnull_arg, &ctx, argarray[i], i + 1, OPT_Wnonnull); + a = NULL_TREE; } } if (a == NULL_TREE) @@ -5829,17 +5830,25 @@ check_function_nonnull (nonnull_arg_ctx &ctx, int nargs, tree *argarray) unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; unsigned int idx2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1; + unsigned int idx3 = idx2; + if (tree chain2 = TREE_CHAIN (TREE_CHAIN (args))) + idx3 = TREE_INT_CST_LOW (TREE_VALUE (chain2)) - 1; if (idx < (unsigned) nargs - firstarg && idx2 < (unsigned) nargs - firstarg + && idx3 < (unsigned) nargs - firstarg && INTEGRAL_TYPE_P (TREE_TYPE (argarray[firstarg + idx2])) - && integer_nonzerop (argarray[firstarg + idx2])) + && integer_nonzerop (argarray[firstarg + idx2]) + && INTEGRAL_TYPE_P (TREE_TYPE (argarray[firstarg + idx3])) + && integer_nonzerop (argarray[firstarg + idx3])) { - ctx.other = firstarg + idx2 + 1; + ctx.other1 = firstarg + idx2 + 1; + ctx.other2 = firstarg + idx3 + 1; check_function_arguments_recurse (check_nonnull_arg, &ctx, argarray[firstarg + idx], firstarg + idx + 1, OPT_Wnonnull); - ctx.other = 0; + ctx.other1 = 0; + ctx.other2 = 0; } } return ctx.warned_p; @@ -6023,14 +6032,25 @@ check_nonnull_arg (void *ctx, tree param, unsigned HOST_WIDE_INT param_num) } else { - if (pctx->other) + if (pctx->other1 && pctx->other2 != pctx->other1) + warned = warning_at (loc, OPT_Wnonnull, + "argument %u null where non-null expected " + "because arguments %u and %u are nonzero", + (unsigned) param_num, + TREE_CODE (pctx->fntype) == METHOD_TYPE + ? (unsigned) pctx->other1 - 1 + : (unsigned) pctx->other1, + TREE_CODE (pctx->fntype) == METHOD_TYPE + ? (unsigned) pctx->other2 - 1 + : (unsigned) pctx->other2); + else if (pctx->other1) warned = warning_at (loc, OPT_Wnonnull, "argument %u null where non-null expected " "because argument %u is nonzero", (unsigned) param_num, TREE_CODE (pctx->fntype) == METHOD_TYPE - ? (unsigned) pctx->other - 1 - : (unsigned) pctx->other); + ? (unsigned) pctx->other1 - 1 + : (unsigned) pctx->other1); else warned = warning_at (loc, OPT_Wnonnull, "argument %u null where non-null expected", @@ -6039,7 +6059,7 @@ check_nonnull_arg (void *ctx, tree param, unsigned HOST_WIDE_INT param_num) inform (DECL_SOURCE_LOCATION (pctx->fndecl), "in a call to function %qD declared %qs", pctx->fndecl, - pctx->other ? "nonnull_if_nonzero" : "nonnull"); + pctx->other1 ? "nonnull_if_nonzero" : "nonnull"); } if (warned) @@ -6295,7 +6315,7 @@ check_function_arguments (location_t loc, const_tree fndecl, const_tree fntype, to do this if format checking is enabled. */ if (warn_nonnull) { - nonnull_arg_ctx ctx = { loc, fndecl, fntype, 0, false }; + nonnull_arg_ctx ctx = { loc, fndecl, fntype, 0, 0, false }; warned_p = check_function_nonnull (ctx, nargs, argarray); } diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index e9aa12ba1041..046de360bbc1 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -2887,13 +2887,18 @@ my_memcpy (void *dest, const void *src, size_t len) @cindex @code{nonnull_if_nonzero} function attribute @item nonnull_if_nonzero @itemx nonnull_if_nonzero (@var{arg-index}, @var{arg2-index}) +@itemx nonnull_if_nonzero (@var{arg-index}, @var{arg2-index}, @var{arg3-index}) The @code{nonnull_if_nonzero} attribute is a conditional version of the -@code{nonnull} attribute. It has two arguments, the first argument +@code{nonnull} attribute. It has two or three arguments, the first argument shall be argument index of a pointer argument which must be in some cases non-null and the second argument shall be argument index of an integral argument (other than boolean). If the integral argument is zero, the pointer argument can be null, if it is non-zero, the pointer -argument must not be null. +argument must not be null. If three arguments are provided, the third +argument shall be argument index of another integral argument (other than +boolean) and the pointer argument can be null if either of the integral +arguments are zero and if both are non-zero, the pointer argument must not +be null. @smallexample extern void * @@ -2903,12 +2908,21 @@ extern void * my_memcpy2 (void *dest, const void *src, size_t len) __attribute__((nonnull_if_nonzero (1, 3), nonnull_if_nonzero (2, 3))); +extern size_t +my_fread (void *buf, size_t size, size_t count, FILE *stream) + __attribute__((nonnull (4), + nonnull_if_nonzero (1, 2, 3))); @end smallexample With these declarations, it is invalid to call @code{my_memcpy (NULL, NULL, 0);} or to -call @code{my_memcpy2 (NULL, NULL, 4);} but it is valid -to call @code{my_memcpy2 (NULL, NULL, 0);}. This attribute should be +call @code{my_memcpy2 (NULL, NULL, 4);} or to call +@code{my_fread(@var{buf}, 0, 0, NULL);} or to call +@code{my_fread(NULL, 1, 1, @var{stream});} but it is valid +to call @code{my_memcpy2 (NULL, NULL, 0);} or +@code{my_fread(NULL, 0, 0, @var{stream});} or +@code{my_fread(NULL, 0, 1, @var{stream});} or +@code{my_fread(NULL, 1, 0, @var{stream});}. This attribute should be used on declarations which have e.g.@: an exception for zero sizes, in which case null may be passed. diff --git a/gcc/gimple-range-infer.cc b/gcc/gimple-range-infer.cc index 72f71b980598..612f66626af3 100644 --- a/gcc/gimple-range-infer.cc +++ b/gcc/gimple-range-infer.cc @@ -197,23 +197,41 @@ gimple_infer_range::gimple_infer_range (gimple *s, range_query *q, unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; unsigned int idx2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1; + unsigned int idx3 = idx2; + if (tree chain2 = TREE_CHAIN (TREE_CHAIN (args))) + idx3 = TREE_INT_CST_LOW (TREE_VALUE (chain2)) - 1; if (idx < gimple_call_num_args (s) - && idx2 < gimple_call_num_args (s)) + && idx2 < gimple_call_num_args (s) + && idx3 < gimple_call_num_args (s)) { tree arg = gimple_call_arg (s, idx); tree arg2 = gimple_call_arg (s, idx2); + tree arg3 = gimple_call_arg (s, idx3); if (!POINTER_TYPE_P (TREE_TYPE (arg)) || !INTEGRAL_TYPE_P (TREE_TYPE (arg2)) - || integer_zerop (arg2)) + || !INTEGRAL_TYPE_P (TREE_TYPE (arg3)) + || integer_zerop (arg2) + || integer_zerop (arg3)) continue; - if (integer_nonzerop (arg2)) + if (integer_nonzerop (arg2) && integer_nonzerop (arg3)) add_nonzero (arg); else { value_range r (TREE_TYPE (arg2)); if (q->range_of_expr (r, arg2, s) && !r.contains_p (build_zero_cst (TREE_TYPE (arg2)))) - add_nonzero (arg); + { + if (idx2 == idx3) + add_nonzero (arg); + else + { + value_range r2 (TREE_TYPE (arg3)); + tree zero3 = build_zero_cst (TREE_TYPE (arg3)); + if (q->range_of_expr (r2, arg3, s) + && !r2.contains_p (zero3)) + add_nonzero (arg); + } + } } } } diff --git a/gcc/gimple.cc b/gcc/gimple.cc index 77b2e50fb60b..41908d4e29a6 100644 --- a/gcc/gimple.cc +++ b/gcc/gimple.cc @@ -3154,16 +3154,20 @@ infer_nonnull_range_by_dereference (gimple *stmt, tree op) } /* Return true if OP can be inferred to be a non-NULL after STMT - executes by using attributes. If OP2 is non-NULL and nonnull_if_nonzero - is the only attribute implying OP being non-NULL and the corresponding - argument isn't non-zero INTEGER_CST, set *OP2 to the corresponding - argument and return true (in that case returning true doesn't mean - OP can be unconditionally inferred to be non-NULL, but conditionally). */ + executes by using attributes. If OP2 and OP3 are non-NULL and + nonnull_if_nonzero is the only attribute implying OP being non-NULL + and the corresponding argument(s) aren't non-zero INTEGER_CST, set *OP2 + and *OP3 to the corresponding arguments and return true (in that case + returning true doesn't mean OP can be unconditionally inferred to be + non-NULL, but conditionally). */ bool -infer_nonnull_range_by_attribute (gimple *stmt, tree op, tree *op2) +infer_nonnull_range_by_attribute (gimple *stmt, tree op, tree *op2, tree *op3) { if (op2) - *op2 = NULL_TREE; + { + *op2 = NULL_TREE; + *op3 = NULL_TREE; + } /* We can only assume that a pointer dereference will yield non-NULL if -fdelete-null-pointer-checks is enabled. */ @@ -3220,26 +3224,33 @@ infer_nonnull_range_by_attribute (gimple *stmt, tree op, tree *op2) unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; unsigned int idx2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1; + unsigned int idx3 = idx2; + if (tree chain2 = TREE_CHAIN (TREE_CHAIN (args))) + idx3 = TREE_INT_CST_LOW (TREE_VALUE (chain2)) - 1; if (idx < gimple_call_num_args (stmt) && idx2 < gimple_call_num_args (stmt) + && idx3 < gimple_call_num_args (stmt) && operand_equal_p (op, gimple_call_arg (stmt, idx), 0)) { tree arg2 = gimple_call_arg (stmt, idx2); - if (!INTEGRAL_TYPE_P (TREE_TYPE (arg2))) + tree arg3 = gimple_call_arg (stmt, idx3); + if (!INTEGRAL_TYPE_P (TREE_TYPE (arg2)) + || !INTEGRAL_TYPE_P (TREE_TYPE (arg3))) return false; - if (integer_nonzerop (arg2)) + if (integer_nonzerop (arg2) && integer_nonzerop (arg3)) return true; - if (integer_zerop (arg2)) + if (integer_zerop (arg2) || integer_zerop (arg3)) return false; if (op2) { /* This case is meant for ubsan instrumentation. - The caller can check at runtime if *OP2 is + The caller can check at runtime if *OP2 and *OP3 are non-zero and OP is null. */ *op2 = arg2; + *op3 = arg3; return true; } - return tree_expr_nonzero_p (arg2); + return tree_expr_nonzero_p (arg2) && tree_expr_nonzero_p (arg3); } } } diff --git a/gcc/gimple.h b/gcc/gimple.h index 94d5a13fcb20..268884677e11 100644 --- a/gcc/gimple.h +++ b/gcc/gimple.h @@ -1667,7 +1667,8 @@ extern bool nonfreeing_call_p (gimple *); extern bool nonbarrier_call_p (gimple *); extern bool infer_nonnull_range (gimple *, tree); extern bool infer_nonnull_range_by_dereference (gimple *, tree); -extern bool infer_nonnull_range_by_attribute (gimple *, tree, tree * = NULL); +extern bool infer_nonnull_range_by_attribute (gimple *, tree, tree * = NULL, + tree * = NULL); extern void sort_case_labels (vec<tree> &); extern void preprocess_case_label_vec_for_gimple (vec<tree> &, tree, tree *); extern void gimple_seq_set_location (gimple_seq, location_t); diff --git a/gcc/testsuite/c-c++-common/ubsan/nonnull-8.c b/gcc/testsuite/c-c++-common/ubsan/nonnull-8.c new file mode 100644 index 000000000000..e2320df027c2 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/nonnull-8.c @@ -0,0 +1,32 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=undefined -fno-sanitize-recover=undefined" } */ + +__attribute__((noipa, nonnull_if_nonzero (1, 4, 7))) +__attribute__((nonnull (3), nonnull_if_nonzero (5, 2, 6))) void +foo (void *a, unsigned long b, void *c, int d, void *e, unsigned long f, int g) +{ + (void) a; + (void) b; + (void) c; + (void) d; + (void) e; + (void) f; + (void) g; +} + +__attribute__((noipa)) +void +bar (void *a, unsigned long b, void *c, int d, void *e, unsigned long f, int g) +{ + foo (a, b, c, d, e, f, g); +} + +int +main () +{ + char x; + bar (&x, 42, &x, 1, &x, 2, 3); + bar (0, 0, &x, 0, 0, 0, 0); + bar (0, 5, &x, 4, 0, 0, 0); + bar (0, 0, &x, 0, 0, 6, 7); +} diff --git a/gcc/testsuite/c-c++-common/ubsan/nonnull-9.c b/gcc/testsuite/c-c++-common/ubsan/nonnull-9.c new file mode 100644 index 000000000000..68a4417b4077 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/nonnull-9.c @@ -0,0 +1,41 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=nonnull-attribute" } */ + +__attribute__((noipa, nonnull_if_nonzero (1, 4, 7))) +__attribute__((nonnull (3), nonnull_if_nonzero (5, 2, 6))) void +foo (void *a, unsigned long b, void *c, int d, void *e, unsigned long f, int g) +{ + (void) a; + (void) b; + (void) c; + (void) d; + (void) e; + (void) f; + (void) g; +} + +__attribute__((noipa)) +void +bar (void *a, unsigned long b, void *c, int d, void *e, unsigned long f, int g) +{ + foo (a, b, c, d, e, f, g); +} + +int +main () +{ + char x; + bar (&x, 42, 0, 1, &x, 17, 18); + bar (0, 25, &x, 7, &x, 0, 8); + bar (&x, -82, &x, 68, 0, 9, 0); + foo (&x, 42, 0, 1, &x, 17, 18); + foo (0, 25, &x, 7, &x, 0, 8); + foo (&x, -82, &x, 68, 0, 9, 0); +} + +/* { dg-output "\.c:21:\[0-9]*:\[^\n\r]*null pointer passed as argument 3, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\.c:21:\[0-9]*:\[^\n\r]*null pointer passed as argument 1, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\.c:21:\[0-9]*:\[^\n\r]*null pointer passed as argument 5, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\.c:31:\[0-9]*:\[^\n\r]*null pointer passed as argument 3, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\.c:32:\[0-9]*:\[^\n\r]*null pointer passed as argument 1, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\.c:33:\[0-9]*:\[^\n\r]*null pointer passed as argument 5, which is declared to never be null" } */ diff --git a/gcc/testsuite/gcc.dg/nonnull-12.c b/gcc/testsuite/gcc.dg/nonnull-12.c new file mode 100644 index 000000000000..d3eb475b4654 --- /dev/null +++ b/gcc/testsuite/gcc.dg/nonnull-12.c @@ -0,0 +1,73 @@ +/* Test for the "nonnull_if_nonzero" function attribute. */ +/* { dg-do compile } */ +/* { dg-options "-Wnonnull" } */ + +#include <stddef.h> + +extern void func1 (char *, char *, int, int) + __attribute__((nonnull_if_nonzero (1, 3, 4), nonnull_if_nonzero (2, 3, 4))); + +extern void func2 (char *, char *, unsigned long, unsigned long) + __attribute__((nonnull_if_nonzero (1, 3, 4))); + +enum E { E0 = 0, E1 = 1, E2 = __INT_MAX__ }; +extern void func3 (char *, int, char *, enum E, int, enum E) + __attribute__((nonnull_if_nonzero (1, 4, 6), nonnull_if_nonzero (3, 2, 5))); + +extern void func4 (long, char *, char *, long, long, long) + __attribute__((nonnull_if_nonzero (2, 1, 5))) + __attribute__((nonnull_if_nonzero (3, 4, 6))); + +void +foo (int i1, int i2, int i3, char *cp1, char *cp2, char *cp3) +{ + func1 (cp1, cp2, i1, i2); + func1 (cp1, cp2, 0, 0); + func1 (cp1, cp2, 42, 42); + func1 (NULL, NULL, 0, 0); + func1 (NULL, NULL, 0, 42); + func1 (NULL, NULL, 42, 0); + func1 (NULL, NULL, i1, i2); + + func1 (NULL, cp2, 42, 42); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + func1 (cp1, NULL, 1, 1); /* { dg-warning "argument 2 null where non-null expected because arguments 3 and 4 are nonzero" } */ + + func2 (cp1, NULL, 17, 17); + func2 (NULL, cp2, 0, 0); + func2 (NULL, cp2, 0, 17); + func2 (NULL, cp2, 17, 0); + func2 (NULL, cp1, 2, 2); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + + func3 (NULL, i2, cp3, i3, i3, i2); + func3 (cp1, i2, NULL, i3, i3, i2); + func3 (NULL, i2, cp3, E0, i3, E0); + func3 (NULL, i2, cp3, E0, i3, E1); + func3 (NULL, i2, cp3, E1, i3, E0); + func3 (cp1, 0, NULL, E2, 0, E2); + func3 (cp1, 0, NULL, E2, 4, E2); + func3 (cp1, 4, NULL, E2, 0, E2); + func3 (NULL, i2, cp3, E2, i3, E2); /* { dg-warning "argument 1 null where non-null expected because arguments 4 and 6 are nonzero" } */ + func3 (cp3, 5, NULL, i3, 1, i2); /* { dg-warning "argument 3 null where non-null expected because arguments 2 and 5 are nonzero" } */ + + func1 (i2 ? cp1 : NULL, cp2, i3, i3); + func1 (i2 ? NULL : cp1, cp2, i3, i3); + func1 (i2 ? (i3 ? cp1 : NULL) : cp2, cp3, i1, i1); + func1 (i1 ? cp1 : NULL, cp2, 0, 0); + func1 (i1 ? cp1 : NULL, cp2, 0, 4); + func1 (i1 ? cp1 : NULL, cp2, 4, 0); + func1 (i1 ? NULL : cp1, cp2, 0, 0); + func1 (i1 ? NULL : cp1, cp2, 0, 2); + func1 (i1 ? NULL : cp1, cp2, 3, 0); + func1 (i1 ? (i2 ? cp1 : NULL) : cp2, cp3, 0, 0); + func1 (i1 ? (i2 ? cp1 : NULL) : cp2, cp3, 0, 1); + func1 (i1 ? (i2 ? cp1 : NULL) : cp2, cp3, 2, 0); + func1 (i1 ? cp1 : NULL, cp2, 1, 2); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + func1 (i1 ? NULL : cp1, cp2, 2, 3); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + func1 (i1 ? (i2 ? cp1 : NULL) : cp2, cp3, 3, 4); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + + func4 (0, NULL, NULL, 0, 0, 0); + func4 (0, NULL, NULL, 0, 1, 2); + func4 (3, NULL, NULL, 4, 0, 0); + func4 (-1, NULL, cp1, 0, 42, 0); /* { dg-warning "argument 2 null where non-null expected because arguments 1 and 5 are nonzero" } */ + func4 (0, cp1, NULL, 77, 0, 12); /* { dg-warning "argument 3 null where non-null expected because arguments 4 and 6 are nonzero" } */ +} diff --git a/gcc/testsuite/gcc.dg/nonnull-13.c b/gcc/testsuite/gcc.dg/nonnull-13.c new file mode 100644 index 000000000000..15f2af0b4ec6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/nonnull-13.c @@ -0,0 +1,210 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -Wnonnull" } */ + +#define N(x, y, z) __attribute__ ((nonnull_if_nonzero (x, y, z))) + +void N (1, 2, 3) f1_1 (void *, int, int); + +void N (1, 3, 4) f2_1 (void *, void *, int, int); +void N (1, 3, 4) N (2, 3, 4) f2_1_2 (void *, void *, int, int); + +void N (1, 4, 6) N (3, 5, 7) f3_1_3 (void *, void *, void *, int, int, int, int); + +void N (1, 5, 6) N (2, 5, 6) N (4, 5, 6) g4_1_2_4 (void *, void *, void *, void *, long, long); +void N (1, 5, 6) N (3, 5, 6) N (4, 5, 6) g4_1_3_4 (void *, void *, void *, void *, long, long); +void N (2, 5, 6) N (3, 5, 6) N (4, 5, 6) g4_2_3_4 (void *, void *, void *, void *, long, long); + +void N (1, 17, 18) N (3, 17, 18) N (5, 17, 18) N (7, 17, 18) N (11, 17, 18) N (13, 17, 18) +g16_1_3_5_7_11_13 (void *, void *, void *, void *, + void *, void *, void *, void *, + void *, void *, void *, void *, + void *, void *, void *, void *, int, int); + +static void *null (void) { return 0; } + +void +test (int t, long u, int v, long w) +{ + void *p0 = null (); + void *px = &px; + + f1_1 (p0, 0, 0); + f1_1 (p0, 0, 4); + f1_1 (p0, 3, 0); + f1_1 (p0, t, v); + f1_1 (p0, t, 0); + f1_1 (p0, 0, v); + f1_1 (p0, 42, 1); /* { dg-warning "argument 1 null where non-null expected because arguments 2 and 3 are nonzero" } */ + if (t && v) + f1_1 (p0, t, v); /* { dg-warning "argument 1 null where non-null expected because arguments 2 and 3 are nonzero" } */ + f1_1 (px, 17, 17); + + f2_1 (p0, px, 0, 0); + f2_1 (p0, px, 0, 3); + f2_1 (p0, px, 7, 0); + f2_1 (p0, px, t, v); + f2_1 (p0, px, t, 0); + f2_1 (p0, px, 0, v); + f2_1 (p0, px, 5, 3); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + if (t > 4 && v > 8) + f2_1 (p0, px, t, v); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + f2_1 (px, p0, 17, 17); + f2_1 (p0, p0, 0, 0); + f2_1 (p0, p0, 0, 4); + f2_1 (p0, p0, 2, 0); + if (t < 0 && v < -3) + f2_1 (p0, p0, t, v); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + + f2_1_2 (p0, p0, 0, 0); + f2_1_2 (p0, p0, 0, 1); + f2_1_2 (p0, p0, 2, 0); + f2_1_2 (p0, p0, t, v); + f2_1_2 (p0, p0, t, 0); + f2_1_2 (p0, p0, 0, v); + f2_1_2 (p0, px, 1, 2); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + if (t > 8 && v >= 16) + f2_1_2 (p0, px, t, v); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + f2_1_2 (px, p0, -3, -4); /* { dg-warning "argument 2 null where non-null expected because arguments 3 and 4 are nonzero" } */ + if (t < -2 && v >= 32) + f2_1_2 (px, p0, t, v); /* { dg-warning "argument 2 null where non-null expected because arguments 3 and 4 are nonzero" } */ + f2_1_2 (p0, p0, 8, 165); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + /* { dg-warning "argument 2 null where non-null expected because arguments 3 and 4 are nonzero" "argument 2" { target *-*-* } .-1 } */ + if (t > 7 && v < -2) + f2_1_2 (p0, p0, t, v); /* { dg-warning "argument 1 null where non-null expected because arguments 3 and 4 are nonzero" } */ + /* { dg-warning "argument 2 null where non-null expected because arguments 3 and 4 are nonzero" "argument 2" { target *-*-* } .-1 } */ + + f3_1_3 (p0, p0, p0, 0, 0, 0, 0); + f3_1_3 (p0, p0, p0, 0, 5, 4, 0); + f3_1_3 (p0, p0, p0, 3, 0, 0, 6); + f3_1_3 (p0, p0, px, 0, 6, 0, 6); + f3_1_3 (p0, p0, px, 0, 6, 4, 6); + f3_1_3 (p0, p0, px, 3, 6, 0, 6); + f3_1_3 (px, p0, p0, 2, 0, 2, 0); + f3_1_3 (px, p0, p0, 2, 0, 2, 4); + f3_1_3 (px, p0, p0, 2, 6, 2, 0); + f3_1_3 (p0, p0, p0, t, t, v, v); + f3_1_3 (p0, p0, px, t, 6, v, 7); + f3_1_3 (px, p0, p0, 2, t, 3, v); + f3_1_3 (p0, px, px, 8, 2, 3, 5); /* { dg-warning "argument 1 null where non-null expected because arguments 4 and 6 are nonzero" } */ + if (t > 9 && v < -19) + f3_1_3 (p0, px, px, t, 3, v, 2); /* { dg-warning "argument 1 null where non-null expected because arguments 4 and 6 are nonzero" } */ + f3_1_3 (px, p0, px, 9, 10, 1, 2); + if (t > 11 && v > 3) + f3_1_3 (px, p0, px, t, t, v, v); + f3_1_3 (px, px, p0, 10, 11, 2, 3); /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 7 are nonzero" } */ + if (t < -5 && v > 2) + f3_1_3 (px, px, p0, 0, t, 2, v); /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 7 are nonzero" } */ + f3_1_3 (p0, p0, px, 11, 12, 1, 2); /* { dg-warning "argument 1 null where non-null expected because arguments 4 and 6 are nonzero" } */ + if (t > 26 && v > 88) + f3_1_3 (p0, p0, px, t, 3, v, 2); /* { dg-warning "argument 1 null where non-null expected because arguments 4 and 6 are nonzero" } */ + f3_1_3 (px, p0, p0, 12, 13, 1, 2); /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 7 are nonzero" } */ + if (t > 31 && v < -1) + f3_1_3 (px, p0, p0, 12, t, 2, v); /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 7 are nonzero" } */ + f3_1_3 (p0, p0, p0, 13, 14, 1, 2); /* { dg-warning "argument 1 null where non-null expected because arguments 4 and 6 are nonzero" } */ + /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 7 are nonzero" "argument 3" { target *-*-* } .-1 } */ + if (t > 28 && v > 42) + f3_1_3 (p0, p0, p0, t, t + 1, v, v + 1); /* { dg-warning "argument 1 null where non-null expected because arguments 4 and 6 are nonzero" } */ + /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 7 are nonzero" "argument 3" { target *-*-* } .-1 } */ + + g4_1_2_4 (p0, px, px, px, u, w); + g4_1_2_4 (px, p0, px, px, u, w); + g4_1_2_4 (px, px, p0, px, u, w); + g4_1_2_4 (px, px, px, p0, u, w); + g4_1_2_4 (p0, px, px, px, 0, 0); + g4_1_2_4 (p0, px, px, px, 0, 2); + g4_1_2_4 (p0, px, px, px, 1, 0); + g4_1_2_4 (px, p0, px, px, 0, 0); + g4_1_2_4 (px, p0, px, px, 0, 3); + g4_1_2_4 (px, p0, px, px, 4, 0); + g4_1_2_4 (px, px, p0, px, 0, 0); + g4_1_2_4 (px, px, p0, px, 0, 5); + g4_1_2_4 (px, px, p0, px, 6, 0); + g4_1_2_4 (px, px, px, p0, 0, 0); + g4_1_2_4 (px, px, px, p0, 0, 7); + g4_1_2_4 (px, px, px, p0, 8, 0); + g4_1_2_4 (p0, px, px, px, 15, 2); /* { dg-warning "argument 1 null where non-null expected because arguments 5 and 6 are nonzero" } */ + if (u && w) + g4_1_2_4 (p0, px, px, px, u, w); /* { dg-warning "argument 1 null where non-null expected because arguments 5 and 6 are nonzero" } */ + g4_1_2_4 (px, p0, px, px, 16, 2); /* { dg-warning "argument 2 null where non-null expected because arguments 5 and 6 are nonzero" } */ + if (u > 2 && w > 3) + g4_1_2_4 (px, p0, px, px, u, w); /* { dg-warning "argument 2 null where non-null expected because arguments 5 and 6 are nonzero" } */ + g4_1_2_4 (px, px, p0, px, 17, 8); + if (u > 3 && w < -2) + g4_1_2_4 (px, px, p0, px, u, w); + g4_1_2_4 (px, px, px, p0, 18, 3); /* { dg-warning "argument 4 null where non-null expected because arguments 5 and 6 are nonzero" } */ + if ((u < -2 || u > 10) && (w < -4 || w > 42)) + g4_1_2_4 (px, px, px, p0, u, w); /* { dg-warning "argument 4 null where non-null expected because arguments 5 and 6 are nonzero" } */ + + g4_1_3_4 (p0, px, px, px, u, u); + g4_1_3_4 (px, p0, px, px, u, u); + g4_1_3_4 (px, px, p0, px, u, u); + g4_1_3_4 (px, px, px, p0, u, u); + g4_1_3_4 (p0, px, px, px, 0, 0); + g4_1_3_4 (p0, px, px, px, 0, 1); + g4_1_3_4 (p0, px, px, px, 2, 0); + g4_1_3_4 (px, p0, px, px, 0, 0); + g4_1_3_4 (px, p0, px, px, 0, 3); + g4_1_3_4 (px, p0, px, px, 4, 0); + g4_1_3_4 (px, px, p0, px, 0, 0); + g4_1_3_4 (px, px, p0, px, 0, 5); + g4_1_3_4 (px, px, p0, px, 6, 0); + g4_1_3_4 (px, px, px, p0, 0, 0); + g4_1_3_4 (px, px, px, p0, 0, 7); + g4_1_3_4 (px, px, px, p0, 8, 0); + g4_1_3_4 (p0, px, px, px, 20, 32); /* { dg-warning "argument 1 null where non-null expected because arguments 5 and 6 are nonzero" } */ + if (u > 4 && w > 2) + g4_1_3_4 (p0, px, px, px, u, w); /* { dg-warning "argument 1 null where non-null expected because arguments 5 and 6 are nonzero" } */ + g4_1_3_4 (px, p0, px, px, 21, 4); + if ((u > 6 || u < -24) && (w > 8 || w < -5)) + g4_1_3_4 (px, p0, px, px, u, w); + g4_1_3_4 (px, px, p0, px, 22, 4); /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 6 are nonzero" } */ + if (u > 9 && w > 13) + g4_1_3_4 (px, px, p0, px, u - 3, w - 8); /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 6 are nonzero" } */ + g4_1_3_4 (px, px, px, p0, 23, 8); /* { dg-warning "argument 4 null where non-null expected because arguments 5 and 6 are nonzero" } */ + if (u > 10 && w > 12) + g4_1_3_4 (px, px, px, p0, u, w); /* { dg-warning "argument 4 null where non-null expected because arguments 5 and 6 are nonzero" } */ + + g4_2_3_4 (p0, px, px, px, u, u); + g4_2_3_4 (px, p0, px, px, u, u); + g4_2_3_4 (px, px, p0, px, u, u); + g4_2_3_4 (px, px, px, p0, u, u); + g4_2_3_4 (p0, px, px, px, 0, 0); + g4_2_3_4 (p0, px, px, px, 0, 1); + g4_2_3_4 (p0, px, px, px, 2, 0); + g4_2_3_4 (px, p0, px, px, 0, 0); + g4_2_3_4 (px, p0, px, px, 0, 3); + g4_2_3_4 (px, p0, px, px, 4, 0); + g4_2_3_4 (px, px, p0, px, 0, 0); + g4_2_3_4 (px, px, p0, px, 0, 5); + g4_2_3_4 (px, px, p0, px, 6, 0); + g4_2_3_4 (px, px, px, p0, 0, 0); + g4_2_3_4 (px, px, px, p0, 0, 7); + g4_2_3_4 (px, px, px, p0, 8, 0); + g4_2_3_4 (p0, px, px, px, 1, 2); + if (u > 12 && w > 16) + g4_2_3_4 (p0, px, px, px, u, w); + g4_2_3_4 (px, p0, px, px, 2, 3); /* { dg-warning "argument 2 null where non-null expected because arguments 5 and 6 are nonzero" } */ + if (u > 17 && w > 19) + g4_2_3_4 (px, p0, px, px, u - 3, w - 2); /* { dg-warning "argument 2 null where non-null expected because arguments 5 and 6 are nonzero" } */ + g4_2_3_4 (px, px, p0, px, 3, 8); /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 6 are nonzero" } */ + if (u > 24 && w > 22) + g4_2_3_4 (px, px, p0, px, u, w); /* { dg-warning "argument 3 null where non-null expected because arguments 5 and 6 are nonzero" } */ + g4_2_3_4 (px, px, px, p0, 4, 2); /* { dg-warning "argument 4 null where non-null expected because arguments 5 and 6 are nonzero" } */ + if (u > 42 && w > 48) + g4_2_3_4 (px, px, px, p0, u, w); /* { dg-warning "argument 4 null where non-null expected because arguments 5 and 6 are nonzero" } */ + + g16_1_3_5_7_11_13 (px, px, px, px, px, px, px, px, + px, px, px, px, px, px, px, px, 17, 18); + g16_1_3_5_7_11_13 (p0, p0, p0, p0, p0, p0, p0, p0, + p0, p0, p0, p0, p0, p0, p0, p0, t, v); + g16_1_3_5_7_11_13 (p0, p0, p0, p0, p0, p0, p0, p0, + p0, p0, p0, p0, p0, p0, p0, p0, 0, 0); + g16_1_3_5_7_11_13 (p0, p0, p0, p0, p0, p0, p0, p0, + p0, p0, p0, p0, p0, p0, p0, p0, 0, 4); + g16_1_3_5_7_11_13 (p0, p0, p0, p0, p0, p0, p0, p0, + p0, p0, p0, p0, p0, p0, p0, p0, 3, 0); + + g16_1_3_5_7_11_13 (px, p0, px, p0, px, p0, px, p0, p0, p0, px, p0, p0, p0, p0, p0, 2, 1); /* { dg-warning "argument 13 null where non-null expected because arguments 17 and 18 are nonzero" } */ + if (t > 122 && v > 18) + g16_1_3_5_7_11_13 (px, p0, px, p0, px, p0, px, p0, p0, p0, px, p0, p0, p0, p0, p0, t, v); /* { dg-warning "argument 13 null where non-null expected because arguments 17 and 18 are nonzero" } */ +} diff --git a/gcc/testsuite/gcc.dg/nonnull-14.c b/gcc/testsuite/gcc.dg/nonnull-14.c new file mode 100644 index 000000000000..6194dd015a37 --- /dev/null +++ b/gcc/testsuite/gcc.dg/nonnull-14.c @@ -0,0 +1,23 @@ +/* Test for the "nonnull" function attribute on builtins. Use the + "__builtin_" style below so we don't need prototypes. */ +/* { dg-do compile } */ +/* { dg-options "-Wnonnull" } */ + +#include <stddef.h> + +void +foo (void *p, char *s) +{ + __builtin_fwrite (s, 0, 0, NULL); /* { dg-warning "null" "null pointer check" } */ + __builtin_fwrite (s, 0, 2, NULL); /* { dg-warning "null" "null pointer check" } */ + __builtin_fwrite (s, 1, 0, NULL); /* { dg-warning "null" "null pointer check" } */ + __builtin_fwrite (NULL, 16, 0, p); + __builtin_fwrite (NULL, 0, 12, p); + __builtin_fwrite (NULL, 2, 3, p); /* { dg-warning "null" "null pointer check" } */ + __builtin_fwrite_unlocked (s, 0, 0, NULL); /* { dg-warning "null" "null pointer check" } */ + __builtin_fwrite_unlocked (s, 0, 2, NULL); /* { dg-warning "null" "null pointer check" } */ + __builtin_fwrite_unlocked (s, 1, 0, NULL); /* { dg-warning "null" "null pointer check" } */ + __builtin_fwrite_unlocked (NULL, 16, 0, p); + __builtin_fwrite_unlocked (NULL, 0, 12, p); + __builtin_fwrite_unlocked (NULL, 2, 3, p); /* { dg-warning "null" "null pointer check" } */ +} diff --git a/gcc/testsuite/gcc.dg/nonnull-9.c b/gcc/testsuite/gcc.dg/nonnull-9.c index c0f95ca66631..31e6c11b4d5a 100644 --- a/gcc/testsuite/gcc.dg/nonnull-9.c +++ b/gcc/testsuite/gcc.dg/nonnull-9.c @@ -3,31 +3,42 @@ /* { dg-options "-std=gnu17 -pedantic-errors" } */ extern void func1 () __attribute__((nonnull_if_nonzero)); /* { dg-error "wrong number of arguments specified for 'nonnull_if_nonzero' attribute" } */ -/* { dg-message "expected 2, found 0" "" { target *-*-* } .-1 } */ +/* { dg-message "expected between 2 and 3, found 0" "" { target *-*-* } .-1 } */ extern void func2 (char *) __attribute__((nonnull_if_nonzero(1))); /* { dg-error "wrong number of arguments specified for 'nonnull_if_nonzero' attribute" } */ -/* { dg-message "expected 2, found 1" "" { target *-*-* } .-1 } */ +/* { dg-message "expected between 2 and 3, found 1" "" { target *-*-* } .-1 } */ -extern void func3 (char *) __attribute__((nonnull_if_nonzero(1, 2, 3))); /* { dg-error "wrong number of arguments specified for 'nonnull_if_nonzero' attribute" } */ -/* { dg-message "expected 2, found 3" "" { target *-*-* } .-1 } */ +extern void func3 (char *) __attribute__((nonnull_if_nonzero(1, 2, 3, 4))); /* { dg-error "wrong number of arguments specified for 'nonnull_if_nonzero' attribute" } */ +/* { dg-message "expected between 2 and 3, found 4" "" { target *-*-* } .-1 } */ extern void func4 (char *, int) __attribute__((nonnull_if_nonzero(3, 2))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 1 value '3' exceeds the number of function parameters 2" } */ extern void func5 (char *, int) __attribute__((nonnull_if_nonzero(1, 3))); /* { dg-warning "nonnull_if_nonzero' attribute argument 2 value '3' exceeds the number of function parameters 2" } */ -extern void func6 (char *, int) __attribute__((nonnull_if_nonzero (foo, 2))); /* { dg-warning ".nonnull_if_nonzero. attribute argument 1 is invalid" } */ +extern void func6 (char *, int) __attribute__((nonnull_if_nonzero(1, 2, 3))); /* { dg-warning "nonnull_if_nonzero' attribute argument 3 value '3' exceeds the number of function parameters 2" } */ + +extern void func7 (char *, int) __attribute__((nonnull_if_nonzero (foo, 2))); /* { dg-warning ".nonnull_if_nonzero. attribute argument 1 is invalid" } */ /* { dg-error ".foo. undeclared" "undeclared argument" { target *-*-* } .-1 } */ -extern void func7 (char *, int) __attribute__((nonnull_if_nonzero (1, bar))); /* { dg-warning ".nonnull_if_nonzero. attribute argument 2 is invalid" } */ +extern void func8 (char *, int) __attribute__((nonnull_if_nonzero (1, bar))); /* { dg-warning ".nonnull_if_nonzero. attribute argument 2 is invalid" } */ /* { dg-error ".bar. undeclared" "undeclared argument" { target *-*-* } .-1 } */ -extern void func8 (int, int) __attribute__((nonnull_if_nonzero(1, 2))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 1 value '1' refers to parameter type 'int'" } */ +extern void func9 (char *, int) __attribute__((nonnull_if_nonzero (1, 2, baz))); /* { dg-warning ".nonnull_if_nonzero. attribute argument 3 is invalid" } */ +/* { dg-error ".baz. undeclared" "undeclared argument" { target *-*-* } .-1 } */ + +extern void func10 (int, int) __attribute__((nonnull_if_nonzero(1, 2))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 1 value '1' refers to parameter type 'int'" } */ + +extern void func11 (char *, float) __attribute__((nonnull_if_nonzero(1, 2))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 2 value '2' refers to parameter type 'float'" } */ + +extern void func12 (char *, _Bool) __attribute__((nonnull_if_nonzero(1, 2))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 2 value '2' refers to parameter type '_Bool'" } */ + +extern void func13 (char *, char *) __attribute__((nonnull_if_nonzero(1, 2))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 2 value '2' refers to parameter type 'char \\\*'" } */ -extern void func9 (char *, float) __attribute__((nonnull_if_nonzero(1, 2))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 2 value '2' refers to parameter type 'float'" } */ +extern void func14 (char *, int, float) __attribute__((nonnull_if_nonzero(1, 2, 3))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 3 value '3' refers to parameter type 'float'" } */ -extern void func10 (char *, _Bool) __attribute__((nonnull_if_nonzero(1, 2))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 2 value '2' refers to parameter type '_Bool'" } */ +extern void func15 (char *, long, _Bool) __attribute__((nonnull_if_nonzero(1, 2, 3))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 3 value '3' refers to parameter type '_Bool'" } */ -extern void func11 (char *, char *) __attribute__((nonnull_if_nonzero(1, 2))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 2 value '2' refers to parameter type 'char \\\*'" } */ +extern void func17 (char *, int, char *) __attribute__((nonnull_if_nonzero(1, 2, 3))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 3 value '3' refers to parameter type 'char \\\*'" } */ void foo (void) @@ -38,3 +49,8 @@ void bar (void) { } + +void +baz (void) +{ +} diff --git a/gcc/tree-ssa-ccp.cc b/gcc/tree-ssa-ccp.cc index 13cd81d95672..f33cc042e9fb 100644 --- a/gcc/tree-ssa-ccp.cc +++ b/gcc/tree-ssa-ccp.cc @@ -4629,17 +4629,24 @@ pass_post_ipa_warn::execute (function *fun) unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; unsigned int idx2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1; + unsigned int idx3 = idx2; + if (tree chain2 = TREE_CHAIN (TREE_CHAIN (args))) + idx3 = TREE_INT_CST_LOW (TREE_VALUE (chain2)) - 1; if (idx < gimple_call_num_args (stmt) - && idx2 < gimple_call_num_args (stmt)) + && idx2 < gimple_call_num_args (stmt) + && idx3 < gimple_call_num_args (stmt)) { tree arg = gimple_call_arg (stmt, idx); tree arg2 = gimple_call_arg (stmt, idx2); + tree arg3 = gimple_call_arg (stmt, idx3); if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE || !integer_zerop (arg) || !INTEGRAL_TYPE_P (TREE_TYPE (arg2)) + || !INTEGRAL_TYPE_P (TREE_TYPE (arg3)) || integer_zerop (arg2) + || integer_zerop (arg3) || ((TREE_CODE (fntype) == METHOD_TYPE || closure) - && (idx == 0 || idx2 == 0))) + && (idx == 0 || idx2 == 0 || idx3 == 0))) continue; if (!integer_nonzerop (arg2) && !tree_expr_nonzero_p (arg2)) @@ -4654,17 +4661,40 @@ pass_post_ipa_warn::execute (function *fun) if (range_includes_zero_p (vr)) continue; } + if (idx2 != idx3 + && !integer_nonzerop (arg3) + && !tree_expr_nonzero_p (arg3)) + { + if (TREE_CODE (arg3) != SSA_NAME || optimize < 2) + continue; + if (!ranger) + ranger = enable_ranger (cfun); + + int_range_max vr; + get_range_query (cfun)->range_of_expr (vr, arg3, stmt); + if (range_includes_zero_p (vr)) + continue; + } unsigned argno = idx + 1; unsigned argno2 = idx2 + 1; + unsigned argno3 = idx3 + 1; location_t loc = (EXPR_HAS_LOCATION (arg) ? EXPR_LOCATION (arg) : gimple_location (stmt)); auto_diagnostic_group d; - if (!warning_at (loc, OPT_Wnonnull, - "argument %u null where non-null " - "expected because argument %u is " - "nonzero", argno, argno2)) + if (idx2 != idx3) + { + if (!warning_at (loc, OPT_Wnonnull, + "argument %u null where non-null " + "expected because arguments %u and %u " + "are nonzero", argno, argno2, argno3)) + continue; + } + else if (!warning_at (loc, OPT_Wnonnull, + "argument %u null where non-null " + "expected because argument %u is " + "nonzero", argno, argno2)) continue; tree fndecl = gimple_call_fndecl (stmt); diff --git a/gcc/ubsan.cc b/gcc/ubsan.cc index 3c130a660951..6d748258b1e3 100644 --- a/gcc/ubsan.cc +++ b/gcc/ubsan.cc @@ -2038,9 +2038,9 @@ instrument_nonnull_arg (gimple_stmt_iterator *gsi) for (unsigned int i = 0; i < gimple_call_num_args (stmt); i++) { tree arg = gimple_call_arg (stmt, i); - tree arg2; + tree arg2, arg3; if (POINTER_TYPE_P (TREE_TYPE (arg)) - && infer_nonnull_range_by_attribute (stmt, arg, &arg2)) + && infer_nonnull_range_by_attribute (stmt, arg, &arg2, &arg3)) { gimple *g; if (!is_gimple_val (arg)) @@ -2050,6 +2050,8 @@ instrument_nonnull_arg (gimple_stmt_iterator *gsi) gsi_safe_insert_before (gsi, g); arg = gimple_assign_lhs (g); } + if (arg2 == arg3) + arg3 = NULL_TREE; if (arg2 && !is_gimple_val (arg2)) { g = gimple_build_assign (make_ssa_name (TREE_TYPE (arg2)), arg2); @@ -2057,6 +2059,13 @@ instrument_nonnull_arg (gimple_stmt_iterator *gsi) gsi_safe_insert_before (gsi, g); arg2 = gimple_assign_lhs (g); } + if (arg3 && !is_gimple_val (arg3)) + { + g = gimple_build_assign (make_ssa_name (TREE_TYPE (arg3)), arg3); + gimple_set_location (g, loc[0]); + gsi_safe_insert_before (gsi, g); + arg3 = gimple_assign_lhs (g); + } basic_block then_bb, fallthru_bb; *gsi = create_cond_insert_point (gsi, true, false, true, @@ -2078,6 +2087,18 @@ instrument_nonnull_arg (gimple_stmt_iterator *gsi) gimple_set_location (g, loc[0]); gsi_insert_after (gsi, g, GSI_NEW_STMT); + *gsi = gsi_after_labels (then_bb); + } + if (arg3) + { + *gsi = create_cond_insert_point (gsi, true, false, true, + &then_bb, &fallthru_bb); + g = gimple_build_cond (NE_EXPR, arg3, + build_zero_cst (TREE_TYPE (arg3)), + NULL_TREE, NULL_TREE); + gimple_set_location (g, loc[0]); + gsi_insert_after (gsi, g, GSI_NEW_STMT); + *gsi = gsi_after_labels (then_bb); } if (flag_sanitize_trap & SANITIZE_NONNULL_ATTRIBUTE)