detect out-of-bounds stores by atomic functions [PR102453]

Message ID 72c637b5-f6cc-22d6-af62-65166f5ab069@gmail.com
State New
Headers show
Series
  • detect out-of-bounds stores by atomic functions [PR102453]
Related show

Commit Message

Richard Purdie via Gcc-patches Oct. 11, 2021, 9:25 p.m.
The attached change extends GCC's warnings for out-of-bounds
stores to cover atomic (and __sync) built-ins.

Rather than hardcoding the properties of these built-ins just
for the sake of the out-of-bounds detection, on the assumption
that it might be useful for future optimizations as well, I took
the approach of extending class attr_fnspec to express their
special property that they encode the size of the access in their
name.

I also took the liberty of making attr_fnspec assignable (something
the rest of my patch relies on), and updating some comments for
the characters the class uses to encode function properties, based
on my understanding of their purpose.

Tested on x86_64-linux.

Martin

Comments

Richard Purdie via Gcc-patches Oct. 12, 2021, 6:52 a.m. | #1
On Mon, Oct 11, 2021 at 11:25 PM Martin Sebor <msebor@gmail.com> wrote:
>

> The attached change extends GCC's warnings for out-of-bounds

> stores to cover atomic (and __sync) built-ins.

>

> Rather than hardcoding the properties of these built-ins just

> for the sake of the out-of-bounds detection, on the assumption

> that it might be useful for future optimizations as well, I took

> the approach of extending class attr_fnspec to express their

> special property that they encode the size of the access in their

> name.

>

> I also took the liberty of making attr_fnspec assignable (something

> the rest of my patch relies on), and updating some comments for

> the characters the class uses to encode function properties, based

> on my understanding of their purpose.

>

> Tested on x86_64-linux.


Hmm, so you place 'A' at an odd place (where the return value is specified),
but you do not actually specify the behavior on the return value.  Shoudln't

+     'A'        specifies that the function atomically accesses a constant
+               1 << N bytes where N is indicated by character 3+2i

maybe read

    'A'     specifies that the function returns the memory pointed to
by argument
             one of size 1 << N bytes where N is indicated by
character 3 +2i accessed atomically

?  I also wonder if it's necessary to constrain this to 'atomic' accesses
for the purpose of the patch and whether that detail could be omitted to
eventually make more use of it?

Likewise

+     '0'...'9'  specifies the size of value written/read is given either
+               by the specified argument, or for atomic functions, by
+               2 ^ N where N is the constant value denoted by the character

should mention (excluding '0') for the argument position.

   /* length of the fn spec string.  */
-  const unsigned len;
+  unsigned len;

why that?

+  /* Return true of the function is an __atomic or __sync built-in.  */

you didn't specify that for 'A' ...

+  bool
+  atomic_p () const
+  {
+    return str[0] == 'A';
+  }

