#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;
}
在此代码中,
px
和py
是使用同一分配的内存块中的放置new
创建的。访问px->x[12]
时,超出了mix
对象的边界,但仍在分配的内存块p
的边界内。
使用
-fsanitize=address
、-fsanitize=undefined
和 Valgrind 不会报告布局新边界内的越界访问的任何错误。我需要一种方法来严格检测这种超出放置设置的对象边界的越界访问new
,即使它没有超出总体分配的内存块。
解决方案必须严格检查每一次越界访问。如果解决方案较慢也是可以接受的,只要它提供严格的检查。无需修改源代码。
我所知道的所有工具(其中我最了解的是 memcheck)都无法做到这一点。
问题是新的放置是非分配的。嗯,确实有两个问题。第二个是你的越界大于你的块大小。一般来说,工具不能很好地处理,红区小于块(否则内存使用开销会令人望而却步)。
如果您将代码更改为类似
#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);
}
然后你会得到类似的东西
==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)