Add support for nonnull_if_nonzero attribute [PR117023]
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.
I'd then follow with the builtins.def changes (and eventually glibc
etc. would need to be adjusted too).
2024-11-28 Jakub Jelinek <jakub@redhat.com>
PR c/117023
gcc/
* gimple.h (infer_nonnull_range_by_attribute): Add a tree *
argument defaulted to NULL.
* gimple.cc (infer_nonnull_range_by_attribute): Add op2 argument.
Handle also nonnull_if_nonzero attributes.
* tree.cc (get_nonnull_args): Fix comment typo.
* builtins.cc (validate_arglist): Handle nonnull_if_nonzero attribute.
* tree-ssa-ccp.cc (pass_post_ipa_warn::execute): Handle
nonnull_if_nonzero attributes.
* ubsan.cc (instrument_nonnull_arg): Adjust
infer_nonnull_range_by_attribute caller. If it returned true and
filed in non-NULL arg2, check that arg2 is non-zero as another
condition next to checking that arg is zero.
* doc/extend.texi (nonnull_if_nonzero): Document new attribute.
gcc/c-family/
* c-attribs.cc (handle_nonnull_if_nonzero_attribute): New
function.
(c_common_gnu_attributes): Add nonnull_if_nonzero attribute.
(handle_nonnull_attribute): Fix comment typo.
* c-common.cc (struct nonnull_arg_ctx): Add other member.
(check_function_nonnull): Also check nonnull_if_nonzero attributes.
(check_nonnull_arg): Use different warning wording if pctx->other
is non-zero.
(check_function_arguments): Initialize ctx.other.
gcc/testsuite/
* gcc.dg/nonnull-8.c: New test.
* gcc.dg/nonnull-9.c: New test.
* gcc.dg/nonnull-10.c: New test.
* c-c++-common/ubsan/nonnull-6.c: New test.
* c-c++-common/ubsan/nonnull-7.c: New test.