Bug 17308

Summary: nonnull attribute not as useful as it could be
Product: gcc Reporter: Ulrich Drepper <drepper.fsp>
Component: middle-endAssignee: Martin Sebor <msebor>
Status: NEW ---    
Severity: normal CC: eike, ericb, gabravier, gcc-bugs, gdr, hoffbrinkle, jakub, jeffreyalaw, jesse.millan, madcoder, manu, mark, msebor, segher, sjames, thutt
Priority: P2 Keywords: diagnostic, patch
Version: 3.4.1   
Target Milestone: ---   
See Also: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78673
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78917
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64463
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87489
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95515
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78817
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110617
Host: Target:
Build: Known to work:
Known to fail: Last reconfirmed: 2005-11-15 16:26:01
Bug Depends on: 16361    
Bug Blocks: 95507    

Description Ulrich Drepper 2004-09-03 20:03:03 UTC
The nonnull function attribute seems to recognize only null values which are
explicitly passed to the marked function.  It would be helpful if deduced values
are also reported.  Example:

void *f(void *p) __attribute__((nonnull(1)));
void *bar(void *p)
{
  if (p == (void *) 0) return f(p);
  return p;
}

There compiler knows that p is NULL in the call of f.  The generated code proves
it.  Still no warning.

I see the same behavior with gcc 3.5.
Comment 1 Andrew Pinski 2004-09-03 20:10:17 UTC
I think the issue here is that we issue the warning way before getting to the middle-end, in the front-
end.
Comment 2 Andrew Pinski 2005-06-24 23:13:09 UTC
*** Bug 22179 has been marked as a duplicate of this bug. ***
Comment 3 Andrew Pinski 2008-04-25 20:52:33 UTC
*** Bug 30043 has been marked as a duplicate of this bug. ***
Comment 4 thutt 2008-12-23 15:40:53 UTC
/*
  I concur with Ulrich, but three years on, using gcc 4.1.2.

  Although a parameter which is marked with the 'nonnull' attribute
  is demonstrably nonnull, and although the compiler recognizes it is
  specifically NULL, no warning is emitted.

  And, in fact, the compiler can end up generating bad code.

  Using built-in specs.
  Target: x86_64-redhat-linux
  Configured with: ../configure --prefix=/usr --mandir=/usr/share/man  \
    --infodir=/usr/share/info --enable-shared --enable-threads=posix   \
    --enable-checking=release --with-system-zlib --enable-__cxa_atexit \
    --disable-libunwind-exceptions --enable-libgcj-multifile           \
    --enable-languages=c,c++,objc,obj-c++,java,fortran,ada             \
    --enable-java-awt=gtk --disable-dssi --enable-plugin               \
    --with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre           \
    --with-cpu=generic --host=x86_64-redhat-linux
  Thread model: posix
  gcc version 4.1.2 20071124 (Red Hat 4.1.2-42)



        gcc -c  -o nonnull.o -O3 -Wall nonnull.c
        nonnull.c: In function ‘f1’:
        nonnull.c:52: warning: null argument where non-null required (argument 1)
        nonnull.c:52: warning: null argument where non-null required (argument 2)

        objdump --disassemble --reloc nonnull.o

        nonnull.o:     file format elf64-x86-64

        Disassembly of section .text:

        0000000000000000 <f1>:
           0:	f3 c3                	repz retq
           2:	0f 1f 80 00 00 00 00 	nopl   0x0(%rax)
           9:	0f 1f 80 00 00 00 00 	nopl   0x0(%rax)

  If '-Wall' is not provided, the compiler does not even warn about
  the NULL arguments.  It hardly matters if the compiler warns or not,
  the code that is generated for 'f1()' is simply bad code.  But, when
  no warning is provided by the compiler, the code is egregiously bad
  -- anyone wondering why their program doesn't work may disassemble
  the object module and see that nothing was emitted.

        0000000000000010 <f0>:
          10:	ba 00 04 00 00       	mov    $0x400,%edx     ; length = 1024
          15:	31 f6                	xor    %esi,%esi       ; second arg = NULL
          17:	bf 00 00 00 00       	mov    $0x0,%edi       ; first arg = buf
          1c:	48 c7 05 00 00 00 00 	movq   $0x0,0(%rip)    ; a = buf
          23:	00 00 00 00
          27:	48 c7 05 00 00 00 00 	movq   $0x0,0(%rip)    ; b = NULL
          2e:	00 00 00 00
          32:	e9 00 00 00 00       	jmpq   memcpy

  In this function, the compiler has automatically inlined
  'copy_block()', and obviously determined that the second parameter
  is NULL, as can be seen from addresses 15 & 27 -- but still no
  warning is presented.
 */

#include <stdlib.h>
#include <string.h>

