On Wed, Nov 13, 2024 at 9:57 AM Jakub Jelinek <ja...@redhat.com> wrote: > > On Tue, Nov 12, 2024 at 06:34:39PM +0100, Jakub Jelinek wrote: > > What do you think about this? So far lightly tested. > > Unfortunately bootstrap/regtest revealed some issues in the patch, > the tree-ssa-ccp.cc changes break bootstrap because fntype in there > may be NULL and that is what get_nonnull_args handles by just returning > NULL, but obviously TYPE_ATTRIBUTES (fntype) can't be accessed, so I've > added if (!fntype) continue; > And the ubsan tests worked in C but not C++ due to extra warning, so I've > adjusted them. > > This has been successfully bootstrapped/regtested on x86_64-linux and > i686-linux.
The middle-end changes are OK. Richard. > 2024-11-13 Jakub Jelinek <ja...@redhat.com> > > PR c/117023 > gcc/ > * gimple.h (infer_nonnull_range_by_attribute): Add a tree * > argument defaulted to NULL. > * gimple.cc (infer_nonnull_range_by_attribute): Add op2 argument. > Handle also nonnull_if_nonzero attributes. > * tree.cc (get_nonnull_args): Fix comment typo. > * builtins.cc (validate_arglist): Handle nonnull_if_nonzero attribute. > * tree-ssa-ccp.cc (pass_post_ipa_warn::execute): Handle > nonnull_if_nonzero attributes. > * ubsan.cc (instrument_nonnull_arg): Adjust > infer_nonnull_range_by_attribute caller. If it returned true and > filed in non-NULL arg2, check that arg2 is non-zero as another > condition next to checking that arg is zero. > * doc/extend.texi (nonnull_if_nonzero): Document new attribute. > gcc/c-family/ > * c-attribs.cc (handle_nonnull_if_nonzero_attribute): New > function. > (c_common_gnu_attributes): Add nonnull_if_nonzero attribute. > (handle_nonnull_attribute): Fix comment typo. > * c-common.cc (struct nonnull_arg_ctx): Add other member. > (check_function_nonnull): Also check nonnull_if_nonzero attributes. > (check_nonnull_arg): Use different warning wording if pctx->other > is non-zero. > (check_function_arguments): Initialize ctx.other. > gcc/testsuite/ > * gcc.dg/nonnull-8.c: New test. > * gcc.dg/nonnull-9.c: New test. > * gcc.dg/nonnull-10.c: New test. > * c-c++-common/ubsan/nonnull-6.c: New test. > * c-c++-common/ubsan/nonnull-7.c: New test. > > --- gcc/gimple.h.jj 2024-09-23 16:01:12.393215457 +0200 > +++ gcc/gimple.h 2024-11-12 12:24:06.544215672 +0100 > @@ -1661,7 +1661,7 @@ 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); > +extern bool infer_nonnull_range_by_attribute (gimple *, tree, 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); > --- gcc/gimple.cc.jj 2024-10-31 08:45:38.241824084 +0100 > +++ gcc/gimple.cc 2024-11-12 14:30:29.104618853 +0100 > @@ -3089,10 +3089,16 @@ infer_nonnull_range_by_dereference (gimp > } > > /* Return true if OP can be inferred to be a non-NULL after STMT > - executes by using attributes. */ > + 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. */ > bool > -infer_nonnull_range_by_attribute (gimple *stmt, tree op) > +infer_nonnull_range_by_attribute (gimple *stmt, tree op, tree *op2) > { > + if (op2) > + *op2 = NULL_TREE; > + > /* We can only assume that a pointer dereference will yield > non-NULL if -fdelete-null-pointer-checks is enabled. */ > if (!flag_delete_null_pointer_checks > @@ -3109,9 +3115,10 @@ infer_nonnull_range_by_attribute (gimple > attrs = lookup_attribute ("nonnull", attrs); > > /* If "nonnull" wasn't specified, we know nothing about > - the argument. */ > + the argument, unless "nonnull_if_nonzero" attribute is > + present. */ > if (attrs == NULL_TREE) > - return false; > + break; > > /* If "nonnull" applies to all the arguments, then ARG > is non-null if it's in the argument list. */ > @@ -3138,6 +3145,37 @@ infer_nonnull_range_by_attribute (gimple > } > } > } > + > + for (attrs = TYPE_ATTRIBUTES (fntype); > + (attrs = lookup_attribute ("nonnull_if_nonzero", attrs)); > + attrs = TREE_CHAIN (attrs)) > + { > + tree args = TREE_VALUE (attrs); > + unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; > + unsigned int idx2 > + = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1; > + if (idx < gimple_call_num_args (stmt) > + && idx2 < 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))) > + return false; > + if (integer_nonzerop (arg2)) > + return true; > + if (integer_zerop (arg2)) > + return false; > + if (op2) > + { > + /* This case is meant for ubsan instrumentation. > + The caller can check at runtime if *OP2 is > + non-zero and OP is null. */ > + *op2 = arg2; > + return true; > + } > + return tree_expr_nonzero_p (arg2); > + } > + } > } > > /* If this function is marked as returning non-null, then we can > --- gcc/tree.cc.jj 2024-10-31 08:46:20.767225624 +0100 > +++ gcc/tree.cc 2024-11-12 13:54:28.115035591 +0100 > @@ -14768,7 +14768,7 @@ get_nonnull_args (const_tree fntype) > /* A function declaration can specify multiple attribute nonnull, > each with zero or more arguments. The loop below creates a bitmap > representing a union of all the arguments. An empty (but non-null) > - bitmap means that all arguments have been declaraed nonnull. */ > + bitmap means that all arguments have been declared nonnull. */ > for ( ; attrs; attrs = TREE_CHAIN (attrs)) > { > attrs = lookup_attribute ("nonnull", attrs); > --- gcc/builtins.cc.jj 2024-11-01 23:03:43.515359648 +0100 > +++ gcc/builtins.cc 2024-11-12 18:06:55.850789518 +0100 > @@ -1149,6 +1149,24 @@ validate_arglist (const_tree callexpr, . > > BITMAP_FREE (argmap); > > + if (res) > + for (tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (TREE_TYPE (fn))); > + (attrs = lookup_attribute ("nonnull_if_nonzero", attrs)); > + attrs = TREE_CHAIN (attrs)) > + { > + tree args = TREE_VALUE (attrs); > + unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; > + unsigned int idx2 > + = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1; > + if (idx < (unsigned) call_expr_nargs (callexpr) > + && idx2 < (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))) > + return false; > + } > + > return res; > } > > --- gcc/tree-ssa-ccp.cc.jj 2024-10-25 10:00:29.536766884 +0200 > +++ gcc/tree-ssa-ccp.cc 2024-11-12 23:31:51.290755985 +0100 > @@ -155,6 +155,7 @@ along with GCC; see the file COPYING3. > #include "ipa-cp.h" > #include "ipa-prop.h" > #include "internal-fn.h" > +#include "gimple-range.h" > > /* Possible lattice values. */ > typedef enum > @@ -4547,6 +4548,7 @@ unsigned int > pass_post_ipa_warn::execute (function *fun) > { > basic_block bb; > + gimple_ranger *ranger = NULL; > > FOR_EACH_BB_FN (bb, fun) > { > @@ -4558,14 +4560,15 @@ pass_post_ipa_warn::execute (function *f > continue; > > tree fntype = gimple_call_fntype (stmt); > - bitmap nonnullargs = get_nonnull_args (fntype); > - if (!nonnullargs) > + if (!fntype) > continue; > + bitmap nonnullargs = get_nonnull_args (fntype); > > tree fndecl = gimple_call_fndecl (stmt); > const bool closure = fndecl && DECL_LAMBDA_FUNCTION_P (fndecl); > > - for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) > + for (unsigned i = nonnullargs ? 0 : ~0U; > + i < gimple_call_num_args (stmt); i++) > { > tree arg = gimple_call_arg (stmt, i); > if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE) > @@ -4613,8 +4616,67 @@ pass_post_ipa_warn::execute (function *f > fndecl, "nonnull"); > } > BITMAP_FREE (nonnullargs); > + > + for (tree attrs = TYPE_ATTRIBUTES (fntype); > + (attrs = lookup_attribute ("nonnull_if_nonzero", attrs)); > + attrs = TREE_CHAIN (attrs)) > + { > + tree args = TREE_VALUE (attrs); > + unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; > + unsigned int idx2 > + = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1; > + if (idx < gimple_call_num_args (stmt) > + && idx2 < gimple_call_num_args (stmt)) > + { > + tree arg = gimple_call_arg (stmt, idx); > + tree arg2 = gimple_call_arg (stmt, idx2); > + if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE > + || !integer_zerop (arg) > + || !INTEGRAL_TYPE_P (TREE_TYPE (arg2)) > + || integer_zerop (arg2) > + || ((TREE_CODE (fntype) == METHOD_TYPE || closure) > + && (idx == 0 || idx2 == 0))) > + continue; > + if (!integer_nonzerop (arg2) > + && !tree_expr_nonzero_p (arg2)) > + { > + if (TREE_CODE (arg2) != SSA_NAME || optimize < 2) > + continue; > + if (!ranger) > + ranger = enable_ranger (cfun); > + > + int_range_max vr; > + get_range_query (cfun)->range_of_expr (vr, arg2, stmt); > + if (range_includes_zero_p (vr)) > + continue; > + } > + unsigned argno = idx + 1; > + unsigned argno2 = idx2 + 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)) > + continue; > + > + tree fndecl = gimple_call_fndecl (stmt); > + if (fndecl && DECL_IS_UNDECLARED_BUILTIN (fndecl)) > + inform (loc, "in a call to built-in function %qD", > + fndecl); > + else if (fndecl) > + inform (DECL_SOURCE_LOCATION (fndecl), > + "in a call to function %qD declared %qs", > + fndecl, "nonnull_if_nonzero"); > + } > + } > } > } > + if (ranger) > + disable_ranger (cfun); > return 0; > } > > --- gcc/ubsan.cc.jj 2024-10-25 10:00:29.556766598 +0200 > +++ gcc/ubsan.cc 2024-11-12 13:01:38.079628478 +0100 > @@ -2047,8 +2047,9 @@ instrument_nonnull_arg (gimple_stmt_iter > for (unsigned int i = 0; i < gimple_call_num_args (stmt); i++) > { > tree arg = gimple_call_arg (stmt, i); > + tree arg2; > if (POINTER_TYPE_P (TREE_TYPE (arg)) > - && infer_nonnull_range_by_attribute (stmt, arg)) > + && infer_nonnull_range_by_attribute (stmt, arg, &arg2)) > { > gimple *g; > if (!is_gimple_val (arg)) > @@ -2058,6 +2059,13 @@ instrument_nonnull_arg (gimple_stmt_iter > gsi_safe_insert_before (gsi, g); > arg = gimple_assign_lhs (g); > } > + if (arg2 && !is_gimple_val (arg2)) > + { > + g = gimple_build_assign (make_ssa_name (TREE_TYPE (arg2)), > arg2); > + gimple_set_location (g, loc[0]); > + gsi_safe_insert_before (gsi, g); > + arg2 = gimple_assign_lhs (g); > + } > > basic_block then_bb, fallthru_bb; > *gsi = create_cond_insert_point (gsi, true, false, true, > @@ -2069,6 +2077,18 @@ instrument_nonnull_arg (gimple_stmt_iter > gsi_insert_after (gsi, g, GSI_NEW_STMT); > > *gsi = gsi_after_labels (then_bb); > + if (arg2) > + { > + *gsi = create_cond_insert_point (gsi, true, false, true, > + &then_bb, &fallthru_bb); > + g = gimple_build_cond (NE_EXPR, arg2, > + build_zero_cst (TREE_TYPE (arg2)), > + 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) > g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0); > else > --- gcc/doc/extend.texi.jj 2024-11-11 20:04:33.377202404 +0100 > +++ gcc/doc/extend.texi 2024-11-12 17:54:26.062340704 +0100 > @@ -2755,9 +2755,10 @@ object size, for example in functions th > Note that the @code{access} attribute merely specifies how an object > referenced by the pointer argument can be accessed; it does not imply that > an access @strong{will} happen. Also, the @code{access} attribute does not > -imply the attribute @code{nonnull}; it may be appropriate to add both > attributes > -at the declaration of a function that unconditionally manipulates a buffer > via > -a pointer argument. See the @code{nonnull} attribute for more information > and > +imply the attribute @code{nonnull} nor the attribute > @code{nonnull_if_nonzero}; > +it may be appropriate to add both attributes at the declaration of a function > +that unconditionally manipulates a buffer via a pointer argument. See the > +@code{nonnull} or @code{nonnull_if_nonzero} attributes for more information > and > caveats. > > @cindex @code{alias} function attribute > @@ -3789,6 +3790,34 @@ my_memcpy (void *dest, const void *src, > __attribute__((nonnull)); > @end smallexample > > +@cindex @code{nonnull_if_nonzero} function attribute > +@item nonnull_if_nonzero > +@itemx nonnull_if_nonzero (@var{arg-index}, @var{arg2-index}) > +The @code{nonnull_if_nonzero} attribute is a conditional version of the > +@code{nonnull} attribute. It has two 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. > + > +@smallexample > +extern void * > +my_memcpy (void *dest, const void *src, size_t len) > + __attribute__((nonnull (1, 2))); > +extern void * > +my_memcpy2 (void *dest, const void *src, size_t len) > + __attribute__((nonnull_if_nonzero (1, 3), > + nonnull_if_nonzero (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 > +used on declarations which have e.g.@: an exception for zero sizes, > +in which case null may be passed. > + > @cindex @code{noplt} function attribute > @item noplt > The @code{noplt} attribute is the counterpart to option @option{-fno-plt}. > --- gcc/c-family/c-attribs.cc.jj 2024-10-25 10:00:29.313770074 +0200 > +++ gcc/c-family/c-attribs.cc 2024-11-12 12:07:29.224220859 +0100 > @@ -139,6 +139,8 @@ static tree handle_vector_size_attribute > static tree handle_vector_mask_attribute (tree *, tree, tree, int, > bool *) ATTRIBUTE_NONNULL(3); > static tree handle_nonnull_attribute (tree *, tree, tree, int, bool *); > +static tree handle_nonnull_if_nonzero_attribute (tree *, tree, tree, int, > + bool *); > static tree handle_nonstring_attribute (tree *, tree, tree, int, bool *); > static tree handle_nothrow_attribute (tree *, tree, tree, int, bool *); > static tree handle_expected_throw_attribute (tree *, tree, tree, int, bool > *); > @@ -488,6 +490,8 @@ const struct attribute_spec c_common_gnu > 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, > + handle_nonnull_if_nonzero_attribute, NULL }, > { "nonstring", 0, 0, true, false, false, false, > handle_nonstring_attribute, NULL }, > { "nothrow", 0, 0, true, false, false, false, > @@ -5000,7 +5004,7 @@ handle_nonnull_attribute (tree *node, tr > /* NEXT is null when the attribute includes just one argument. > That's used to tell positional_argument to avoid mentioning > the argument number in diagnostics (since there's just one > - mentioning it is unnecessary and coule be confusing). */ > + mentioning it is unnecessary and could be confusing). */ > tree next = TREE_CHAIN (args); > if (tree val = positional_argument (type, name, pos, POINTER_TYPE, > next || i > 1 ? i : 0)) > @@ -5015,6 +5019,29 @@ handle_nonnull_attribute (tree *node, tr > > return NULL_TREE; > } > + > +/* Handle the "nonnull_if_nonzero" attribute. */ > + > +static tree > +handle_nonnull_if_nonzero_attribute (tree *node, tree name, > + tree args, int ARG_UNUSED (flags), > + bool *no_add_attrs) > +{ > + tree type = *node; > + tree pos = TREE_VALUE (args); > + tree pos2 = TREE_VALUE (TREE_CHAIN (args)); > + tree val = positional_argument (type, name, pos, POINTER_TYPE, 1); > + tree val2 = positional_argument (type, name, pos2, INTEGER_TYPE, 2); > + if (val && val2) > + { > + TREE_VALUE (args) = val; > + TREE_VALUE (TREE_CHAIN (args)) = val2; > + } > + else > + *no_add_attrs = true; > + > + return NULL_TREE; > +} > > /* Handle the "fd_arg", "fd_arg_read" and "fd_arg_write" attributes */ > > --- gcc/c-family/c-common.cc.jj 2024-11-11 20:04:33.358202671 +0100 > +++ gcc/c-family/c-common.cc 2024-11-12 16:53:19.148000499 +0100 > @@ -5718,6 +5718,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; > /* True if a warning has been issued. */ > bool warned_p; > }; > @@ -5756,23 +5758,19 @@ check_function_nonnull (nonnull_arg_ctx > } > > tree attrs = lookup_attribute ("nonnull", TYPE_ATTRIBUTES (ctx.fntype)); > - if (attrs == NULL_TREE) > - return ctx.warned_p; > > tree a = attrs; > /* See if any of the nonnull attributes has no arguments. If so, > then every pointer argument is checked (in which case the check > for pointer type is done in check_nonnull_arg). */ > - if (TREE_VALUE (a) != NULL_TREE) > - do > - a = lookup_attribute ("nonnull", TREE_CHAIN (a)); > - while (a != NULL_TREE && TREE_VALUE (a) != NULL_TREE); > + while (a != NULL_TREE && TREE_VALUE (a) != NULL_TREE) > + a = lookup_attribute ("nonnull", TREE_CHAIN (a)); > > if (a != NULL_TREE) > for (int i = firstarg; i < nargs; i++) > check_function_arguments_recurse (check_nonnull_arg, &ctx, argarray[i], > i + 1, OPT_Wnonnull); > - else > + else if (attrs) > { > /* Walk the argument list. If we encounter an argument number we > should check for non-null, do it. */ > @@ -5791,6 +5789,28 @@ check_function_nonnull (nonnull_arg_ctx > OPT_Wnonnull); > } > } > + if (a == NULL_TREE) > + for (attrs = TYPE_ATTRIBUTES (ctx.fntype); > + (attrs = lookup_attribute ("nonnull_if_nonzero", attrs)); > + attrs = TREE_CHAIN (attrs)) > + { > + tree args = TREE_VALUE (attrs); > + unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1; > + unsigned int idx2 > + = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1; > + if (idx < (unsigned) nargs - firstarg > + && idx2 < (unsigned) nargs - firstarg > + && INTEGRAL_TYPE_P (TREE_TYPE (argarray[firstarg + idx2])) > + && integer_nonzerop (argarray[firstarg + idx2])) > + { > + ctx.other = firstarg + idx2 + 1; > + check_function_arguments_recurse (check_nonnull_arg, &ctx, > + argarray[firstarg + idx], > + firstarg + idx + 1, > + OPT_Wnonnull); > + ctx.other = 0; > + } > + } > return ctx.warned_p; > } > > @@ -5972,13 +5992,23 @@ check_nonnull_arg (void *ctx, tree param > } > else > { > - warned = warning_at (loc, OPT_Wnonnull, > - "argument %u null where non-null expected", > - (unsigned) param_num); > + if (pctx->other) > + 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); > + else > + warned = warning_at (loc, OPT_Wnonnull, > + "argument %u null where non-null expected", > + (unsigned) param_num); > if (warned && pctx->fndecl) > inform (DECL_SOURCE_LOCATION (pctx->fndecl), > "in a call to function %qD declared %qs", > - pctx->fndecl, "nonnull"); > + pctx->fndecl, > + pctx->other ? "nonnull_if_nonzero" : "nonnull"); > } > > if (warned) > @@ -6224,7 +6254,7 @@ check_function_arguments (location_t loc > to do this if format checking is enabled. */ > if (warn_nonnull) > { > - nonnull_arg_ctx ctx = { loc, fndecl, fntype, false }; > + nonnull_arg_ctx ctx = { loc, fndecl, fntype, 0, false }; > warned_p = check_function_nonnull (ctx, nargs, argarray); > } > > --- gcc/testsuite/gcc.dg/nonnull-8.c.jj 2024-11-12 15:08:22.138633984 +0100 > +++ gcc/testsuite/gcc.dg/nonnull-8.c 2024-11-12 17:02:45.213024228 +0100 > @@ -0,0 +1,57 @@ > +/* Test for the "nonnull_if_nonzero" function attribute. */ > +/* { dg-do compile } */ > +/* { dg-options "-Wnonnull" } */ > + > +#include <stddef.h> > + > +extern void func1 (char *, char *, int) > + __attribute__((nonnull_if_nonzero (1, 3), nonnull_if_nonzero (2, 3))); > + > +extern void func2 (char *, char *, unsigned long) > + __attribute__((nonnull_if_nonzero (1, 3))); > + > +enum E { E0 = 0, E1 = __INT_MAX__ }; > +extern void func3 (char *, int, char *, enum E) > + __attribute__((nonnull_if_nonzero (1, 4), nonnull_if_nonzero (3, 2))); > + > +extern void func4 (long, char *, char *, long) > + __attribute__((nonnull_if_nonzero (2, 1))) > + __attribute__((nonnull_if_nonzero (3, 4))); > + > +void > +foo (int i1, int i2, int i3, char *cp1, char *cp2, char *cp3) > +{ > + func1 (cp1, cp2, i1); > + func1 (cp1, cp2, 0); > + func1 (cp1, cp2, 42); > + func1 (NULL, NULL, 0); > + func1 (NULL, NULL, i1); > + > + func1 (NULL, cp2, 42); /* { dg-warning "argument 1 null where non-null > expected because argument 3 is nonzero" } */ > + func1 (cp1, NULL, 1); /* { dg-warning "argument 2 null where non-null > expected because argument 3 is nonzero" } */ > + > + func2 (cp1, NULL, 17); > + func2 (NULL, cp2, 0); > + func2 (NULL, cp1, 2); /* { dg-warning "argument 1 null where non-null > expected because argument 3 is nonzero" } */ > + > + func3 (NULL, i2, cp3, i3); > + func3 (cp1, i2, NULL, i3); > + func3 (NULL, i2, cp3, E0); > + func3 (cp1, 0, NULL, E1); > + func3 (NULL, i2, cp3, E1); /* { dg-warning "argument 1 null where non-null > expected because argument 4 is nonzero" } */ > + func3 (cp3, 5, NULL, i3); /* { dg-warning "argument 3 null where non-null > expected because argument 2 is nonzero" } */ > + > + func1 (i2 ? cp1 : NULL, cp2, i3); > + func1 (i2 ? NULL : cp1, cp2, i3); > + func1 (i2 ? (i3 ? cp1 : NULL) : cp2, cp3, i1); > + func1 (i1 ? cp1 : NULL, cp2, 0); > + func1 (i1 ? NULL : cp1, cp2, 0); > + func1 (i1 ? (i2 ? cp1 : NULL) : cp2, cp3, 0); > + func1 (i1 ? cp1 : NULL, cp2, 1); /* { dg-warning "argument 1 null where > non-null expected because argument 3 is nonzero" } */ > + func1 (i1 ? NULL : cp1, cp2, 2); /* { dg-warning "argument 1 null where > non-null expected because argument 3 is nonzero" } */ > + func1 (i1 ? (i2 ? cp1 : NULL) : cp2, cp3, 3); /* { dg-warning "argument 1 > null where non-null expected because argument 3 is nonzero" } */ > + > + func4 (0, NULL, NULL, 0); > + func4 (-1, NULL, cp1, 0); /* { dg-warning "argument 2 null where non-null > expected because argument 1 is nonzero" } */ > + func4 (0, cp1, NULL, 77); /* { dg-warning "argument 3 null where non-null > expected because argument 4 is nonzero" } */ > +} > --- gcc/testsuite/gcc.dg/nonnull-9.c.jj 2024-11-12 15:36:03.014255810 +0100 > +++ gcc/testsuite/gcc.dg/nonnull-9.c 2024-11-12 16:01:48.208513109 +0100 > @@ -0,0 +1,40 @@ > +/* Test for the invalid use of the "nonnull_if_nonzero" function attribute. > */ > +/* { dg-do compile } */ > +/* { 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 } */ > + > +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 } */ > + > +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 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" > } */ > +/* { 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" } */ > +/* { 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 *, float) __attribute__((nonnull_if_nonzero(1, 2))); > /* { dg-warning "'nonnull_if_nonzero' attribute argument 2 value '2' 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 func11 (char *, char *) __attribute__((nonnull_if_nonzero(1, > 2))); /* { dg-warning "'nonnull_if_nonzero' attribute argument 2 value '2' > refers to parameter type 'char \\\*'" } */ > + > +void > +foo (void) > +{ > +} > + > +void > +bar (void) > +{ > +} > --- gcc/testsuite/gcc.dg/nonnull-10.c.jj 2024-11-12 17:04:10.170826997 > +0100 > +++ gcc/testsuite/gcc.dg/nonnull-10.c 2024-11-12 17:36:40.542340272 +0100 > @@ -0,0 +1,162 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -Wnonnull" } */ > + > +#define N(x, y) __attribute__ ((nonnull_if_nonzero (x, y))) > + > +void N (1, 2) f1_1 (void *, int); > + > +void N (1, 3) f2_1 (void *, void *, int); > +void N (1, 3) N (2, 3) f2_1_2 (void *, void *, int); > + > +void N (1, 4) N (3, 5) f3_1_3 (void *, void *, void *, int, int); > + > +void N (1, 5) N (2, 5) N (4, 5) g4_1_2_4 (void *, void *, void *, void *, > long); > +void N (1, 5) N (3, 5) N (4, 5) g4_1_3_4 (void *, void *, void *, void *, > long); > +void N (2, 5) N (3, 5) N (4, 5) g4_2_3_4 (void *, void *, void *, void *, > long); > + > +void N (1, 17) N (3, 17) N (5, 17) N (7, 17) N (11, 17) N (13, 17) > +g16_1_3_5_7_11_13 (void *, void *, void *, void *, > + void *, void *, void *, void *, > + void *, void *, void *, void *, > + void *, void *, void *, void *, int); > + > +static void *null (void) { return 0; } > + > +void > +test (int t, long u) > +{ > + void *p0 = null (); > + void *px = &px; > + > + f1_1 (p0, 0); > + f1_1 (p0, t); > + f1_1 (p0, 42); /* { dg-warning "argument 1 null where non-null expected > because argument 2 is nonzero" } */ > + if (t) > + f1_1 (p0, t); /* { dg-warning "argument 1 null where non-null expected > because argument 2 is nonzero" } */ > + f1_1 (px, 17); > + > + f2_1 (p0, px, 0); > + f2_1 (p0, px, t); > + f2_1 (p0, px, 5); /* { dg-warning "argument 1 null where non-null > expected because argument 3 is nonzero" } */ > + if (t > 4) > + f2_1 (p0, px, t); /* { dg-warning "argument 1 null where non-null > expected because argument 3 is nonzero" } */ > + f2_1 (px, p0, 17); > + f2_1 (p0, p0, 0); > + if (t < 0) > + f2_1 (p0, p0, t); /* { dg-warning "argument 1 null where non-null > expected because argument 3 is nonzero" } */ > + > + f2_1_2 (p0, p0, 0); > + f2_1_2 (p0, p0, t); > + f2_1_2 (p0, px, 1); /* { dg-warning "argument 1 null where non-null > expected because argument 3 is nonzero" } */ > + if (t > 8) > + f2_1_2 (p0, px, t); /* { dg-warning "argument 1 null where non-null > expected because argument 3 is nonzero" } */ > + f2_1_2 (px, p0, -3); /* { dg-warning "argument 2 null where non-null > expected because argument 3 is nonzero" } */ > + if (t < -2) > + f2_1_2 (px, p0, t); /* { dg-warning "argument 2 null where non-null > expected because argument 3 is nonzero" } */ > + f2_1_2 (p0, p0, 8); /* { dg-warning "argument 1 null where non-null > expected because argument 3 is nonzero" } */ > + /* { dg-warning "argument 2 null where non-null expected because argument > 3 is nonzero" "argument 2" { target *-*-* } .-1 } */ > + if (t > 7) > + f2_1_2 (p0, p0, t); /* { dg-warning "argument 1 null where non-null > expected because argument 3 is nonzero" } */ > + /* { dg-warning "argument 2 null where non-null expected because argument > 3 is nonzero" "argument 2" { target *-*-* } .-1 } */ > + > + f3_1_3 (p0, p0, p0, 0, 0); > + f3_1_3 (p0, p0, px, 0, 6); > + f3_1_3 (px, p0, p0, 2, 0); > + f3_1_3 (p0, p0, p0, t, t); > + f3_1_3 (p0, p0, px, t, 6); > + f3_1_3 (px, p0, p0, 2, t); > + f3_1_3 (p0, px, px, 8, 2); /* { dg-warning "argument 1 null where non-null > expected because argument 4 is nonzero" } */ > + if (t > 9) > + f3_1_3 (p0, px, px, t, 3); /* { dg-warning "argument 1 null where > non-null expected because argument 4 is nonzero" } */ > + f3_1_3 (px, p0, px, 9, 10); > + if (t > 11) > + f3_1_3 (px, p0, px, t, t); > + f3_1_3 (px, px, p0, 10, 11); /* { dg-warning "argument 3 null where > non-null expected because argument 5 is nonzero" } */ > + if (t < -5) > + f3_1_3 (px, px, p0, 0, t); /* { dg-warning "argument 3 null where > non-null expected because argument 5 is nonzero" } */ > + f3_1_3 (p0, p0, px, 11, 12); /* { dg-warning "argument 1 null where > non-null expected because argument 4 is nonzero" } */ > + if (t > 26) > + f3_1_3 (p0, p0, px, t, 0); /* { dg-warning "argument 1 null where > non-null expected because argument 4 is nonzero" } */ > + f3_1_3 (px, p0, p0, 12, 13); /* { dg-warning "argument 3 null where > non-null expected because argument 5 is nonzero" } */ > + if (t > 31) > + f3_1_3 (px, p0, p0, 12, t); /* { dg-warning "argument 3 null where > non-null expected because argument 5 is nonzero" } */ > + f3_1_3 (p0, p0, p0, 13, 14); /* { dg-warning "argument 1 null where > non-null expected because argument 4 is nonzero" } */ > + /* { dg-warning "argument 3 null where non-null expected because argument > 5 is nonzero" "argument 3" { target *-*-* } .-1 } */ > + if (t > 28) > + f3_1_3 (p0, p0, p0, t, t + 1); /* { dg-warning "argument 1 null where > non-null expected because argument 4 is nonzero" } */ > + /* { dg-warning "argument 3 null where non-null expected because argument > 5 is nonzero" "argument 3" { target *-*-* } .-1 } */ > + > + g4_1_2_4 (p0, px, px, px, u); > + g4_1_2_4 (px, p0, px, px, u); > + g4_1_2_4 (px, px, p0, px, u); > + g4_1_2_4 (px, px, px, p0, u); > + g4_1_2_4 (p0, px, px, px, 0); > + g4_1_2_4 (px, p0, px, px, 0); > + g4_1_2_4 (px, px, p0, px, 0); > + g4_1_2_4 (px, px, px, p0, 0); > + g4_1_2_4 (p0, px, px, px, 15); /* { dg-warning "argument 1 null where > non-null expected because argument 5 is nonzero" } */ > + if (u) > + g4_1_2_4 (p0, px, px, px, u); /* { dg-warning "argument 1 null where > non-null expected because argument 5 is nonzero" } */ > + g4_1_2_4 (px, p0, px, px, 16); /* { dg-warning "argument 2 null where > non-null expected because argument 5 is nonzero" } */ > + if (u > 2) > + g4_1_2_4 (px, p0, px, px, u); /* { dg-warning "argument 2 null where > non-null expected because argument 5 is nonzero" } */ > + g4_1_2_4 (px, px, p0, px, 17); > + if (u > 3) > + g4_1_2_4 (px, px, p0, px, u); > + g4_1_2_4 (px, px, px, p0, 18); /* { dg-warning "argument 4 null where > non-null expected because argument 5 is nonzero" } */ > + if (u < -2 || u > 10) > + g4_1_2_4 (px, px, px, p0, u); /* { dg-warning "argument 4 null where > non-null expected because argument 5 is nonzero" } */ > + > + g4_1_3_4 (p0, px, px, px, u); > + g4_1_3_4 (px, p0, px, px, u); > + g4_1_3_4 (px, px, p0, px, u); > + g4_1_3_4 (px, px, px, p0, u); > + g4_1_3_4 (p0, px, px, px, 0); > + g4_1_3_4 (px, p0, px, px, 0); > + g4_1_3_4 (px, px, p0, px, 0); > + g4_1_3_4 (px, px, px, p0, 0); > + g4_1_3_4 (p0, px, px, px, 20); /* { dg-warning "argument 1 null where > non-null expected because argument 5 is nonzero" } */ > + if (u > 4) > + g4_1_3_4 (p0, px, px, px, u); /* { dg-warning "argument 1 null where > non-null expected because argument 5 is nonzero" } */ > + g4_1_3_4 (px, p0, px, px, 21); > + if (u > 6 || u < -24) > + g4_1_3_4 (px, p0, px, px, u); > + g4_1_3_4 (px, px, p0, px, 22); /* { dg-warning "argument 3 null where > non-null expected because argument 5 is nonzero" } */ > + if (u > 9) > + g4_1_3_4 (px, px, p0, px, u - 3); /* { dg-warning "argument 3 null where > non-null expected because argument 5 is nonzero" } */ > + g4_1_3_4 (px, px, px, p0, 23); /* { dg-warning "argument 4 null where > non-null expected because argument 5 is nonzero" } */ > + if (u > 10) > + g4_1_3_4 (px, px, px, p0, u); /* { dg-warning "argument 4 null where > non-null expected because argument 5 is nonzero" } */ > + > + g4_2_3_4 (p0, px, px, px, u); > + g4_2_3_4 (px, p0, px, px, u); > + g4_2_3_4 (px, px, p0, px, u); > + g4_2_3_4 (px, px, px, p0, u); > + g4_2_3_4 (p0, px, px, px, 0); > + g4_2_3_4 (px, p0, px, px, 0); > + g4_2_3_4 (px, px, p0, px, 0); > + g4_2_3_4 (px, px, px, p0, 0); > + g4_2_3_4 (p0, px, px, px, 1); > + if (u > 12) > + g4_2_3_4 (p0, px, px, px, u); > + g4_2_3_4 (px, p0, px, px, 2); /* { dg-warning "argument 2 null where > non-null expected because argument 5 is nonzero" } */ > + if (u > 17) > + g4_2_3_4 (px, p0, px, px, u - 3); /* { dg-warning "argument 2 null where > non-null expected because argument 5 is nonzero" } */ > + g4_2_3_4 (px, px, p0, px, 3); /* { dg-warning "argument 3 null where > non-null expected because argument 5 is nonzero" } */ > + if (u > 24) > + g4_2_3_4 (px, px, p0, px, u); /* { dg-warning "argument 3 null where > non-null expected because argument 5 is nonzero" } */ > + g4_2_3_4 (px, px, px, p0, 4); /* { dg-warning "argument 4 null where > non-null expected because argument 5 is nonzero" } */ > + if (u > 42) > + g4_2_3_4 (px, px, px, p0, u); /* { dg-warning "argument 4 null where > non-null expected because argument 5 is 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); > + g16_1_3_5_7_11_13 (p0, p0, p0, p0, p0, p0, p0, p0, > + p0, p0, p0, p0, p0, p0, p0, p0, t); > + g16_1_3_5_7_11_13 (p0, p0, p0, p0, p0, p0, p0, p0, > + p0, p0, p0, p0, p0, p0, p0, p0, 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); /* { dg-warning "argument 13 null where non-null expected because > argument 17 is nonzero" } */ > + if (t > 122) > + g16_1_3_5_7_11_13 (px, p0, px, p0, px, p0, px, p0, p0, p0, px, p0, p0, > p0, p0, p0, t); /* { dg-warning "argument 13 null where non-null expected > because argument 17 is nonzero" } */ > +} > --- gcc/testsuite/c-c++-common/ubsan/nonnull-6.c.jj 2024-11-12 > 23:16:27.686866970 +0100 > +++ gcc/testsuite/c-c++-common/ubsan/nonnull-6.c 2024-11-13 > 09:13:05.077160640 +0100 > @@ -0,0 +1,28 @@ > +/* { dg-do run } */ > +/* { dg-options "-fsanitize=undefined -fno-sanitize-recover=undefined" } */ > + > +__attribute__((noipa, nonnull_if_nonzero (1, 4))) > +__attribute__((nonnull (3), nonnull_if_nonzero (5, 2))) void > +foo (void *a, unsigned long b, void *c, int d, void *e) > +{ > + (void) a; > + (void) b; > + (void) c; > + (void) d; > + (void) e; > +} > + > +__attribute__((noipa)) > +void > +bar (void *a, unsigned long b, void *c, int d, void *e) > +{ > + foo (a, b, c, d, e); > +} > + > +int > +main () > +{ > + char x; > + bar (&x, 42, &x, 1, &x); > + bar (0, 0, &x, 0, 0); > +} > --- gcc/testsuite/c-c++-common/ubsan/nonnull-7.c.jj 2024-11-12 > 23:16:27.686866970 +0100 > +++ gcc/testsuite/c-c++-common/ubsan/nonnull-7.c 2024-11-13 > 09:13:45.732591954 +0100 > @@ -0,0 +1,39 @@ > +/* { dg-do run } */ > +/* { dg-options "-fsanitize=nonnull-attribute" } */ > + > +__attribute__((noipa, nonnull_if_nonzero (1, 4))) > +__attribute__((nonnull (3), nonnull_if_nonzero (5, 2))) void > +foo (void *a, unsigned long b, void *c, int d, void *e) > +{ > + (void) a; > + (void) b; > + (void) c; > + (void) d; > + (void) e; > +} > + > +__attribute__((noipa)) > +void > +bar (void *a, unsigned long b, void *c, int d, void *e) > +{ > + foo (a, b, c, d, e); > +} > + > +int > +main () > +{ > + char x; > + bar (&x, 42, 0, 1, &x); > + bar (0, 25, &x, 7, &x); > + bar (&x, -82, &x, 68, 0); > + foo (&x, 42, 0, 1, &x); > + foo (0, 25, &x, 7, &x); > + foo (&x, -82, &x, 68, 0); > +} > + > +/* { dg-output "\.c:19:\[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:19:\[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:19:\[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:29:\[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:30:\[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:31:\[0-9]*:\[^\n\r]*null pointer passed as > argument 5, which is declared to never be null" } */ > > Jakub >