Bug 109446 - Possible destination array overflow without diagnosis in memcpy
Summary: Possible destination array overflow without diagnosis in memcpy
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: sanitizer (show other bugs)
Version: 8.4.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-04-07 15:47 UTC by Mohamed
Modified: 2023-04-12 11:16 UTC (History)
8 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail: 12.2.0, 13.0
Last reconfirmed: 2023-04-11 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Mohamed 2023-04-07 15:47:49 UTC
Possible overflow of destination array using std::memcpy, the behavior doesn't trigger any diagnostic by the sanitizer in scenario I, while in scenario II the behavior triggers the sanitizer diagnosis. 

In the test the overflow is about 40 bytes, by overflow 24 bytes array with 64 bytes src string literals. 

I've also tried to use alignas(64) to align class bar on 64 bytes i.e. 

class alignas(64) Bar
{
 ...
};


But it didn't trigger the sanitizer diagnosis.



#include <iostream>
#include <array>
#include <cstring>

const char txt[] = "123456789-123456789-123456789-123456789-123456789-123456789-123";


class Bar
{
 public:
  constexpr Bar() : m1{}, m2{}, m3{}, m4{}, dst{} {}
  std::uint16_t m1;
  std::uint16_t m2;
  std::uint16_t m3;
  std::uint16_t m4;

  std::int8_t dst[24];
};

void test(Bar& b) // scenario II
//void test(Bar& b) // scenario II
{
	std::cout << "staring memcpy.\n";

	std::cout << "size of bytes to be copied: " << sizeof(txt) <<"\n";
	std::cout << "dst array size: " << sizeof(b.dst) << "\n";
	std::cout << "overflow size: " << sizeof(txt) - sizeof(b.dst) << "\n";
	std::memcpy(b.dst, txt, sizeof(txt));
}


class  client
{
public:
	void func()
	{
		test(b);
	}

private:
	Bar b{};
};

//g++-8 -o vtest vmain.cpp -std=c++14 -fsanitize=address -fsanitize=undefined -static-libasan -static-libubsan
int main()
{
	client c{};
	c.func();

	std::cout << "size of Bar: " << sizeof(Bar) << "\n";
	std::cout << "align of Bar: " << alignof(Bar) << "\n";
    return 0;
}
Comment 1 Mohamed 2023-04-11 11:02:08 UTC
correction to scenario II should pass by value as follows
//void test(Bar b) // scenario II
Comment 2 Xi Ruoyao 2023-04-11 13:33:52 UTC
Should we disable __builtin_memcpy etc. with -fsanitize=address?
Comment 3 Martin Liška 2023-04-12 08:05:23 UTC
The problem here is that we normally preserve memcpy calls and then __interceptor_memcpy is used from the run-time library. However, in this case the second argument of memcpy is a known constant and we convert it to:
  MEM <unsigned char[64]> [(char * {ref-all})_7] = MEM <unsigned char[64]> [(char * {ref-all})&txt];