extern void *a;
extern void *b;

static void __attribute__((nonnull(1, 2)))
copy_block(void *dest, void *src)
{
   memcpy(dest, src, 1024);
}


void f0(void)
{
   static char buf[100];
   a = buf;
   b = NULL;
   copy_block(a, b);
}

void f1(void)
{
   copy_block(NULL, NULL);
}

Comment 5 Martin Sebor 2010-02-15 20:51:11 UTC
I second Ulrich's request.

Besides nonnull, this enhancement would be useful in attribute printf
as well. For example, in the program below, both calls to printf() have
undefined behavior in C99 and should be diagnosed:

$ cat t.c && gcc -Wformat -pedantic -std=c99 -O3 t.c
int printf(const char*, ...)
  __attribute__((__nonnull__((1))))
  __attribute__ ((__format__ (__printf__, 1, 2)));

int main() {
  char *s = 0;
  printf(s, "");
  printf("%s", s);
}
$
Comment 6 Eric Blake 2011-02-11 16:58:01 UTC
It's been several years, and this attribute is still missing functionality about warning for deduced NULL values.  Can this be looked at?
Comment 7 Manuel López-Ibáñez 2011-02-11 19:43:20 UTC
(In reply to comment #6)
> It's been several years, and this attribute is still missing functionality
> about warning for deduced NULL values.  Can this be looked at?

GCC has as a unwritten policy to not emit warnings from the middle-end, except for a few exceptions. The front-ends do not have the infrastructure to do this kind of analysis, and GCC maintainers have repeatedly said that there is no intention to make gcc front-ends serve as a source code analysis tool like the clang analyzer. So, unless new contributors step forward with a convincing implementation of this, and for what I mean by "convincing" please read point 3 in the notes to beginners[*], the answer is: No, I wouldn't expect this to be implemented in the near or medium future. Sorry for the bad news, but I think it is better to be honest and save you the frustration. 

[*] http://gcc.gnu.org/wiki/GCC_Research
Comment 8 Eric Blake 2012-04-25 19:31:59 UTC
I hit this again today, and I'm still upset that gcc is doing such a poor job with (not) using this attribute as a way to improve code quality via decent warnings.

Basically, libvirt had an issue where we accidentally misused the nonnull attribute marker; we had added the marker in a header file, then later changed the C file to work with a NULL argument but without updating the header file to match: https://bugzilla.redhat.com/show_bug.cgi?id=815270

The calling code managed to pass in a NULL pointer to a parameter based on the semantics we saw in the .c file, but the NULL check in our function in question was "helpfully" elided by gcc since the attribute was still present, all without ever warning us that we were losing the NULL check.  As a result, the code misbehaved when it dereferenced NULL.  If gcc is smart enough to elide code based on the attribute, it should be smart enough to issue a warning that we have dead code, and the only reason the code is assumed to be dead is because of a suspicious attribute.  Had we had the warning, we would have known to either remove our dead NULL check, or to fix the header to no longer have the (now-inappropriate) attribute.

In other words, with a header like:

void foo(void *bar) __attribute__((nonnull(1)));

and a C file like:

void foo(void *bar) { if (!bar) abort(); }

Even if you decide that you are unable to warn about a call to foo(var) because the only way to analyze that var might be NULL is in the middle end but the warning is only emitted by the front end, AT LEAST you could have warned that the 'if (!bar)' conditional is assumed to be dead code based on the attribute placed on var.
Comment 9 Manuel López-Ibáñez 2012-04-25 20:00:44 UTC
(In reply to comment #8)
> Even if you decide that you are unable to warn about a call to foo(var) because
> the only way to analyze that var might be NULL is in the middle end but the
> warning is only emitted by the front end, AT LEAST you could have warned that
> the 'if (!bar)' conditional is assumed to be dead code based on the attribute
> placed on var.

GCC does not warn about unreachable code anymore:

http://gcc.gnu.org/ml/gcc-help/2011-05/msg00360.html

Someone would have to contribute a new -Wunreachable-code implementation. I honestly don't know what kind of implementation would be acceptable by GCC maintainers. Maybe one implemented in the FE? If so, it would require exactly the same infrastructure necessary to implement what Ulrich asks for above.

Sorry, comment #7 still applies.
Comment 10 Manuel López-Ibáñez 2015-07-25 20:04:10 UTC
*** Bug 46936 has been marked as a duplicate of this bug. ***
Comment 11 Mark Wielaard 2015-09-11 21:51:18 UTC
(In reply to Eric Blake from comment #8)
> In other words, with a header like:
> 
> void foo(void *bar) __attribute__((nonnull(1)));
> 
> and a C file like:
> 
> void foo(void *bar) { if (!bar) abort(); }
> 
> Even if you decide that you are unable to warn about a call to foo(var)
> because the only way to analyze that var might be NULL is in the middle end
> but the warning is only emitted by the front end, AT LEAST you could have
> warned that the 'if (!bar)' conditional is assumed to be dead code based on
> the attribute placed on var.

This part has now been implemented:
https://gcc.gnu.org/ml/gcc-patches/2015-09/msg00626.html
Comment 12 Jakub Jelinek 2016-02-17 17:39:24 UTC
(In reply to Mark Wielaard from comment #11)
> This part has now been implemented:
> https://gcc.gnu.org/ml/gcc-patches/2015-09/msg00626.html

And updated in
https://gcc.gnu.org/ml/gcc-patches/2016-02/msg01103.html
Comment 13 frankhb1989 2016-11-11 06:37:33 UTC
It should be noted that current implementation is conflict with assertions in some cases, as in https://lists.fedoraproject.org/archives/list/devel@lists.fedoraproject.org/thread/VA2QCXJGXVLH43327TRR5UM2BP52DWIC/. The purpose of such an attribute is not emphasized in the documentation, which leads to the confusion.

I'm actually not against the semantics it implies, but I find the warning actually reduce its availability for other reasons.

Note `assert` is required to be supported by the standard, so it will work across different implementations, including those do not support `__attribute__` at all. To get the code portable, the attributes will always be wrapped in macros whose names are not reserved identifiers. It should be natural that by conditionally defining such macros as nothing, each configuration of every implementation would be happy eventually, because modifying assertions everywhere in current code (instead modifying the macros) is obviously infeasible in general.

A question is, when to disable the attribute?

For debug build configurations (with `NDEBUG` undefined), it has almost no side effect by conditional definition based on some conditions (like `NDEBUG`), except one: the conditions may have to be duplicated in #ifdef/#if in different places of the code. It is still not a big trouble for `assert` normally, but it can be a disaster for custom assertion macros when the conditions are complicated enough. And though rarely, it can be a nightmare after the condition macros are defined (after necessary #undef) more than once, by different authors of the code.

For release configurations (such as builds with properly defined `NDEBUG`), the problem is more serious. Since the attribute is designed as a hint of optimization, it should not be defined as nothing besides debugging. But to modify all occurrence of assertions is still even more absurd, whether they will generate code or not.

So there is a dilemma: which one to keep, the assertions or the attribute? Removing the assertions is infeasible; unconditionally disabling the attribute is illogical (and totally nonsense for old versions of GCC which I need to adapt to). And clearly, I don't want to maintain multiple copies of the code just for different build configurations; I definitely need both of them in such cases. Thus either choice is essentially bad.

The final workaround (*not the fix*) is to disable the warning itself, which is configuration-neutral and it should does work at least as old versions of GCC. However, old compiler drivers do not recognize such options. To maintain the divergence in build scripts is dirty. So it should better be... pragmas? The last problem: #pragma or _Pragma to disable such diagnostics does not work. Is it a bug, or unsupported at all?
Comment 14 Martin Sebor 2016-12-05 02:08:07 UTC
Testing a patch.
Comment 15 Martin Sebor 2016-12-05 23:00:15 UTC
Patch posted for review:
https://gcc.gnu.org/ml/gcc-patches/2016-12/msg00399.html
Comment 16 Martin Sebor 2016-12-14 17:23:48 UTC
Author: msebor
Date: Wed Dec 14 17:23:16 2016
New Revision: 243661

URL: https://gcc.gnu.org/viewcvs?rev=243661&root=gcc&view=rev
Log:
PR c/78673 - sprintf missing attribute nonnull on destination argument
PR c/17308 - nonnull attribute not as useful as it could be

gcc/ChangeLog:

	PR c/17308
	* builtin-attrs.def (ATTR_NONNULL_1_1, ATTR_NONNULL_1_2): Defined.
	(ATTR_NONNULL_1_3, ATTR_NONNULL_1_4, ATTR_NONNULL_1_5): Same.
	(ATTR_NOTHROW_NONNULL_1_1, ATTR_NOTHROW_NONNULL_1_2): Same.
	(ATTR_NOTHROW_NONNULL_1_3, ATTR_NOTHROW_NONNULL_1_4): Same.
	(ATTR_NOTHROW_NONNULL_1_5): Same.
	(ATTR_NONNULL_1_FORMAT_PRINTF_1_2): Same.
	(ATTR_NONNULL_1_FORMAT_PRINTF_2_0): Same.
	(ATTR_NONNULL_1_FORMAT_PRINTF_2_3): Same.
	(ATTR_NONNULL_1_FORMAT_PRINTF_3_0): Same.
	(ATTR_NONNULL_1_FORMAT_PRINTF_3_4): Same.
	(ATTR_NONNULL_1_FORMAT_PRINTF_4_0): Same.
	(ATTR_NONNULL_1_FORMAT_PRINTF_4_5): Same.
	* builtins.c (validate_arg): Add argument.  Treat null pointers
	passed to nonnull arguments as invalid.
	(validate_arglist): Same.
	* builtins.def (fprintf, fprintf_unlocked): Add nonnull attribute.
	(printf, printf_unlocked, sprintf. vfprintf, vsprintf): Same.
	(__sprintf_chk, __vsprintf_chk, __fprintf_chk, __vfprintf_chk): Same.
	* calls.c (get_nonnull_ags, maybe_warn_null_arg): New functions.
	(initialize_argument_information): Diagnose null pointers passed to
	arguments declared nonnull.
	* calls.h (get_nonnull_args): Declared.

gcc/c-family/ChangeLog:

	PR c/17308
	* c-common.c (check_nonnull_arg): Disable when optimization
	is enabled.

gcc/testsuite/ChangeLog:

	PR c/17308
	* gcc.dg/builtins-nonnull.c: New test.
	* gcc.dg/nonnull-4.c: New test.


Added:
    trunk/gcc/testsuite/gcc.dg/builtins-nonnull.c
    trunk/gcc/testsuite/gcc.dg/nonnull-4.c
Modified:
    trunk/gcc/ChangeLog
    trunk/gcc/builtin-attrs.def
    trunk/gcc/builtins.c
    trunk/gcc/builtins.def
    trunk/gcc/c-family/ChangeLog
    trunk/gcc/c-family/c-common.c
    trunk/gcc/calls.c
    trunk/gcc/calls.h
    trunk/gcc/testsuite/ChangeLog
Comment 17 Martin Sebor 2016-12-14 17:24:54 UTC
Implemented in 7.0 in r243661.
Comment 18 Segher Boessenkool 2016-12-14 20:12:26 UTC
This gives a warning in powerpc-linuc (which breaks bootstrap), when
compiling tree-inline.c:

/home/segher/src/gcc/gcc/vec.h:1613:5: error: argument 1 null where non-null expected [-Werror=nonnull]
   memset (&(address ()[oldlen]), 0, sz);
   ^~~~~~

Configuring with --disable-werror ends with a bootstrap comparison failure:

Bootstrap comparison failure!
gcc/gcc.o differs
gcc/plugin.o differs

(which could of course be something else).
Comment 19 Segher Boessenkool 2016-12-14 20:15:17 UTC
powerpc64-linux, even.
Comment 20 Martin Sebor 2016-12-14 21:01:46 UTC
I've raised bug 78817 for the powerpc64le bootstrap failure.
Comment 21 Andrew Pinski 2023-02-02 22:39:23 UTC
The patch was reverted, see PR 78817 on the reasons why.
Comment 22 Andrew Pinski 2023-02-02 22:39:39 UTC
*** Bug 95515 has been marked as a duplicate of this bug. ***
Comment 23 Andrew Pinski 2023-02-02 22:43:17 UTC
The patch which would have "fixed" this was reverted as there was too many false positives and that happens when you do optimization based warnings ...
I don't know if we want to keep this open or close this as won't fix though. 
It has been 6 years since this was tried too and we still most likely have just as many false positives as before even.
I also suspect many of these new warnings we are doing in recent years really should not be part of -Wall because of how many false positives we have. GCC has been getting a recent attention because of the false positives warnings too.
Comment 24 Segher Boessenkool 2023-02-03 01:19:26 UTC
(In reply to Andrew Pinski from comment #23)
> I also suspect many of these new warnings we are doing in recent years
> really should not be part of -Wall because of how many false positives we
> have. GCC has been getting a recent attention because of the false positives
> warnings too.

Current documentation says

'-Wall'
     This enables all the warnings about constructions that some users
     consider questionable, and that are easy to avoid (or modify to
     prevent the warning), even in conjunction with macros.

and

'-Wextra'
     This enables some extra warning flags that are not enabled by
     '-Wall'.

We don't document at all what options should be enabled by -Wall, what
options should be enabled by -W, and which should be done by neither.  The
current documentation for -Wall is very noncommittal.

It all is a tradeoff of course.  IMO our documentation should make that clear
as well.

In my view, -Wall should enable all warnings that have few false positives
(less than 5% or 10%, say), and when they do this is easy to avoid, or perhaps
the warning points out very important (security) problems.

-W is the same but with higher tolerance for false positives.  And the
warnings that are not so useful, or are hard to avoid, and have a high false
positive rate, should not be enabled by either.

(Oh, and please note that -Werror is not part of these considerations at all.
When -Werror makes things "break" this is just a learning opportunity for
whoever asked for it.  Maybe we should document -Werror as an alias of
-Wself-flagellation).