Reputation: 19
#include <iostream>
struct mix {
int64_t x[10];
};
int main() {
int64_t* p = new int64_t[100];
mix* px = new (p) mix;
mix* py = new (p + 10) mix;
px->x[12] = 104;
std::cout << py->x[2] << std::endl;
delete[] p;
}
In this code, px
and py
are created using placement new
within the same allocated memory block. When accessing px->x[12]
, it exceeds the boundary of the mix
object, but it is still within the bounds of the allocated memory block p
.
Using -fsanitize=address
, -fsanitize=undefined
, and Valgrind does not report any errors for this out-of-bounds access within the placement new boundaries. I need a method to strictly detect such out-of-bounds access beyond the object boundaries set by placement new
, even if it does not exceed the overall allocated memory block.
The solution must strictly check every out-of-bounds access. It is acceptable if the solution is slower, as long as it provides strict checking. No modifications to the source code should be required.
Upvotes: 1
Views: 157
Reputation: 601
Using -fsanitize=address, -fsanitize=undefined, and Valgrind does not report any errors for this out-of-bounds access within the placement new boundaries. I need a method to strictly detect such out-of-bounds access beyond the object boundaries set by placement new, even if it does not exceed the overall allocated memory block.
To directly quote the requirement, you could write a custom checker on your own side. Here is a simple example to enforce logical object boundaries and verify that accesses stay within them.:
struct mix {
int64_t x[10];
};
void* operator new(std::size_t size, void* ptr) {
// Custom placement new (same as default)
return ptr;
}
void* operator new(std::size_t size) {
// Regular allocation
return ::operator new(size);
}
int main() {
int64_t* p = new int64_t[100];
mix* px = new (p) mix;
mix* py = new (p + 10) mix;
// manually enforce bounds
if (p + 22 >= p + 100) {
std::cout << "out-of-bounds access!" << std::endl;
return 1;
}
px->x[12] = 104;
std::cout << py->x[2] << std::endl;
delete[] p;
}
Upvotes: 0
Reputation: 6946
All of the tools that I'm aware of (of which memcheck I know best) cannot do this.
The problem is that placement new is non-allocating. Well, there are two problems really. The second is that your out-of-boundedness is bigger than your chunk size. As a rule tools don't handle that well, having redzones smaller than the chunks (otherwise the memory use overhead gets to be prohibitive).
If you change your code to be something like
#include <iostream>
#include "memcheck.h"
struct mix {
int64_t x[10];
};
int main()
{
int64_t* p = new int64_t[1000];
// second argument is size of redzones
// third argument means memory is uninitialized
VALGRIND_CREATE_MEMPOOL(p, 10*sizeof(int64_t), false);
// allow space for redzone before px
mix* px = new (p+10) mix;
VALGRIND_MEMPOOL_ALLOC(p, px, 10*sizeof(int64_t));
// allow space for redzone after px and before py
mix* py = new (p + 30) mix;
VALGRIND_MEMPOOL_ALLOC(p, py, 10*sizeof(int64_t));
px->x[12] = 104;
std::cout << py->x[2] << std::endl;
delete[] p;
VALGRIND_DESTROY_MEMPOOL(p);
}
Then you will get something like
==712880== Invalid write of size 8
==712880== at 0x40151B: main (so_placement_new.cpp:16)
==712880== Address 0x53ea130 is 64 bytes before a block of size 80 client-defined
==712880== at 0x40150C: main (so_placement_new.cpp:15)
==712880==
==712880== Conditional jump or move depends on uninitialised value(s)
==712880== at 0x4B655AC: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (locale_facets.tcc:892)
==712880== by 0x4B72A39: put (locale_facets.h:2400)
==712880== by 0x4B72A39: std::ostream& std::ostream::_M_insert<long>(long) (ostream.tcc:78)
==712880== by 0x401537: main (so_placement_new.cpp:17)
==712880==
==712880== Use of uninitialised value of size 8
==712880== at 0x4B654BB: int std::__int_to_char<char, unsigned long>(char*, unsigned long, char const*, std::_Ios_Fmtflags, bool) (locale_facets.tcc:821)
==712880== by 0x4B655D6: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (locale_facets.tcc:894)
==712880== by 0x4B72A39: put (locale_facets.h:2400)
==712880== by 0x4B72A39: std::ostream& std::ostream::_M_insert<long>(long) (ostream.tcc:78)
==712880== by 0x401537: main (so_placement_new.cpp:17)
==712880==
==712880== Conditional jump or move depends on uninitialised value(s)
==712880== at 0x4B654CD: int std::__int_to_char<char, unsigned long>(char*, unsigned long, char const*, std::_Ios_Fmtflags, bool) (locale_facets.tcc:824)
==712880== by 0x4B655D6: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (locale_facets.tcc:894)
==712880== by 0x4B72A39: put (locale_facets.h:2400)
==712880== by 0x4B72A39: std::ostream& std::ostream::_M_insert<long>(long) (ostream.tcc:78)
==712880== by 0x401537: main (so_placement_new.cpp:17)
==712880==
==712880== Conditional jump or move depends on uninitialised value(s)
==712880== at 0x4B6560B: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (locale_facets.tcc:914)
==712880== by 0x4B72A39: put (locale_facets.h:2400)
==712880== by 0x4B72A39: std::ostream& std::ostream::_M_insert<long>(long) (ostream.tcc:78)
==712880== by 0x401537: main (so_placement_new.cpp:17)
I don't know ASAN so well. I'm not sure if it has any user memory pool mechanisms. You should be able to use ASAN_POISON_MEMORY_REGION (for p) and ASAN_UNPOISON_MEMORY_REGION (for px and py).
Upvotes: 3