[PATCH] analyzer: Add support of placement new and improved operator new [PR105948]

Benjamin Priour priour.be@gmail.com
Mon Jul 31 11:46:42 GMT 2023


Hi Dave,

On Fri, Jul 21, 2023 at 10:10 PM David Malcolm <dmalcolm@redhat.com> wrote:
 [...]

It looks like something's gone wrong with the indentation in the above:
> previously we had tab characters, but now I'm seeing a pair of spaces,
> which means this wouldn't line up properly.  This might be a glitch
> somewhere in our email workflow, but please check it in your editor
> (with visual whitespace enabled).
>

I'll double check that before submitting.


> [...snip...]
>
> Some comments on the test cases:
>
> > diff --git a/gcc/testsuite/g++.dg/analyzer/new-2.C
> b/gcc/testsuite/g++.dg/analyzer/new-2.C
> > new file mode 100644
> > index 00000000000..4e696040a54
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/analyzer/new-2.C
> > @@ -0,0 +1,50 @@
> > +// { dg-additional-options "-O0" }
> > +
> > +struct A
> > +{
> > +  int x;
> > +  int y;
> > +};
> > +
> > +void test_spurious_null_warning_throwing ()
> > +{
> > +  int *x = new int; /* { dg-bogus "dereference of possibly-NULL" } */
> > +  int *y = new int (); /* { dg-bogus "dereference of possibly-NULL"
> "non-throwing" } */
> > +  int *arr = new int[3]; /* { dg-bogus "dereference of possibly-NULL" }
> */
> > +  A *a = new A (); /* { dg-bogus "dereference of possibly-NULL"
> "throwing new cannot be null" } */
> > +
> > +  int z = *y + 2;
> > +  z = *x + 4; /* { dg-bogus "dereference of possibly-NULL 'x'" } */
> > +  /* { dg-warning "use of uninitialized value '\\*x'" "" { target *-*-*
> } .-1 } */
> > +  z = arr[0] + 4; /* { dg-bogus "dereference of possibly-NULL" } */
> > +
> > +  delete a;
> > +  delete y;
> > +  delete x;
> > +  delete[] arr;
> > +}
> > +
> > +void test_default_initialization ()
> > +{
> > +    int *y = ::new int;
> > +    int *x = ::new int (); /* { dg-bogus "dereference of possibly-NULL
> 'operator new" } */
> > +
> > +    int b = *x + 3; /* { dg-bogus "dereference of possibly-NULL" } */
> > +    /* { dg-bogus "use of uninitialized ‘*x’" "" { target *-*-* } .-1 }
> */
> > +    int a = *y + 2; /* { dg-bogus "dereference of possibly-NULL 'y'" }
> */
> > +    /* { dg-warning "use of uninitialized value '\\*y'" "no default
> init" { target *-*-* } .-1 } */
> > +
> > +    delete x;
> > +    delete y;
> > +}
> > +
> > +/* From clang core.uninitialized.NewArraySize
> > +new[] should not be called with an undefined size argument */
> > +
> > +void test_garbage_new_array ()
> > +{
> > +  int n;
> > +  int *arr = ::new int[n]; /* { dg-warning "use of uninitialized value
> 'n'" } */
> > +  arr[0] = 7;
> > +  ::delete[] arr; /* no warnings emitted here either */
> > +}
> > diff --git a/gcc/testsuite/g++.dg/analyzer/noexcept-new.C
> b/gcc/testsuite/g++.dg/analyzer/noexcept-new.C
> > new file mode 100644
> > index 00000000000..7699cd99cff
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/analyzer/noexcept-new.C
> > @@ -0,0 +1,48 @@
> > +/* { dg-additional-options "-O0 -fno-exceptions
> -fno-analyzer-suppress-followups" } */
> > +#include <new>
> > +
> > +/* Test non-throwing variants of operator new */
> > +
> > +struct A
> > +{
> > +  int x;
> > +  int y;
> > +};
> > +
> > +void test_throwing ()
> > +{
> > +  int* x = new int;
> > +  int* y = new int(); /* { dg-warning "dereference of possibly-NULL" }
> */
> > +  int* arr = new int[10];
> > +  A *a = new A(); /* { dg-warning "dereference of possibly-NULL" } */
> > +
> > +  int z = *y + 2;
> > +  z = *x + 4; /* { dg-warning "dereference of possibly-NULL 'x'" } */
> > +  /* { dg-warning "use of uninitialized value '\\*x'" "" { target *-*-*
> } .-1 } */
> > +  z = arr[0] + 4; /* { dg-warning "dereference of possibly-NULL 'arr'"
> } */
> > +  /* { dg-warning "use of uninitialized value '\\*arr'" "" { target
> *-*-* } .-1 } */
> > +  a->y = a->x + 3;
> > +
> > +  delete a;
> > +  delete y;
> > +  delete x;
> > +  delete[] arr;
> > +}
> > +
> > +void test_nonthrowing ()
> > +{
> > +  int* x = new(std::nothrow) int;
> > +  int* y = new(std::nothrow) int();
> > +  int* arr = new(std::nothrow) int[10];
> > +
> > +  int z = *y + 2; /* { dg-warning "dereference of NULL 'y'" } */
> > +  /* { dg-warning "use of uninitialized value '\\*y'" "" { target *-*-*
> } .-1 } */
> > +  z = *x + 4; /* { dg-warning "dereference of possibly-NULL 'x'" } */
> > +  /* { dg-warning "use of uninitialized value '\\*x'" "" { target *-*-*
> } .-1 } */
> > +  z = arr[0] + 4; /* { dg-warning "dereference of possibly-NULL 'arr'"
> } */
> > +  /* { dg-warning "use of uninitialized value '\\*arr'" "" { target
> *-*-* } .-1 } */
> > +
> > +  delete y;
> > +  delete x;
> > +  delete[] arr;
> > +}
>
> I see that we have test coverage for:
>   noexcept-new.C: -fno-exceptions with new vs nothrow-new
> whereas:
>   new-2.C has (implicitly) -fexceptions with new
>
> It seems that of the four combinations for:
>   - exceptions enabled or disabled
> and:
>   - throwing versus non-throwing new
> this is covering three of the cases but is missing the case of nothrow-
> new when exceptions are enabled.
> Presumably new-2.C should gain test coverage for this case.  Or am I
> missing something here?  Am I right in thinking that it's possible for
> the user to use nothrow new when exceptions are enabled to get a new
> that can fail and return nullptr?  Or is that not possible?
>
>
Thanks a lot for spotting that, the new test pointed out an issue with the
detection of nothrow.
It has been fixed and now both test cases behave similarly.
However, this highlighted a faulty test case I had written.