+attr_fnspec
+atomic_builtin_fnspec (tree callee)
+{
+  switch (DECL_FUNCTION_CODE (callee))
+    {
+#define BUILTIN_ACCESS_SIZE_FNSPEC(N, lgsz)            \
+      BUILT_IN_ATOMIC_LOAD_ ## N:                      \
+       return "Ap" "R" lgsz;

note that doing this for atomics makes those no longer a compiler barrier
for (aliased) loads and stores which means they are no longer a reliable
way to implement locks.  That's a reason why I never pushed a
PTA/alias patch I have to open-code this.

Thus, do we really want to do this?

Richard.

>

> Martin
Richard Purdie via Gcc-patches Oct. 12, 2021, 7:44 p.m. | #2
On 10/12/21 12:52 AM, Richard Biener wrote:
> On Mon, Oct 11, 2021 at 11:25 PM Martin Sebor <msebor@gmail.com> wrote:

>>

>> The attached change extends GCC's warnings for out-of-bounds

>> stores to cover atomic (and __sync) built-ins.

>>

>> Rather than hardcoding the properties of these built-ins just

>> for the sake of the out-of-bounds detection, on the assumption

>> that it might be useful for future optimizations as well, I took

>> the approach of extending class attr_fnspec to express their

>> special property that they encode the size of the access in their

>> name.

>>

>> I also took the liberty of making attr_fnspec assignable (something

>> the rest of my patch relies on), and updating some comments for

>> the characters the class uses to encode function properties, based

>> on my understanding of their purpose.

>>

>> Tested on x86_64-linux.

> 

> Hmm, so you place 'A' at an odd place (where the return value is specified),

> but you do not actually specify the behavior on the return value.  Shoudln't

> 

> +     'A'        specifies that the function atomically accesses a constant

> +               1 << N bytes where N is indicated by character 3+2i

> 

> maybe read

> 

>      'A'     specifies that the function returns the memory pointed to

> by argument

>               one of size 1 << N bytes where N is indicated by

> character 3 +2i accessed atomically

> 

> ?


I didn't think the return value would be interesting because in
general (parallel accesses) it's not related (in an observable
way) to the value of the dereferenced operand.  Not all
the built-ins also return a value (e.g., atomic_store), and
whether or not one does return the argument would need to be
encoded somehow because it cannot be determined from the return
type (__atomic_compare_exchange and __atomic_test_and_set return
bool that's not necessarily the value of the operand).  Also,
since the functions return the operand value either before or
after the update, we'd need another letter to describe that.
(This alone could be dealt with simply by using 'A' and 'a',
but that's not enough for the other cases.)

So with all these possibilities I don't think encoding
the return value at this point is worthwhile.  If/when this
enhancement turns out to be used for optimization and we think
encoding the return value would be helpful, I'd say let's
revisit it then.  The accessor APIs should make it a fairly
straightforward exercise.

> I also wonder if it's necessary to constrain this to 'atomic' accesses

> for the purpose of the patch and whether that detail could be omitted to

> eventually make more use of it?


I pondered the same question but I couldn't think of any other
built-ins with similar semantics (read-write-modify, return
a result either pre- or post-modification), so I opted for
simplicity.  I am open to generalizing it if/when there is
a function I could test it with, although I'm not sure
the current encoding scheme has enough letters and letter
positions to describe the effects in their full generality.

> 

> Likewise

> 

> +     '0'...'9'  specifies the size of value written/read is given either

> +               by the specified argument, or for atomic functions, by

> +               2 ^ N where N is the constant value denoted by the character

> 

> should mention (excluding '0') for the argument position.


Sure, I'll update the comment if you think this change is worth
pursuing.

> 

>     /* length of the fn spec string.  */

> -  const unsigned len;

> +  unsigned len;

> 

> why that?


The const member is what prevents the struct from being assignable,
which is what the rest of the patch depends on.

> 

> +  /* Return true of the function is an __atomic or __sync built-in.  */

> 

> you didn't specify that for 'A' ...

> 

> +  bool

> +  atomic_p () const

> +  {

> +    return str[0] == 'A';

> +  }

> 

> +attr_fnspec

> +atomic_builtin_fnspec (tree callee)

> +{

> +  switch (DECL_FUNCTION_CODE (callee))

> +    {

> +#define BUILTIN_ACCESS_SIZE_FNSPEC(N, lgsz)            \

> +      BUILT_IN_ATOMIC_LOAD_ ## N:                      \

> +       return "Ap" "R" lgsz;

> 

> note that doing this for atomics makes those no longer a compiler barrier

> for (aliased) loads and stores which means they are no longer a reliable

> way to implement locks.  That's a reason why I never pushed a

> PTA/alias patch I have to open-code this.

> 

> Thus, do we really want to do this?


That's my question to you :) If you don't think this attr_fnspec
extension would be useful for optimization I'll drop this part
of the patch and open-code it separately only for the out-of-bounds
diagnostics.  I'd started out that way but the fnspec class made
the code cleaner and if it could be used elsewhere so much
the better.  Please let me know.

Martin
Richard Purdie via Gcc-patches Oct. 13, 2021, 8:15 a.m. | #3
On Tue, Oct 12, 2021 at 9:44 PM Martin Sebor <msebor@gmail.com> wrote:
>

> On 10/12/21 12:52 AM, Richard Biener wrote:

> > On Mon, Oct 11, 2021 at 11:25 PM Martin Sebor <msebor@gmail.com> wrote:

> >>

> >> The attached change extends GCC's warnings for out-of-bounds

> >> stores to cover atomic (and __sync) built-ins.

> >>

> >> Rather than hardcoding the properties of these built-ins just

> >> for the sake of the out-of-bounds detection, on the assumption

> >> that it might be useful for future optimizations as well, I took

> >> the approach of extending class attr_fnspec to express their

> >> special property that they encode the size of the access in their

> >> name.

> >>

> >> I also took the liberty of making attr_fnspec assignable (something

> >> the rest of my patch relies on), and updating some comments for

> >> the characters the class uses to encode function properties, based

> >> on my understanding of their purpose.

> >>

> >> Tested on x86_64-linux.

> >

> > Hmm, so you place 'A' at an odd place (where the return value is specified),

> > but you do not actually specify the behavior on the return value.  Shoudln't

> >

> > +     'A'        specifies that the function atomically accesses a constant

> > +               1 << N bytes where N is indicated by character 3+2i

> >

> > maybe read

> >

> >      'A'     specifies that the function returns the memory pointed to

> > by argument

> >               one of size 1 << N bytes where N is indicated by

> > character 3 +2i accessed atomically

> >

> > ?

>

> I didn't think the return value would be interesting because in

> general (parallel accesses) it's not related (in an observable

> way) to the value of the dereferenced operand.  Not all

> the built-ins also return a value (e.g., atomic_store), and

> whether or not one does return the argument would need to be

> encoded somehow because it cannot be determined from the return

> type (__atomic_compare_exchange and __atomic_test_and_set return

> bool that's not necessarily the value of the operand).  Also,

> since the functions return the operand value either before or

> after the update, we'd need another letter to describe that.

> (This alone could be dealt with simply by using 'A' and 'a',

> but that's not enough for the other cases.)

>

> So with all these possibilities I don't think encoding

> the return value at this point is worthwhile.  If/when this

> enhancement turns out to be used for optimization and we think

> encoding the return value would be helpful, I'd say let's

> revisit it then.  The accessor APIs should make it a fairly

> straightforward exercise.


I though it would be useful for points-to analysis since knowing how
the return value is composed improves the points-to result for it.

Note that IPA mod-ref now synthesizes fn-spec and might make use
of 'A' if it were not narrowly defined.  Sure it's probably difficult to
fully specify the RMW cycle that's eventually done but since we
have a way to specify a non-constant size of accesses as passed
by a parameter it would be nice to allow specifying a constant size
anyhow.  It just occured to me we could use "fake" parameters to
encode those, so for

void foo (int *);

use like ". R2c4" saying that parameter 1 is read with the size
specified by (non-existing) parameter 2 which is specified as
'c'onstant 1 << 4.

Alternatively a constant size specification could use alternate
encoding 'a' to 'f'.  That said, if 'A' is not suppose to specify
the return value it shouldn't be in the return value specification...

> > I also wonder if it's necessary to constrain this to 'atomic' accesses

> > for the purpose of the patch and whether that detail could be omitted to

> > eventually make more use of it?

>

> I pondered the same question but I couldn't think of any other

> built-ins with similar semantics (read-write-modify, return

> a result either pre- or post-modification), so I opted for

> simplicity.  I am open to generalizing it if/when there is

> a function I could test it with, although I'm not sure

> the current encoding scheme has enough letters and letter

> positions to describe the effects in their full generality.

>

> >

> > Likewise

> >

> > +     '0'...'9'  specifies the size of value written/read is given either

> > +               by the specified argument, or for atomic functions, by

> > +               2 ^ N where N is the constant value denoted by the character

> >

> > should mention (excluding '0') for the argument position.

>

> Sure, I'll update the comment if you think this change is worth

> pursuing.

>

> >

> >     /* length of the fn spec string.  */

> > -  const unsigned len;

> > +  unsigned len;

> >

> > why that?

>

> The const member is what prevents the struct from being assignable,

> which is what the rest of the patch depends on.

>

> >

> > +  /* Return true of the function is an __atomic or __sync built-in.  */

> >

> > you didn't specify that for 'A' ...

> >

> > +  bool

> > +  atomic_p () const

> > +  {

> > +    return str[0] == 'A';

> > +  }

> >

> > +attr_fnspec

> > +atomic_builtin_fnspec (tree callee)

> > +{

> > +  switch (DECL_FUNCTION_CODE (callee))

> > +    {

> > +#define BUILTIN_ACCESS_SIZE_FNSPEC(N, lgsz)            \

> > +      BUILT_IN_ATOMIC_LOAD_ ## N:                      \

> > +       return "Ap" "R" lgsz;

> >

> > note that doing this for atomics makes those no longer a compiler barrier

> > for (aliased) loads and stores which means they are no longer a reliable

> > way to implement locks.  That's a reason why I never pushed a

> > PTA/alias patch I have to open-code this.

> >

> > Thus, do we really want to do this?

>

> That's my question to you :) If you don't think this attr_fnspec

> extension would be useful for optimization I'll drop this part

> of the patch and open-code it separately only for the out-of-bounds

> diagnostics.  I'd started out that way but the fnspec class made

> the code cleaner and if it could be used elsewhere so much

> the better.  Please let me know.


Well - as said, we're relying on the property of a function call being
a barrier for global/escaped memory for things like pthread_mutex_lock
and at least some of the atomic builtins might be used as locking primitives
and most definitely that would break.  For example

int cnt;
int lock;

void foo(int n)
{
   for (int i = 0; i < n; ++i)
      {
         int val = 0;
         int ret;
         if (__atomic_compare_exchange (&lock, &val, 1, false, __ATOMIC_ACQ))
           {
              cnt++;
           }
      }
}

will see PRE applied to the load of 'cnt' with your patch(?), that is, the lock
is not a barrier for protected loads and stores.  Of course you have to provide
some incentive to perform an invalid optimization and the trivial one like
initialization before the locked area that can be CSEd is probably invalid
since the init then has data races.

So yes, I think we should be very careful here.  The reason why I chickened
out myself doing the obvious improvement for the atomic builtins...

Any opinions from others?

Richard.

>

> Martin

Patch

Detect overflow by atomic functions [PR102453].

Resolves:
PR middle-end/102453 - buffer overflow by atomic built-ins not diagnosed

gcc/ChangeLog:

	PR middle-end/102453
	* attr-fnspec.h (attr_fnspec): Make members modifiable or static.
	(attr_fnspec::arg_access_size_constant_p): New function.
	(attr_fnspec::atomic_p): New function.
	* builtins.c (atomic_builtin_fnspec): New function.
	* gimple-ssa-warn-access.cc (pass_waccess::check_builtin): Handle
	atomic built-ins.
	* tree-ssa-alias.c (attr_fnspec::verify): Handle new characters.

gcc/testsuite/ChangeLog:

	PR middle-end/102453
	* gcc.dg/Warray-bounds-90.c: New test.
	* gcc.dg/Wstringop-overflow-77.c: New test.
	* gcc.dg/Wstringop-overflow-78.c: New test.
	* gcc.dg/Wstringop-overflow-79.c: New test.
	* gcc.dg/Wstringop-overflow-80.c: New test.
	* c-c++-common/gomp/atomic-4.c: Avoid an out-of-bounds access.

diff --git a/gcc/attr-fnspec.h b/gcc/attr-fnspec.h
index 1154c30e7b0..9da29902bcd 100644
--- a/gcc/attr-fnspec.h
+++ b/gcc/attr-fnspec.h
@@ -22,8 +22,11 @@ 
    describing side effects of a function as follows:
 
    character 0  specifies properties of return values as follows:
-     '1'...'4'  specifies number of argument function returns (as in memset)
+     '1'...'4'  specifies the 1-based argument number the function returns
+		(e.g., '1' for memset)
      'm'	specifies that returned value is noalias (as in malloc)
+     'A'        specifies that the function atomically accesses a constant
+		1 << N bytes where N is indicated by character 3+2i
      '.'	specifies that nothing is known.
    character 1  specifies additional function properties
      ' '        specifies that nothing is known
@@ -41,9 +44,9 @@ 
 		written and does not escape
      'w' or 'W' specifies that the memory pointed to by the parameter does not
 		escape
-     '1'....'9' specifies that the memory pointed to by the parameter is
-		copied to memory pointed to by different parameter
-		(as in memcpy).
+     '1'....'9' specifies the 1-based parameter number into which the memory
+		pointed to by the argument is copied  (e.g., '1' for memcpy
+		and '2' for bcopy).
      '.'	specifies that nothing is known.
    The uppercase letter in addition specifies that the memory pointed to
    by the parameter is not dereferenced.  For 'r' only read applies
@@ -54,8 +57,9 @@ 
      ' '        nothing is known
      't'	the size of value written/read corresponds to the size of
 		of the pointed-to type of the argument type
-     '1'...'9'  specifies the size of value written/read is given by the
-		specified argument
+     '0'...'9'  specifies the size of value written/read is given either
+		by the specified argument, or for atomic functions, by
+		2 ^ N where N is the constant value denoted by the character
  */
 
 #ifndef ATTR_FNSPEC_H
@@ -67,14 +71,14 @@  private:
   /* fn spec attribute string.  */
   const char *str;
   /* length of the fn spec string.  */
-  const unsigned len;
+  unsigned len;
   /* Number of characters specifying return value.  */
-  const unsigned int return_desc_size = 2;
+  static constexpr unsigned int return_desc_size = 2;
   /* Number of characters specifying size.  */
-  const unsigned int arg_desc_size = 2;
+  static constexpr unsigned int arg_desc_size = 2;
 
   /* Return start of specifier of arg i.  */
-  unsigned int arg_idx (int i)
+  static unsigned int arg_idx (int i)
   {
     return return_desc_size + arg_desc_size * i;
   }
@@ -195,9 +199,22 @@  public:
     return str[idx + 1] == 't';
   }
 
-  /* Return true if memory pointer to by argument is copied to a memory
+  /* Return true if the size of the memory acccess for the argument is
+     constant and if so set *SIZE to the constant.  */
+  bool
+  arg_access_size_constant_p (unsigned int i, unsigned int *size)
+  {
+    if (!atomic_p ())
+      return false;
+    unsigned int idx = arg_idx (i);
+    gcc_checking_assert (arg_specified_p (i));
+    *size = 1 << (str[idx + 1] - '0');
+    return true;
+  }
+
+  /* Return true if memory pointed to by argument is copied to a memory
      pointed to by a different argument (as in memcpy).
-     In this case set ARG.  */
+     In this case set ARG to the zero-based argument number.  */
   bool
   arg_copied_to_arg_p (unsigned int i, unsigned int *arg)
   {
@@ -264,6 +281,13 @@  public:
     return str[1] == 'C' || str[1] == 'P';
   }
 
+  /* Return true of the function is an __atomic or __sync built-in.  */
+  bool
+  atomic_p () const
+  {
+    return str[0] == 'A';
+  }
+
   /* Check validity of the string.  */
   void verify ();
 
@@ -277,5 +301,6 @@  public:
 
 extern attr_fnspec gimple_call_fnspec (const gcall *stmt);
 extern attr_fnspec builtin_fnspec (tree);
+extern attr_fnspec atomic_builtin_fnspec (tree);
 
 #endif /* ATTR_FNSPEC_H  */
diff --git a/gcc/builtins.c b/gcc/builtins.c
index f1c3fea3583..b44b566888b 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -10957,9 +10957,11 @@  access_ref::offset_bounded () const
   return wi::to_offset (min) <= offrng[0] && offrng[1] <= wi::to_offset (max);
 }
 
-/* If CALLEE has known side effects, fill in INFO and return true.
-   See tree-ssa-structalias.c:find_func_aliases
-   for the list of builtins we might need to handle here.  */
+/* If CALLEE has known side effects, return an attr_fnspec object describing
+   those effects.  Otherwise return an empty attr_fnspec whose known_p() is
+   false.
+   See tree-ssa-structalias.c:find_func_aliases for the list of builtins we
+   might need to handle here.  */
 
 attr_fnspec
 builtin_fnspec (tree callee)
@@ -11138,3 +11140,70 @@  builtin_fnspec (tree callee)
 	return "";
     }
 }
+
+/* For an atomic or sync built-in CALLEE with known side effects, return
+   an attr_fnspec object describing those effects.  Otherwise return
+   an empty attr_fnspec whose known_p() is false.  */
+
+attr_fnspec
+atomic_builtin_fnspec (tree callee)
+{
+  switch (DECL_FUNCTION_CODE (callee))
+    {
+#define BUILTIN_ACCESS_SIZE_FNSPEC(N, lgsz)		\
+      BUILT_IN_ATOMIC_LOAD_ ## N:			\
+	return "Ap" "R" lgsz;				\
+    case BUILT_IN_SYNC_FETCH_AND_ADD_ ## N:		\
+    case BUILT_IN_SYNC_FETCH_AND_SUB_ ## N:		\
+    case BUILT_IN_SYNC_FETCH_AND_OR_ ## N:		\
+    case BUILT_IN_SYNC_FETCH_AND_AND_ ## N:		\
+    case BUILT_IN_SYNC_FETCH_AND_XOR_ ## N:		\
+    case BUILT_IN_SYNC_FETCH_AND_NAND_ ## N:		\
+    case BUILT_IN_SYNC_ADD_AND_FETCH_ ## N:		\
+    case BUILT_IN_SYNC_SUB_AND_FETCH_ ## N:		\
+    case BUILT_IN_SYNC_OR_AND_FETCH_ ## N:		\
+    case BUILT_IN_SYNC_AND_AND_FETCH_ ## N:		\
+    case BUILT_IN_SYNC_XOR_AND_FETCH_ ## N:		\
+    case BUILT_IN_SYNC_NAND_AND_FETCH_ ## N:		\
+    case BUILT_IN_SYNC_LOCK_TEST_AND_SET_ ## N:		\
+      return "Ap" "W" lgsz;				\
+    case BUILT_IN_SYNC_BOOL_COMPARE_AND_SWAP_ ## N:	\
+    case BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_ ## N:	\
+      return "Ap" "W" lgsz;				\
+    case BUILT_IN_SYNC_LOCK_RELEASE_ ## N:		\
+      return "Ap" "W" lgsz;				\
+    case BUILT_IN_ATOMIC_EXCHANGE_ ## N:		\
+      return "Ap" "W" lgsz;				\
+    case BUILT_IN_ATOMIC_COMPARE_EXCHANGE_ ## N:	\
+      return "Ap" "W" lgsz "W" lgsz;			\
+    case BUILT_IN_ATOMIC_STORE_ ## N:			\
+      return "Ap" "W" lgsz;				\
+    case BUILT_IN_ATOMIC_ADD_FETCH_ ## N:		\
+    case BUILT_IN_ATOMIC_SUB_FETCH_ ## N:		\
+    case BUILT_IN_ATOMIC_AND_FETCH_ ## N:		\
+    case BUILT_IN_ATOMIC_NAND_FETCH_ ## N:		\
+    case BUILT_IN_ATOMIC_XOR_FETCH_ ## N:		\
+    case BUILT_IN_ATOMIC_OR_FETCH_ ## N:		\
+    case BUILT_IN_ATOMIC_FETCH_ADD_ ## N:		\
+    case BUILT_IN_ATOMIC_FETCH_SUB_ ## N:		\
+    case BUILT_IN_ATOMIC_FETCH_AND_ ## N:		\
+    case BUILT_IN_ATOMIC_FETCH_NAND_ ## N:		\
+    case BUILT_IN_ATOMIC_FETCH_XOR_ ## N:		\
+    case BUILT_IN_ATOMIC_FETCH_OR_ ## N:		\
+      return "Ap" "W" lgsz				\
+
+    case BUILTIN_ACCESS_SIZE_FNSPEC (1, "0");
+      break;
+    case BUILTIN_ACCESS_SIZE_FNSPEC (2, "1");
+      break;
+    case BUILTIN_ACCESS_SIZE_FNSPEC (4, "2");
+      break;
+    case BUILTIN_ACCESS_SIZE_FNSPEC (8, "3");
+      break;
+    case BUILTIN_ACCESS_SIZE_FNSPEC (16, "4");
+      break;
+
+    default:
+      return "";
+    }
+}
diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index 00c3ea0f505..ffe45fef93c 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -51,6 +51,7 @@ 
 #include "attribs.h"
 #include "demangle.h"
 #include "pointer-query.h"
+#include "attr-fnspec.h"
 
 /* Return true if tree node X has an associated location.  */
 
@@ -2795,10 +2796,52 @@  pass_waccess::check_builtin (gcall *stmt)
       }
 	
     default:
-      return false;
+      break;
     }
 
-  return true;
+  /* Try to determine the function's properties from its fnspec.  */
+  attr_fnspec fnspec = gimple_call_fnspec (stmt);
+  if (!fnspec.known_p () && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
+    fnspec = atomic_builtin_fnspec (callee);
+
+  if (!fnspec.known_p ())
+    return false;
+
+  bool handled = false;
+  unsigned nargs = gimple_call_num_args (stmt);
+  for (unsigned i = 0; i != nargs; ++i)
+    {
+      if (!fnspec.arg_specified_p (i)
+         || !fnspec.arg_direct_p (i))
+       continue;
+
+      tree len = NULL_TREE;
+      unsigned ni;
+
+      if (fnspec.arg_access_size_constant_p (i, &ni))
+       len = build_int_cstu (sizetype, ni);
+      else if (fnspec.arg_access_size_given_by_type_p (i))
+       {
+         /* TODO: Determine access size from parameter type?  */
+       }
+
+      unsigned di;
+      if (len && fnspec.arg_copied_to_arg_p (i, &di))
+       {
+         tree dst = call_arg (stmt, di);
+         tree src = call_arg (stmt, i);
+         check_memop_access (stmt, dst, src, len);
+	 handled = true;
+       }
+
+      if (len && fnspec.arg_maybe_written_p (i))
+       {
+         tree dst = gimple_call_arg (stmt, i);
+	 check_memop_access (stmt, dst, NULL_TREE, len);
+	 handled = true;
+       }
+    }
+  return handled;
 }
 
 /* Returns the type of the argument ARGNO to function with type FNTYPE
diff --git a/gcc/testsuite/c-c++-common/gomp/atomic-4.c b/gcc/testsuite/c-c++-common/gomp/atomic-4.c
index 7f27370d535..5dd18d1d5fa 100644
--- a/gcc/testsuite/c-c++-common/gomp/atomic-4.c
+++ b/gcc/testsuite/c-c++-common/gomp/atomic-4.c
@@ -8,7 +8,7 @@  int *bar(void);
 void f1(void)
 {
   #pragma omp atomic
-    a[4] += 1;
+    a[3] += 1;
   #pragma omp atomic
     *p += 1;
   #pragma omp atomic
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-90.c b/gcc/testsuite/gcc.dg/Warray-bounds-90.c
new file mode 100644
index 00000000000..2e72a3daa1c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-90.c
@@ -0,0 +1,147 @@ 
+/* PR middle-end/102453 - buffer overflow by atomic built-ins not diagnosed
+   Verify that out-of-bounds accesses by atomic functions are diagnosed.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" }  */
+
+#ifndef __cplusplus
+#  define bool _Bool
+#endif
+
+#define load        __atomic_load
+#define store       __atomic_store
+#define add_fetch   __atomic_add_fetch
+#define sub_fetch   __atomic_sub_fetch
+#define and_fetch   __atomic_and_fetch
+#define or_fetch    __atomic_or_fetch
+#define xor_fetch   __atomic_xor_fetch
+#define nand_fetch  __atomic_nand_fetch
+
+typedef __SIZE_TYPE__ size_t;
+
+void sink (void*, ...);
+#define sink(...) sink (0, __VA_ARGS__)
+
+extern _Bool eb;
+extern char ec;
+extern short int esi;
+extern int ei;
+extern long int eli;
+extern long long int elli;
+
+extern const _Bool ecb;
+extern const char ecc;
+extern const short int ecsi;
+extern const int eci;
+extern const long int ecli;
+extern const long long int eclli;
+
+extern _Atomic _Bool eab;
+extern _Atomic char eac;
+extern _Atomic short int easi;
+extern _Atomic int eai;
+extern _Atomic long int eali;
+extern _Atomic long long int ealli;
+
+extern _Atomic const _Bool eacb;
+extern _Atomic const char eacc;
+extern _Atomic const short int eacsi;
+extern _Atomic const int eaci;
+extern _Atomic const long int eacli;
+extern _Atomic const long long int eaclli;
+
+
+void nowarn_atomic_load (void)
+{
+  load (&eacb, &eb, 0);
+  load (&eacc, &ec, 0);
+  load (&eacsi, &esi, 0);
+  load (&eaci, &ei, 0);
+  load (&eacli, &eli, 0);
+  load (&eaclli, &elli, 0);
+}
+
+
+void warn_atomic_load_note (void)
+{
+  int i;                            // { dg-message "'i'" }
+
+  int *pi = (int*)((char*)&i + 1);
+  load (&eaci, pi, 0);              // { dg-warning "-Warray-bounds" }
+  sink (&i);
+
+  pi = (int*)((char*)&i + 2);
+  load (&eaci, pi, 0);              // { dg-warning "-Warray-bounds" }
+  sink (&i);
+
+  pi = &i + 1;
+  load (&eaci, pi, 0);              // { dg-warning "-Warray-bounds" }
+  sink (&i);
+}
+
+
+void warn_atomic_load (void)
+{
+  bool *pb = &eb + 1;
+  load (&eacb, pb, 0);              // { dg-warning "-Warray-bounds" }
+
+  char *pc = &ec + 1;
+  load (&eacc, pc, 0);              // { dg-warning "-Warray-bounds" }
+
+  short *psi = (short*)((char*)&esi + 1);
+  load (&eacsi, psi, 0);            // { dg-warning "-Warray-bounds" }
+  psi = (short*)((char*)&esi + 2);
+  load (&eacsi, psi, 0);            // { dg-warning "-Warray-bounds" }
+
+  int *pi = (int*)((char*)&ei + 1);
+  load (&eaci, pi, 0);              // { dg-warning "-Warray-bounds" }
+  pi = (int*)((char*)&ei + 2);
+  load (&eaci, pi, 0);              // { dg-warning "-Warray-bounds" }
+  pi = (int*)((char*)&ei + sizeof ei);
+  load (&eaci, pi, 0);              // { dg-warning "-Warray-bounds" }
+
+  long *pli = (long*)((char*)&eli + 1);
+  load (&eacli, pli, 0);            // { dg-warning "-Warray-bounds" }
+  pli = (long*)((char*)&eli + 1);
+  load (&eacli, pli, 0);            // { dg-warning "-Warray-bounds" }
+  pli = &eli + 1;
+  load (&eacli, pli, 0);            // { dg-warning "-Warray-bounds" }
+
+  long long *plli = (long long*)((char*)&elli + 1);
+  load (&eaclli, plli, 0);          // { dg-warning "-Warray-bounds" }
+  plli = (long long*)((char*)&elli + 1);
+  load (&eacli, plli, 0);           // { dg-warning "-Warray-bounds" }
+  plli = &elli + 1;
+  load (&eaclli, plli, 0);          // { dg-warning "-Warray-bounds" }
+}
+
+
+void warn_atomic_store (void)
+{
+  const bool *pb = &eb + 1;
+  store (&eab, pb, 0);              // { dg-warning "-Warray-bounds" }
+
+  const char *pc = &ec + 1;
+  store (&eac, pc, 0);              // { dg-warning "-Warray-bounds" }
+
+  const short *psi = (const short*)((const char*)&ecsi + 1);
+  store (&easi, psi, 0);            // { dg-warning "-Warray-bounds" }
+  psi = (const short*)((const char*)&esi + 2);
+  store (&easi, psi, 0);            // { dg-warning "-Warray-bounds" }
+
+  const int *pi = (const int*)((const char*)&eci + 1);
+  store (&eai, pi, 0);              // { dg-warning "-Warray-bounds" }
+  pi = (const int*)((const char*)&ei + 2);
+  store (&eai, pi, 0);              // { dg-warning "-Warray-bounds" }
+  pi = (const int*)((const char*)&ei + sizeof ei);
+  store (&eai, pi, 0);              // { dg-warning "-Warray-bounds" }
+
+  const long *pli = (const long*)((const char*)&eli + 1);
+  store (&eali, pli, 0);            // { dg-warning "-Warray-bounds" }
+  pli = (const long*)((const char*)&eli + sizeof (eli));
+  store (&eali, pli, 0);            // { dg-warning "-Warray-bounds" }
+
+  const long long *plli = (const long long*)((const char*)&elli + 1);
+  store (&ealli, plli, 0);          // { dg-warning "-Warray-bounds" }
+  plli = (const long long*)((const char*)&elli + sizeof elli);
+  store (&ealli, plli, 0);          // { dg-warning "-Warray-bounds" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-77.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-77.c
new file mode 100644
index 00000000000..732f56849ae
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-77.c
@@ -0,0 +1,516 @@ 
+/* PR middle-end/102453 - buffer overflow by atomic built-ins not diagnosed
+   Verify that out-of-bounds accesses by atomic functions are diagnosed with
+   optimization disabled.
+   { dg-do compile }
+   { dg-options "-O0 -Wall -ftrack-macro-expansion=0" }  */
+
+#ifndef __cplusplus
+#  define bool _Bool
+#endif
+
+#define add_fetch(p, q)    __atomic_add_fetch (p, q, 0)
+#define sub_fetch(p, q)    __atomic_sub_fetch (p, q, 0)
+#define and_fetch(p, q)    __atomic_and_fetch (p, q, 0)
+#define or_fetch(p, q)     __atomic_or_fetch (p, q, 0)
+#define xor_fetch(p, q)    __atomic_xor_fetch (p, q, 0)
+#define nand_fetch(p, q)   __atomic_nand_fetch (p, q, 0)
+#define exchange(p, q, r)  __atomic_exchange (p, q, r, 0)
+#define exchange_n(p, n)   __atomic_exchange_n (p, n, 0)
+#define cmpxchg(p, q, r)   __atomic_compare_exchange (p, q, r, 0, 0, 0)
+
+typedef __SIZE_TYPE__ size_t;
+
+void sink (void*, ...);
+#define sink(...) sink (0, __VA_ARGS__)
+
+extern _Bool eb;
+extern char ec;
+extern short int esi;
+extern int ei;
+extern long int eli;
+extern long long int elli;
+
+extern const _Bool ecb;
+extern const char ecc;
+extern const short int ecsi;
+extern const int eci;
+extern const long int ecli;
+extern const long long int eclli;
+
+extern _Atomic _Bool eab;
+extern _Atomic char eac;
+extern _Atomic short int easi;
+extern _Atomic int eai;
+extern _Atomic long int eali;
+extern _Atomic long long int ealli;
+
+extern _Atomic const _Bool eacb;
+extern _Atomic const char eacc;
+extern _Atomic const short int eacsi;
+extern _Atomic const int eaci;
+extern _Atomic const long int eacli;
+extern _Atomic const long long int eaclli;
+
+
+void nowarn_atomic_add_fetch (void)
+{
+  add_fetch (&eac, ecc);
+  add_fetch (&easi, esi);
+  add_fetch (&eai, ei);
+  add_fetch (&eali, eli);
+  add_fetch (&ealli, elli);
+}
+
+
+void warn_atomic_add_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  add_fetch (pc, ecc);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  add_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  add_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  add_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  add_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  add_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  add_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  add_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  add_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  add_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  add_fetch (plli, eali);               // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  add_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void nowarn_atomic_sub_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  sub_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  sub_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  sub_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  sub_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  sub_fetch (plli, elli);
+}
+
+
+void warn_atomic_sub_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  sub_fetch (pc, ecc);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  sub_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  sub_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  sub_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  sub_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  sub_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  sub_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  sub_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  sub_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  sub_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  sub_fetch (plli, eali);               // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  sub_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void nowarn_atomic_and_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  and_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  and_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  and_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  and_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  and_fetch (plli, elli);
+}
+
+
+void warn_atomic_and_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  and_fetch (pc, ecc);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  and_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  and_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  and_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  and_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  and_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  and_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  and_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  and_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  and_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  and_fetch (plli, eali);               // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  and_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void nowarn_atomic_or_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  or_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  or_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  or_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  or_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  or_fetch (plli, elli);
+}
+
+
+void warn_atomic_or_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  or_fetch (pc, ecc);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  or_fetch (psi, esi);                  // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  or_fetch (psi, esi);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  or_fetch (pi, ei);                    // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  or_fetch (pi, ei);                    // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  or_fetch (pi, ei);                    // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  or_fetch (pli, eli);                  // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  or_fetch (pli, eli);                  // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  or_fetch (pli, eli);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  or_fetch (plli, elli);                // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  or_fetch (plli, eali);                // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  or_fetch (plli, elli);                // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void nowarn_atomic_xor_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  xor_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  xor_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  xor_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  xor_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  xor_fetch (plli, elli);
+}
+
+
+void warn_atomic_xor_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  xor_fetch (pc, ecc);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  xor_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 1);
+  xor_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  xor_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  xor_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  xor_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  xor_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  xor_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  xor_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  xor_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&eali + 1);
+  xor_fetch (plli, eali);               // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  xor_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void nowarn_atomic_nand_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  nand_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  nand_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  nand_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  nand_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  nand_fetch (plli, elli);
+}
+
+
+void warn_atomic_nand_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  nand_fetch (pc, ecc);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  nand_fetch (psi, esi);                // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 1);
+  nand_fetch (psi, esi);                // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  nand_fetch (pi, ei);                  // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  nand_fetch (pi, ei);                  // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  nand_fetch (pi, ei);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  nand_fetch (pli, eli);                // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  nand_fetch (pli, eli);                // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  nand_fetch (pli, eli);                // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  nand_fetch (plli, elli);              // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&eai + 1);
+  nand_fetch (plli, eali);              // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  nand_fetch (plli, elli);              // { dg-warning "-Wstringop-overflow" }
+}
+
+
+void nowarn_atomic_exchange (void)
+{
+  char rc;
+  _Atomic char *pc = &eac;
+  exchange (pc, &ecc, &rc);
+
+  short rsi;
+  _Atomic short *psi = &easi;
+  exchange (psi, &esi, &rsi);
+
+  int ri;
+  _Atomic int *pi = &eai;
+  exchange (pi, &ei, &ri);
+
+  long rli;
+  _Atomic long *pli = &eali;
+  exchange (pli, &eli, &rli);
+
+  long long rlli;
+  _Atomic long long *plli = &ealli;
+  exchange (plli, &elli, &rlli);
+
+  sink (&rc, &rsi, &ri, &rli, &rlli);
+}
+
+void warn_atomic_exchange (void)
+{
+  char rc;
+  _Atomic char *pc = &eac + 1;
+  exchange (pc, &ecc, &rc);             // { dg-warning "-Wstringop-overflow" }
+
+  short rsi[2];
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  exchange (psi, &ecsi, rsi);           // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  exchange (psi, &ecsi, rsi + 1);       // { dg-warning "-Wstringop-overflow" }
+
+  int ri[3];
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  exchange (pi, &eci, ri);              // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  exchange (pi, &eci, ri + 1);          // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  exchange (pi, &eci, ri + 2);          // { dg-warning "-Wstringop-overflow" }
+
+  long rli[3];
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  exchange (pli, &ecli, rli);           // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  exchange (pli, &ecli, rli + 1);       // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  exchange (pli, &ecli, rli + 2);       // { dg-warning "-Wstringop-overflow" }
+
+  long long rlli[3];
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  exchange (plli, &eclli, rlli);        // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  exchange (plli, &eclli, rlli + 1);    // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  exchange (plli, &eclli, rlli + 2);    // { dg-warning "-Wstringop-overflow" }
+
+  sink (&rc, rsi, ri, rli, rlli);
+}
+
+
+void nowarn_atomic_exchange_n (_Atomic unsigned char *pauc,
+			       _Atomic unsigned short *pausi,
+			       _Atomic unsigned int *paui,
+			       _Atomic unsigned long *pauli,
+			       _Atomic unsigned long long *paulli)
+{
+  char rc = exchange_n (&eac, ecc);
+  short rsi = exchange_n (&easi, esi);
+  int ri = exchange_n (&eai, ei);
+  long rli = exchange_n (&eali, eli);
+  long long rlli = exchange_n (&ealli, elli);
+
+  sink (rc, rsi, ri, rli, rlli);
+
+  char ruc = exchange_n (pauc, ecc);
+  short rusi = exchange_n (pausi, esi);
+  int rui = exchange_n (paui, ei);
+  long ruli = exchange_n (pauli, eli);
+  long long rulli = exchange_n (paulli, elli);
+
+  sink (ruc, rusi, rui, ruli, rulli);
+}
+
+
+void warn_atomic_exchange_n (void)
+{
+  _Atomic char *pc = &eac + 1;
+  char rc = exchange_n (pc, ecc);       // { dg-warning "-Wstringop-overflow" }
+
+  short rsi[2];
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  rsi[0] = exchange_n (psi, ecsi);      // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  rsi[1] = exchange_n (psi, ecsi);      // { dg-warning "-Wstringop-overflow" }
+
+  int ri[3];
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  ri[0] = exchange_n (pi, eci);         // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  ri[1] = exchange_n (pi, eci);         // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  ri[2] = exchange_n (pi, eci);         // { dg-warning "-Wstringop-overflow" }
+
+  long rli[3];
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  rli[0] = exchange_n (pli, ecli);      // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  rli[1] = exchange_n (pli, ecli);      // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  rli[2] = exchange_n (pli, ecli);      // { dg-warning "-Wstringop-overflow" }
+
+  long long rlli[3];
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  rlli[0] = exchange_n (plli, eclli);   // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  rlli[1] = exchange_n (plli, eclli);   // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  rlli[2] = exchange_n (plli, eclli);   // { dg-warning "-Wstringop-overflow" }
+
+  sink (&rc, rsi, ri, rli, rlli);
+}
+
+
+void warn_atomic_compare_exchange (void)
+{
+  _Atomic char *pc = &eac + 1;
+  cmpxchg (pc, &ec, &ecc);              // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  cmpxchg (psi, &esi, &ecsi);           // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  cmpxchg (psi, &esi, &ecsi);           // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  cmpxchg (pi, &ei, &eci);              // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  cmpxchg (pi, &ei, &eci);              // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  cmpxchg (pi, &ei, &eci);              // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  cmpxchg (pli, &eli, &ecli);           // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  cmpxchg (pli, &eli, &ecli);           // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  cmpxchg (pli, &eli, &ecli);           // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  cmpxchg (plli, &elli, &eclli);        // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  cmpxchg (plli, &elli, &eclli);        // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  cmpxchg (plli, &elli, &eclli);        // { dg-warning "-Wstringop-overflow" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-78.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-78.c
new file mode 100644
index 00000000000..a25a418ed76
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-78.c
@@ -0,0 +1,518 @@ 
+/* PR middle-end/102453 - buffer overflow by atomic built-ins not diagnosed
+   Verify that out-of-bounds accesses by atomic functions are diagnosed with
+   optimization enabled.
+   { dg-do compile }
+   { dg-options "-O3 -Wall -ftrack-macro-expansion=0" }  */
+
+#ifndef __cplusplus
+#  define bool _Bool
+#endif
+
+#define NOIPA __attribute__ ((noipa))
+
+#define add_fetch(p, q)   __atomic_add_fetch (p, q, 0)
+#define sub_fetch(p, q)   __atomic_sub_fetch (p, q, 0)
+#define and_fetch(p, q)   __atomic_and_fetch (p, q, 0)
+#define or_fetch(p, q)    __atomic_or_fetch (p, q, 0)
+#define xor_fetch(p, q)   __atomic_xor_fetch (p, q, 0)
+#define nand_fetch(p, q)  __atomic_nand_fetch (p, q, 0)
+#define exchange(p, q, r) __atomic_exchange (p, q, r, 0)
+#define exchange_n(p, n)  __atomic_exchange_n (p, n, 0)
+#define cmpxchg(p, q, r)  __atomic_compare_exchange (p, q, r, __COUNTER__, 0, 0)
+
+typedef __SIZE_TYPE__ size_t;
+
+void sink (void*, ...);
+#define sink(...) sink (0, __VA_ARGS__)
+
+extern _Bool eb;
+extern char ec;
+extern short int esi;
+extern int ei;
+extern long int eli;
+extern long long int elli;
+
+extern const _Bool ecb;
+extern const char ecc;
+extern const short int ecsi;
+extern const int eci;
+extern const long int ecli;
+extern const long long int eclli;
+
+extern _Atomic _Bool eab;
+extern _Atomic char eac;
+extern _Atomic short int easi;
+extern _Atomic int eai;
+extern _Atomic long int eali;
+extern _Atomic long long int ealli;
+
+extern _Atomic const _Bool eacb;
+extern _Atomic const char eacc;
+extern _Atomic const short int eacsi;
+extern _Atomic const int eaci;
+extern _Atomic const long int eacli;
+extern _Atomic const long long int eaclli;
+
+
+NOIPA void nowarn_atomic_add_fetch (void)
+{
+  add_fetch (&eac, ecc);
+  add_fetch (&easi, esi);
+  add_fetch (&eai, ei);
+  add_fetch (&eali, eli);
+  add_fetch (&ealli, elli);
+}
+
+
+NOIPA void warn_atomic_add_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  add_fetch (pc, ecc);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  add_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  add_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  add_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  add_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  add_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  add_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  add_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  add_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  add_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  add_fetch (plli, eali);               // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  add_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void nowarn_atomic_sub_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  sub_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  sub_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  sub_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  sub_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  sub_fetch (plli, elli);
+}
+
+
+NOIPA void warn_atomic_sub_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  sub_fetch (pc, ecc);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  sub_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  sub_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  sub_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  sub_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  sub_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  sub_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  sub_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  sub_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  sub_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  sub_fetch (plli, eali);               // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  sub_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void nowarn_atomic_and_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  and_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  and_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  and_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  and_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  and_fetch (plli, elli);
+}
+
+
+NOIPA void warn_atomic_and_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  and_fetch (pc, ecc);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  and_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  and_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  and_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  and_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  and_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  and_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  and_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  and_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  and_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  and_fetch (plli, eali);               // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  and_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void nowarn_atomic_or_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  or_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  or_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  or_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  or_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  or_fetch (plli, elli);
+}
+
+
+NOIPA void warn_atomic_or_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  or_fetch (pc, ecc);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  or_fetch (psi, esi);                  // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  or_fetch (psi, esi);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  or_fetch (pi, ei);                    // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  or_fetch (pi, ei);                    // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  or_fetch (pi, ei);                    // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  or_fetch (pli, eli);                  // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  or_fetch (pli, eli);                  // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  or_fetch (pli, eli);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  or_fetch (plli, elli);                // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  or_fetch (plli, eali);                // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  or_fetch (plli, elli);                // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void nowarn_atomic_xor_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  xor_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  xor_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  xor_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  xor_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  xor_fetch (plli, elli);
+}
+
+
+NOIPA void warn_atomic_xor_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  xor_fetch (pc, ecc);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  xor_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 1);
+  xor_fetch (psi, esi);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  xor_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  xor_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  xor_fetch (pi, ei);                   // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  xor_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  xor_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  xor_fetch (pli, eli);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  xor_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&eali + 1);
+  xor_fetch (plli, eali);               // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  xor_fetch (plli, elli);               // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void nowarn_atomic_nand_fetch (void)
+{
+  _Atomic char *pc = &eac;
+  nand_fetch (pc, ecc);
+
+  _Atomic short *psi = &easi;
+  nand_fetch (psi, esi);
+
+  _Atomic int *pi = &eai;
+  nand_fetch (pi, ei);
+
+  _Atomic long *pli = &eali;
+  nand_fetch (pli, eli);
+
+  _Atomic long long *plli = &ealli;
+  nand_fetch (plli, elli);
+}
+
+
+NOIPA void warn_atomic_nand_fetch (void)
+{
+  _Atomic char *pc = &eac + 1;
+  nand_fetch (pc, ecc);                 // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  nand_fetch (psi, esi);                // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 1);
+  nand_fetch (psi, esi);                // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  nand_fetch (pi, ei);                  // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  nand_fetch (pi, ei);                  // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  nand_fetch (pi, ei);                  // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  nand_fetch (pli, eli);                // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  nand_fetch (pli, eli);                // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  nand_fetch (pli, eli);                // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  nand_fetch (plli, elli);              // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&eai + 1);
+  nand_fetch (plli, eali);              // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  nand_fetch (plli, elli);              // { dg-warning "-Wstringop-overflow" }
+}
+
+
+NOIPA void nowarn_atomic_exchange (void)
+{
+  char rc;
+  _Atomic char *pc = &eac;
+  exchange (pc, &ecc, &rc);
+
+  short rsi;
+  _Atomic short *psi = &easi;
+  exchange (psi, &esi, &rsi);
+
+  int ri;
+  _Atomic int *pi = &eai;
+  exchange (pi, &ei, &ri);
+
+  long rli;
+  _Atomic long *pli = &eali;
+  exchange (pli, &eli, &rli);
+
+  long long rlli;
+  _Atomic long long *plli = &ealli;
+  exchange (plli, &elli, &rlli);
+
+  sink (&rc, &rsi, &ri, &rli, &rlli);
+}
+
+NOIPA void warn_atomic_exchange (void)
+{
+  char rc;
+  _Atomic char *pc = &eac + 1;
+  exchange (pc, &ecc, &rc);             // { dg-warning "-Wstringop-overflow" }
+
+  short rsi[2];
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  exchange (psi, &ecsi, rsi);           // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  exchange (psi, &ecsi, rsi + 1);       // { dg-warning "-Wstringop-overflow" }
+
+  int ri[3];
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  exchange (pi, &eci, ri);              // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  exchange (pi, &eci, ri + 1);          // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  exchange (pi, &eci, ri + 2);          // { dg-warning "-Wstringop-overflow" }
+
+  long rli[3];
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  exchange (pli, &ecli, rli);           // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  exchange (pli, &ecli, rli + 1);       // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  exchange (pli, &ecli, rli + 2);       // { dg-warning "-Wstringop-overflow" }
+
+  long long rlli[3];
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  exchange (plli, &eclli, rlli);        // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  exchange (plli, &eclli, rlli + 1);    // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  exchange (plli, &eclli, rlli + 2);    // { dg-warning "-Wstringop-overflow" }
+
+  sink (&rc, rsi, ri, rli, rlli);
+}
+
+
+NOIPA void nowarn_atomic_exchange_n (_Atomic unsigned char *pauc,
+			       _Atomic unsigned short *pausi,
+			       _Atomic unsigned int *paui,
+			       _Atomic unsigned long *pauli,
+			       _Atomic unsigned long long *paulli)
+{
+  char rc = exchange_n (&eac, ecc);
+  short rsi = exchange_n (&easi, esi);
+  int ri = exchange_n (&eai, ei);
+  long rli = exchange_n (&eali, eli);
+  long long rlli = exchange_n (&ealli, elli);
+
+  sink (rc, rsi, ri, rli, rlli);
+
+  char ruc = exchange_n (pauc, ecc);
+  short rusi = exchange_n (pausi, esi);
+  int rui = exchange_n (paui, ei);
+  long ruli = exchange_n (pauli, eli);
+  long long rulli = exchange_n (paulli, elli);
+
+  sink (ruc, rusi, rui, ruli, rulli);
+}
+
+
+NOIPA void warn_atomic_exchange_n (void)
+{
+  _Atomic char *pc = &eac + 1;
+  char rc = exchange_n (pc, ecc);       // { dg-warning "-Wstringop-overflow" }
+
+  short rsi[2];
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  rsi[0] = exchange_n (psi, ecsi);      // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  rsi[1] = exchange_n (psi, ecsi);      // { dg-warning "-Wstringop-overflow" }
+
+  int ri[3];
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  ri[0] = exchange_n (pi, eci);         // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  ri[1] = exchange_n (pi, eci);         // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  ri[2] = exchange_n (pi, eci);         // { dg-warning "-Wstringop-overflow" }
+
+  long rli[3];
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  rli[0] = exchange_n (pli, ecli);      // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  rli[1] = exchange_n (pli, ecli);      // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  rli[2] = exchange_n (pli, ecli);      // { dg-warning "-Wstringop-overflow" }
+
+  long long rlli[3];
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  rlli[0] = exchange_n (plli, eclli);   // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  rlli[1] = exchange_n (plli, eclli);   // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  rlli[2] = exchange_n (plli, eclli);   // { dg-warning "-Wstringop-overflow" }
+
+  sink (&rc, rsi, ri, rli, rlli);
+}
+
+
+NOIPA void warn_atomic_compare_exchange (void)
+{
+  _Atomic char *pc = &eac + 1;
+  cmpxchg (pc, &ec, &ecc);              // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic short *psi = (_Atomic short*)((char*)&easi + 1);
+  cmpxchg (psi, &esi, &ecsi);           // { dg-warning "-Wstringop-overflow" }
+  psi = (_Atomic short*)((char*)&easi + 2);
+  cmpxchg (psi, &esi, &ecsi);           // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic int *pi = (_Atomic int*)((char*)&eai + 1);
+  cmpxchg (pi, &ei, &eci);              // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + 2);
+  cmpxchg (pi, &ei, &eci);              // { dg-warning "-Wstringop-overflow" }
+  pi = (_Atomic int*)((char*)&eai + sizeof eai);
+  cmpxchg (pi, &ei, &eci);              // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long *pli = (_Atomic long*)((char*)&eali + 1);
+  cmpxchg (pli, &eli, &ecli);           // { dg-warning "-Wstringop-overflow" }
+  pli = (_Atomic long*)((char*)&eali + 1);
+  cmpxchg (pli, &eli, &ecli);           // { dg-warning "-Wstringop-overflow" }
+  pli = &eali + 1;
+  cmpxchg (pli, &eli, &ecli);           // { dg-warning "-Wstringop-overflow" }
+
+  _Atomic long long *plli = (_Atomic long long*)((char*)&ealli + 1);
+  cmpxchg (plli, &elli, &eclli);        // { dg-warning "-Wstringop-overflow" }
+  plli = (_Atomic long long*)((char*)&ealli + 1);
+  cmpxchg (plli, &elli, &eclli);        // { dg-warning "-Wstringop-overflow" }
+  plli = &ealli + 1;
+  cmpxchg (plli, &elli, &eclli);        // { dg-warning "-Wstringop-overflow" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-79.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-79.c
new file mode 100644
index 00000000000..15eb26fbdb7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-79.c
@@ -0,0 +1,70 @@ 
+/* Verify that a separate note is issued for each offset into the same
+   object after a -Wstringop-overflow.  Since all arguments are known
+   the test doesn't need optimization.  Wstringop-overflow-79.c verifies
+   they're also issued at -O2.
+   { dg-do compile }
+   { dg-options "-O0 -Wno-array-bounds" } */
+
+extern char a[8];                 // dg-message at offset \\\[3, 6] into destination object 'a'" "note 1" }
+                                  // dg-message at offset \\\[5, 8] into destination object 'a'" "note 2" { target *-*-* } .-1 }
+
+void test_2_notes (int i)
+{
+  char *p = i ? a + 3 : a + 5;
+  __builtin_memset (p, 0, 7);     // { dg-warning "-Wstringop-overflow" }
+}
+
+
+extern char b[8];                 // dg-message at offset \\\[3, 6] into destination object 'b'" "note 1" }
+                                  // dg-message at offset \\\[4, 7] into destination object 'b'" "note 2" { target *-*-* } .-1 }
+                                  // dg-message at offset \\\[5, 8] into destination object 'b'" "note 3" { target *-*-* } .-2 }
+
+void test_3_notes (int i)
+{
+  char *p = i < 0 ? b + 3 : 0 < i ? b + 5 : b + 4;
+  __builtin_memset (p, 0, 7);     // { dg-warning "-Wstringop-overflow" }
+}
+
+
+extern char c[8];                 // dg-message at offset \\\[3, 6] into destination object 'c'" "note 1" }
+                                  // dg-message at offset \\\[4, 7] into destination object 'c'" "note 2" { target *-*-* } .-1 }
+                                  // dg-message at offset \\\[5, 8] into destination object 'c'" "note 3" { target *-*-* } .-2 }
+                                  // dg-message at offset \\\[6, 8] into destination object 'c'" "note 3" { target *-*-* } .-2 }
+
+void test_4_notes (int i)
+{
+  char *p;
+  if (i < -1)
+    p = c + 3;
+  else if (i < 0)
+    p = c + 4;
+  else if (0 < i)
+    p = c + 6;
+  else
+    p = c + 5;
+
+  __builtin_memset (p, 0, 7);     // { dg-warning "-Wstringop-overflow" }
+}
+
+
+extern char d[8];                 // dg-message at offset \\\[3, 6] into destination object 'd'" "note 1" }
+                                  // dg-message at offset \\\[4, 7] into destination object 'd'" "note 2" { target *-*-* } .-1 }
+                                  // dg-message at offset \\\[5, 8] into destination object 'd'" "note 3" { target *-*-* } .-2 }
+                                  // dg-message at offset \\\[6, 8] into destination object 'd'" "note 3" { target *-*-* } .-3 }
+                                  // dg-message at offset \\\[7, 8] into destination object 'd'" "note 3" { target *-*-* } .-4 }
+
+void test_5_notes (int i)
+{
+  char *p;
+  switch (i)
+    {
+    case -9: p = d + 3; break;
+    case -5: p = d + 4; break;
+    case  0: p = d + 5; break;
+    case  3: p = d + 6; break;
+    case  4: p = d + 7; break;
+    default: return;
+    }
+
+  __builtin_memset (p, 0, 7);     // { dg-warning "-Wstringop-overflow" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-80.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-80.c
new file mode 100644
index 00000000000..1628c2f0159
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-80.c
@@ -0,0 +1,70 @@ 
+/* Verify that a separate note is issued for each offset into the same
+   object after a -Wstringop-overflow.  Even though the warnings don't
+   need optimization the test enables it to verify they're still issued
+   with it.  Wstringop-overflow-78.c verifies they're issued at -O0.
+   { dg-do compile }
+   { dg-options "-O2 -Wno-array-bounds" } */
+
+extern char a[8];                 // dg-message at offset \\\[3, 6] into destination object 'a'" "note 1" }
+                                  // dg-message at offset \\\[5, 8] into destination object 'a'" "note 2" { target *-*-* } .-1 }
+
+void test_2_notes (int i)
+{
+  char *p = i ? a + 3 : a + 5;
+  __builtin_memset (p, 0, 7);     // { dg-warning "-Wstringop-overflow" }
+}
+
+
+extern char b[8];                 // dg-message at offset \\\[3, 6] into destination object 'b'" "note 1" }
+                                  // dg-message at offset \\\[4, 7] into destination object 'b'" "note 2" { target *-*-* } .-1 }
+                                  // dg-message at offset \\\[5, 8] into destination object 'b'" "note 3" { target *-*-* } .-2 }
+
+void test_3_notes (int i)
+{
+  char *p = i < 0 ? b + 3 : 0 < i ? b + 5 : b + 4;
+  __builtin_memset (p, 0, 7);     // { dg-warning "-Wstringop-overflow" }
+}
+
+
+extern char c[8];                 // dg-message at offset \\\[3, 6] into destination object 'c'" "note 1" }
+                                  // dg-message at offset \\\[4, 7] into destination object 'c'" "note 2" { target *-*-* } .-1 }
+                                  // dg-message at offset \\\[5, 8] into destination object 'c'" "note 3" { target *-*-* } .-2 }
+                                  // dg-message at offset \\\[6, 8] into destination object 'c'" "note 3" { target *-*-* } .-2 }
+
+void test_4_notes (int i)
+{
+  char *p;
+  if (i < -1)
+    p = c + 3;
+  else if (i < 0)
+    p = c + 4;
+  else if (0 < i)
+    p = c + 6;
+  else
+    p = c + 5;
+
+  __builtin_memset (p, 0, 7);     // { dg-warning "-Wstringop-overflow" }
+}
+
+
+extern char d[8];                 // dg-message at offset \\\[3, 6] into destination object 'd'" "note 1" }
+                                  // dg-message at offset \\\[4, 7] into destination object 'd'" "note 2" { target *-*-* } .-1 }
+                                  // dg-message at offset \\\[5, 8] into destination object 'd'" "note 3" { target *-*-* } .-2 }
+                                  // dg-message at offset \\\[6, 8] into destination object 'd'" "note 3" { target *-*-* } .-3 }
+                                  // dg-message at offset \\\[7, 8] into destination object 'd'" "note 3" { target *-*-* } .-4 }
+
+void test_5_notes (int i)
+{
+  char *p;
+  switch (i)
+    {
+    case -9: p = d + 3; break;
+    case -5: p = d + 4; break;
+    case  0: p = d + 5; break;
+    case  3: p = d + 6; break;
+    case  4: p = d + 7; break;
+    default: return;
+    }
+
+  __builtin_memset (p, 0, 7);     // { dg-warning "-Wstringop-overflow" }
+}
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index ce667ff32b9..2d143f23354 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -3850,13 +3850,16 @@  attr_fnspec::verify ()
   if (!len)
     return;
 
+  const bool atomic_func = atomic_p ();
+
   /* Check return value specifier.  */
   if (len < return_desc_size)
     err = true;
   else if ((len - return_desc_size) % arg_desc_size)
     err = true;
   else if ((str[0] < '1' || str[0] > '4')
-	   && str[0] != '.' && str[0] != 'm')
+	   && str[0] != '.' && str[0] != 'm'
+	   && !atomic_func)
     err = true;
 
   switch (str[1])
@@ -3873,6 +3876,10 @@  attr_fnspec::verify ()
   if (err)
     internal_error ("invalid fn spec attribute \"%s\"", str);
 
+  /* Either the minimum access size expressed as (1 << N) or the one-based
+     argument number that specifies the access size.  */
+  const char mindig = atomic_func ? '0' : '1';
+
   /* Now check all parameters.  */
   for (unsigned int i = 0; arg_specified_p (i); i++)
     {
@@ -3888,14 +3895,16 @@  attr_fnspec::verify ()
 	  case 'w':
 	  case 'W':
 	  case '.':
-	    if ((str[idx + 1] >= '1' && str[idx + 1] <= '9')
+	    if ((str[idx + 1] >= mindig && str[idx + 1] <= '9')
 		|| str[idx + 1] == 't')
 	      {
 		if (str[idx] != 'r' && str[idx] != 'R'
 		    && str[idx] != 'w' && str[idx] != 'W'
 		    && str[idx] != 'o' && str[idx] != 'O')
 		  err = true;
-		if (str[idx + 1] != 't'
+		if (!atomic_func
+		    /* Size is not constant and hardwired into attribute.  */
+		    && str[idx + 1] != 't'
 		    /* Size specified is scalar, so it should be described
 		       by ". " if specified at all.  */
 		    && (arg_specified_p (str[idx + 1] - '1')
@@ -3906,7 +3915,7 @@  attr_fnspec::verify ()
 	      err = true;
 	    break;
 	  default:
-	    if (str[idx] < '1' || str[idx] > '9')
+	    if (str[idx] < mindig || str[idx] > '9')
 	      err = true;
 	}
       if (err)