]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Extend nonnull_if_nonzero attribute [PR120520]
authorJakub Jelinek <jakub@redhat.com>
Mon, 30 Jun 2025 09:08:16 +0000 (11:08 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Mon, 30 Jun 2025 09:08:16 +0000 (11:08 +0200)
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  <jakub@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.

18 files changed:
gcc/analyzer/sm-malloc.cc
gcc/builtin-attrs.def
gcc/builtins.cc
gcc/builtins.def
gcc/c-family/c-attribs.cc
gcc/c-family/c-common.cc
gcc/doc/extend.texi
gcc/gimple-range-infer.cc
gcc/gimple.cc
gcc/gimple.h
gcc/testsuite/c-c++-common/ubsan/nonnull-8.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/ubsan/nonnull-9.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/nonnull-12.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/nonnull-13.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/nonnull-14.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/nonnull-9.c
gcc/tree-ssa-ccp.cc
gcc/ubsan.cc

index ee60b963b2b7ff881aabef36320ea14dbcccad38..b9563b9358878df9619a5cd7363b0edbdec6d39c 100644 (file)
@@ -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);
                    }
index 850efea11ca0a59a41002d93dfa93e41fab7161d..2b82fc2326e0acbf1b606f3473922f497ae8df13 100644 (file)
@@ -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)
index 3064bff1ae6323b9727418fdcfc3a2c5f42fb0d3..a2ce37268107b1d4eb4cd44b1ce25ac292cb3a39 100644 (file)
@@ -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;
       }
 
index 59a43a10058a2aa9d422ebb13a50a43e5af8b439..d7b2894bcfa06b71507120bd956143904f72f61b 100644 (file)
@@ -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)
index 5a0e3d328ba79b556029b54eda0dc5e00a00504b..5d7a31fd99b644f05f32754dc113f87f96c0a9f2 100644 (file)
@@ -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;
index f71cb2652d5a5596e97166d65a2730e51f1f0842..9d6929824dccaa2782c635919249be6e0fcc9de1 100644 (file)
@@ -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);
     }
 
index e9aa12ba1041b0241ba6ac2d3e0e086553b48e0e..046de360bbc15d615a9391a8911194dde243119f 100644 (file)
@@ -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.
 
index 72f71b980598c60cdd39094a309d2ce9905c0e88..612f66626af35dede7173f623b8621a9c0963c36 100644 (file)
@@ -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);
+                         }
+                     }
                  }
              }
          }
index 77b2e50fb60b1c1124e734a66f937679dfecdfbb..41908d4e29a6a0c77464423fd46ca641e22386be 100644 (file)
@@ -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);
            }
        }
     }
index 94d5a13fcb20e5f5ff82ec686471a78dd5ee888d..268884677e1174b4a6019b5548ad69cdfddfe00f 100644 (file)
@@ -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 (file)
index 0000000..e2320df
--- /dev/null
@@ -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 (file)
index 0000000..68a4417
--- /dev/null
@@ -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 (file)
index 0000000..d3eb475
--- /dev/null
@@ -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 (file)
index 0000000..15f2af0
--- /dev/null
@@ -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 (file)
index 0000000..6194dd0
--- /dev/null
@@ -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" } */
+}
index c0f95ca666315086a14bfcca8b1cb8092a0adc89..31e6c11b4d5acda8dace89d575f5c7a2ae626939 100644 (file)
@@ -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)
+{
+}
index 13cd81d9567271cc51a86123eb016b335e083ad4..f33cc042e9fb6590a9fd2b2ac35e55a7e4875bf1 100644 (file)
@@ -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);
index 3c130a660951df8d8b52f62d08808a5da2b4cf9a..6d748258b1e38d5990755fb3a32976d190f36813 100644 (file)
@@ -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)