int* y = new(std::nothrow) int();
int z = *y + 2; /* { dg-warning "dereference of NULL 'y'" } */
/* { dg-warning "use of uninitialized value '\\*y'" "" { xfail *-*-* } .-1
} */ // (#) should be a bogus
delete y;

The test labelled (#) is wrong and should be a bogus instead.
If "y" is null then the allocation failed and dereferencing "y" will cause
a segfault, not a "use-of-uninitialized-value".
Thus we should stick to 'dereference of NULL 'y'" only.
If "y" is non-null then the allocation succeeded and "*y" is initialized
since we are calling a default initialization with the empty parenthesis.

This led me to consider having "null-dereference" supersedes
"use-of-uninitialized-value", but
new PR 110830 made me reexamine it.

I believe fixing PR 110830 is thus required before submitting this patch,
or we would have some extra irrelevant warnings.

I've also fixed the wording of "use-after-free" when using a placement
pointer after deallocation of its underlying buffer.

A *a = ::new A ();
int *lp = ::new (&a->y) int;
delete a;
int m = *lp; /* { dg-bogus "use after 'free' of 'lp'" "PREVIOUSLY" } */
int m = *lp; /* { dg-warning "use after 'delete' of 'lp'" "CURRENTLY" } */

Thanks
Benjamin


More information about the Gcc-patches mailing list