On Tue, 12 Nov 2024, Jakub Jelinek wrote:
> Hi!
>
> As mentioned in an earlier thread, C2Y voted in a change which made
> various library APIs callable with NULL arguments in certain cases,
> e.g.
> memcpy (NULL, NULL, 0);
> is now valid, although
> memcpy (NULL, NULL, 1);
> remains invalid. This affects various APIs, including several of
> GCC builtins; plus on the C library side those APIs are often declared
> with nonnull attribute(s) as well.
>
> Florian suggested using the access attribute for this, but our docs
> explicitly say that access attribute doesn't imply nonnull and it doesn't
> cover e.g. the qsort case where the comparison function pointer may be
> also NULL if nmemb is 0, but must be non-zero otherwise.
> As this case affects 21 APIs in C standard and I think is going to affect
> various wrappers around those in various packages as well, I think it
> is a common thing that should have its own attribute, because we should
> still warn when people use
> qsort (NULL, 1, 1, NULL);
> etc., and similarly want to have -fsanitize=null instrumentation for those.
>
> So, the following patch introduces nonnull_if_nonzero attribute (or would
> you prefer cond_nonnull or some other name?), which has always 2 arguments,
> argument index of a pointer argument (like one argument nonnull) and
> argument index of an associated integral argument. If that argument is
> non-zero, it is UB to pass NULL to the pointer argument, if that argument
> is zero, it is valid. And changes various spots which already handled the
> nonnull attribute to handle this one as well, with sometimes using the
> ranger (or for -fsanitize=nonnull explicitly checking the associated
> argument value, so instead of if (!ptr) __ubsan_... (...); it will
> now do if (!ptr && sz) __ubsan_... (...);).
> I've so far omitted changing gimple_infer_range (am not 100% sure how I can
> use the ranger inside of the ranger) and changing the analyzer to handle it.
> And I haven't changed builtins.def etc. to make use of that attribute
> instead of nonnull where appropriate.
>
> What do you think about this? So far lightly tested.
> Ok for trunk if it passes full bootstrap/regtest?
> I'd then follow with the builtins.def changes (and eventually glibc
> etc. would need to be adjusted too).
>
> 2024-11-12 Jakub Jelinek <[email protected]>
>
> 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. */
I think this should say it returns true when *OP2 is set.
The rest looks reasonable to me. I think we should tell clang and/or
glibc people we're doing this.
Richard.
> 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 14:30:23.322700234 +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)
> {
> @@ -4559,13 +4561,12 @@ pass_post_ipa_warn::execute (function *f
>
> tree fntype = gimple_call_fntype (stmt);
> bitmap nonnullargs = get_nonnull_args (fntype);
> - if (!nonnullargs)
> - continue;
>
> 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 +4614,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
> 13:25:56.070137497 +0100
> +++ gcc/testsuite/c-c++-common/ubsan/nonnull-6.c 2024-11-12
> 13:26:03.996025915 +0100
> @@ -0,0 +1,27 @@
> +/* { 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 ()
> +{
> + bar ("", 42, "", 1, "");
> + bar (0, 0, "", 0, 0);
> +}
> --- gcc/testsuite/c-c++-common/ubsan/nonnull-7.c.jj 2024-11-12
> 13:26:12.436907082 +0100
> +++ gcc/testsuite/c-c++-common/ubsan/nonnull-7.c 2024-11-12
> 13:31:02.020830219 +0100
> @@ -0,0 +1,38 @@
> +/* { 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 ()
> +{
> + bar ("", 42, 0, 1, "");
> + bar (0, 25, "", 7, "");
> + bar ("", -82, "", 68, 0);
> + foo ("", 42, 0, 1, "");
> + foo (0, 25, "", 7, "");
> + foo ("", -82, "", 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:28:\[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:29:\[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:30:\[0-9]*:\[^\n\r]*null pointer passed as
> argument 5, which is declared to never be null" } */
>
> Jakub
>
>
--
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)