for such an assignment we only check the beginning and the end of the chunk and we miss the overflow.
Comment 4 Xi Ruoyao 2023-04-12 08:08:50 UTC
(In reply to Martin Liška from comment #3)
> The problem here is that we normally preserve memcpy calls and then
> __interceptor_memcpy is used from the run-time library. However, in this
> case the second argument of memcpy is a known constant and we convert it to:
>   MEM <unsigned char[64]> [(char * {ref-all})_7] = MEM <unsigned char[64]>
> [(char * {ref-all})&txt];
> 
> for such an assignment we only check the beginning and the end of the chunk
> and we miss the overflow.

It seems Clang disables this optimization and convert memcpy to __asan_memcpy calls if -fsanitize=address used:

https://godbolt.org/z/dcfadoMYY
Comment 5 Martin Liška 2023-04-12 08:10:10 UTC
> It seems Clang disables this optimization and convert memcpy to
> __asan_memcpy calls if -fsanitize=address used:
> 
> https://godbolt.org/z/dcfadoMYY

Yep! Richi, do you know a place where we lower this?
Comment 6 Richard Biener 2023-04-12 08:46:18 UTC
(In reply to Martin Liška from comment #5)
> > It seems Clang disables this optimization and convert memcpy to
> > __asan_memcpy calls if -fsanitize=address used:
> > 
> > https://godbolt.org/z/dcfadoMYY
> 
> Yep! Richi, do you know a place where we lower this?

gimple_fold_builtin_memory_op

not sure if we should prevent all of those transforms.  But the question is
why ASAN doesn't instrument the generated aggregate copy?  Maybe because
in C/C++ you cannot write an aggregate array copy?
Comment 7 Jakub Jelinek 2023-04-12 08:52:06 UTC
(In reply to Richard Biener from comment #6)
> not sure if we should prevent all of those transforms.  But the question is
> why ASAN doesn't instrument the generated aggregate copy?  Maybe because
> in C/C++ you cannot write an aggregate array copy?

We do instrument those.  But only instrument them by checking the first and last byte
of the copy, not all bytes in between (because that would be for inline checking too large - we'd need to emit inline a loop over those bytes).
Comment 8 Richard Biener 2023-04-12 08:59:16 UTC
(In reply to Jakub Jelinek from comment #7)
> (In reply to Richard Biener from comment #6)
> > not sure if we should prevent all of those transforms.  But the question is
> > why ASAN doesn't instrument the generated aggregate copy?  Maybe because
> > in C/C++ you cannot write an aggregate array copy?
> 
> We do instrument those.  But only instrument them by checking the first and
> last byte
> of the copy, not all bytes in between (because that would be for inline
> checking too large - we'd need to emit inline a loop over those bytes).

OK, that's lack of an appropriate API then?  But still the first and last
byte should be sufficient to detect the problem (but maybe I'm missing
something here).
Comment 9 Martin Liška 2023-04-12 09:01:02 UTC
(In reply to Richard Biener from comment #8)
> (In reply to Jakub Jelinek from comment #7)
> > (In reply to Richard Biener from comment #6)
> > > not sure if we should prevent all of those transforms.  But the question is
> > > why ASAN doesn't instrument the generated aggregate copy?  Maybe because
> > > in C/C++ you cannot write an aggregate array copy?
> > 
> > We do instrument those.  But only instrument them by checking the first and
> > last byte
> > of the copy, not all bytes in between (because that would be for inline
> > checking too large - we'd need to emit inline a loop over those bytes).
> 
> OK, that's lack of an appropriate API then?  But still the first and last
> byte should be sufficient to detect the problem (but maybe I'm missing
> something here).

No, because the last byte is out of redzone:

=>0x7ffff5300000: f1 f1 f1 f1 00 00 00[f3]f3 f3 f3 f3 00 00 00 00

the 'f3' redzone is covering 5*8 bytes after the data type only.
Comment 10 Andrew Pinski 2023-04-12 09:04:36 UTC
(In reply to Richard Biener from comment #8)
> (In reply to Jakub Jelinek from comment #7)
> > (In reply to Richard Biener from comment #6)
> > > not sure if we should prevent all of those transforms.  But the question is
> > > why ASAN doesn't instrument the generated aggregate copy?  Maybe because
> > > in C/C++ you cannot write an aggregate array copy?
> > 
> > We do instrument those.  But only instrument them by checking the first and
> > last byte
> > of the copy, not all bytes in between (because that would be for inline
> > checking too large - we'd need to emit inline a loop over those bytes).
> 
> OK, that's lack of an appropriate API then?  But still the first and last
> byte should be sufficient to detect the problem (but maybe I'm missing
> something here).

Maybe it just happens the end to be on the stack of the inner most function so it just happens that it is an variable address still.
Comment 11 Martin Liška 2023-04-12 09:07:18 UTC
> 
> Maybe it just happens the end to be on the stack of the inner most function
> so it just happens that it is an variable address still.

No, that's not the case, see my previous comment.
Comment 12 rguenther@suse.de 2023-04-12 09:18:46 UTC
On Wed, 12 Apr 2023, marxin at gcc dot gnu.org wrote:

> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109446
> 
> --- Comment #9 from Martin Li?ka <marxin at gcc dot gnu.org> ---
> (In reply to Richard Biener from comment #8)
> > (In reply to Jakub Jelinek from comment #7)
> > > (In reply to Richard Biener from comment #6)
> > > > not sure if we should prevent all of those transforms.  But the question is
> > > > why ASAN doesn't instrument the generated aggregate copy?  Maybe because
> > > > in C/C++ you cannot write an aggregate array copy?
> > > 
> > > We do instrument those.  But only instrument them by checking the first and
> > > last byte
> > > of the copy, not all bytes in between (because that would be for inline
> > > checking too large - we'd need to emit inline a loop over those bytes).
> > 
> > OK, that's lack of an appropriate API then?  But still the first and last
> > byte should be sufficient to detect the problem (but maybe I'm missing
> > something here).
> 
> No, because the last byte is out of redzone:
> 
> =>0x7ffff5300000: f1 f1 f1 f1 00 00 00[f3]f3 f3 f3 f3 00 00 00 00
> 
> the 'f3' redzone is covering 5*8 bytes after the data type only.

OK, so it's lack of an API then.  The alternative would be to
do sth similar to stack-checking - instrument every 5*8 byte,
possibly up to some limit.  Or, as you probably suggest, avoid
folding memcpy with size larger than 5*8 byte inline.
Comment 13 Xi Ruoyao 2023-04-12 11:16:14 UTC
(In reply to rguenther@suse.de from comment #12)
> On Wed, 12 Apr 2023, marxin at gcc dot gnu.org wrote:
> 
> > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109446
> > 
> > --- Comment #9 from Martin Li?ka <marxin at gcc dot gnu.org> ---
> > (In reply to Richard Biener from comment #8)
> > > (In reply to Jakub Jelinek from comment #7)
> > > > (In reply to Richard Biener from comment #6)
> > > > > not sure if we should prevent all of those transforms.  But the question is
> > > > > why ASAN doesn't instrument the generated aggregate copy?  Maybe because
> > > > > in C/C++ you cannot write an aggregate array copy?
> > > > 
> > > > We do instrument those.  But only instrument them by checking the first and
> > > > last byte
> > > > of the copy, not all bytes in between (because that would be for inline
> > > > checking too large - we'd need to emit inline a loop over those bytes).
> > > 
> > > OK, that's lack of an appropriate API then?  But still the first and last
> > > byte should be sufficient to detect the problem (but maybe I'm missing
> > > something here).
> > 
> > No, because the last byte is out of redzone:
> > 
> > =>0x7ffff5300000: f1 f1 f1 f1 00 00 00[f3]f3 f3 f3 f3 00 00 00 00
> > 
> > the 'f3' redzone is covering 5*8 bytes after the data type only.
> 
> OK, so it's lack of an API then.  The alternative would be to
> do sth similar to stack-checking - instrument every 5*8 byte,
> possibly up to some limit.  Or, as you probably suggest, avoid
> folding memcpy with size larger than 5*8 byte inline.

I guess doing so will need to change the cpymemM expand of all targets.