如何修复 "Invalid read of size 8 - 40 bytes inside a block of size 64 free'd"
How to fix "Invalid read of size 8 - 40 bytes inside a block of size 64 free'd"
m_PhyToBtMap 中 SPacket 中的 shared_ptr 似乎导致 "Invalid read of size 8 - 40 bytes inside a block of size 64 free'd." 注意:这个 运行 在 valgrind(下面的日志)发布之前有将近 22 小时的数百万条消息此错误消息,但我也在 EraseAcknowledgedPackets(下方)中收到 SIGSEGV 崩溃,并怀疑这是原因。我正在使用 Boost 1.63,因为交叉编译器不支持 shared_ptr。 SendMessageToBt(大小为 8 的无效读取)和 EraseAcknowledgedPackets(大小为 64 的块内的 40 个字节已释放)在 valgrind 日志中被调出。
- 我使用 shared_ptr 或 make_shared 不当?
- 在执行 map.erase 或插入之前是否需要添加任何检查?
- 我是否正确理解了 valgrind 日志?
SPacket 和 m_PhyToBtMap
typedef struct SPacket
{
boost::shared_ptr<uint8_t[]> data;
size_t size;
} SPacket;
map<uint16_t, SPacket> m_PhyToBtMap;
SendMessageToBt
void RadioManager::SendMessageToBt(uint8_t * response, size_t responseSize)
{
CSrProtected ThreadSafe(m_LockPhyToBt);
SPacket sentPacket;
sentPacket.size = MESSAGE_HEADER_OFFSET + responseSize + CRC32_SIZE;
sentPacket.data = boost::make_shared<uint8_t[]>(sentPacket.size);
assert(sentPacket.data.get());
memcpy(&sentPacket.data.get()[MESSAGE_HEADER_OFFSET], response, responseSize);
m_AcknowledgementNumberSent = m_NextReceiveSequenceNumber;
m_SequenceNumberSent = m_NextSendSequenceNumber;
++m_NextSendSequenceNumber;
if (0 == m_NextSendSequenceNumber) m_NextSendSequenceNumber = SEQUENCE_NUMBER_MIN;
m_PhyToBtMap[m_SequenceNumberSent] = sentPacket; // RadioManager.cpp:246
sentPacket.data.get()[DATAGRAM_ACKNOWLEDGEMENT_LSB] = m_AcknowledgementNumberSent;
sentPacket.data.get()[DATAGRAM_ACKNOWLEDGEMENT_MSB] = m_AcknowledgementNumberSent >> 8;
sentPacket.data.get()[DATAGRAM_HEADER_SIZE + SEQUENCE_NUMBER_LIST_SIZE_LSB] = m_SequenceNumberSent;
sentPacket.data.get()[DATAGRAM_HEADER_SIZE + SEQUENCE_NUMBER_LIST_SIZE_MSB] = m_SequenceNumberSent >> 8;
SetCrc32(sentPacket.data.get(), sentPacket.size - CRC32_SIZE);
m_Socket->SendTo(sentPacket.data.get(), sentPacket.size);
}
EraseAcknowledgedPackets
void RadioManager::EraseAcknowledgedPackets(uint16_t acknowledgementNumber)
{
CSrProtected ThreadSafe(m_LockPhyToBt);
if (!m_PhyToBtMap.empty())
{
map<uint16_t, SPacket>::iterator it = m_PhyToBtMap.upper_bound(acknowledgementNumber);
int begin = m_PhyToBtMap.begin()->first;
if (begin > acknowledgementNumber) m_PhyToBtMap.erase(it, m_PhyToBtMap.end()); // acknowledgementNumber rollover
else m_PhyToBtMap.erase(m_PhyToBtMap.begin(), it); // RadioManager.cpp:1113
}
}
valgrind 日志
Invalid read of size 8
at 0x474F84: void std::swap<unsigned char*>(unsigned char*&, unsigned char*&) (move.h:176)
by 0x47430A: boost::shared_ptr<unsigned char []>::swap(boost::shared_ptr<unsigned char []>&) (shared_ptr.hpp:743)
by 0x473042: boost::shared_ptr<unsigned char []>::operator=(boost::shared_ptr<unsigned char []> const&) (shared_ptr.hpp:526)
by 0x4729B8: SPacket::operator=(SPacket const&) (RadioManager.h:32)
by 0x467C8E: RadioManager::SendMessageToBt(unsigned char*, unsigned long) (RadioManager.cpp:246)
by 0x46B64C: RadioManager::TransmitFecBlockResponseEvent(unsigned char*, unsigned long) (RadioManager.cpp:683)
by 0x4A5FDD: BtToRadioInterfaceClient::ProcessMessage(unsigned char*, unsigned long) (BtToRadioInterface.cpp:174)
by 0x4AB6AD: SrZmqPubSubInterface::ReadThread(void*) (SrZmqPubSubInterface.cpp:220)
by 0x4AC258: SingleParamObjCallback<SrZmqPubSubInterface, void*>::operator()(void*) (CallbackFunctors.h:161)
by 0x4AC1D4: CSrClassThread<SrZmqPubSubInterface, void*>::ThreadProcedure(void*) (SrClassThread.h:21)
by 0x46360A: SrThreadProcedure(void*) (SrThread.cpp:41)
by 0x50BCDC4: start_thread (pthread_create.c:308)
by 0x610273C: clone (clone.S:113)
Address 0xb93c638 is 40 bytes inside a block of size 64 free'd
at 0x4C29131: operator delete(void*) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
by 0x476841: __gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<unsigned short const, SPacket> > >::deallocate(std::_Rb_tree_node<std::pair<unsigned short const, SPacket> >*, unsigned long) (new_allocator.h:110)
by 0x47562D: std::_Rb_tree<unsigned short, std::pair<unsigned short const, SPacket>, std::_Select1st<std::pair<unsigned short const, SPacket> >, std::less<unsigned short>, std::allocator<std::pair<unsigned short const, SPacket> > >::_M_put_node(std::_Rb_tree_node<std::pair<unsigned short const, SPacket> >*) (stl_tree.h:374)
by 0x4748BC: std::_Rb_tree<unsigned short, std::pair<unsigned short const, SPacket>, std::_Select1st<std::pair<unsigned short const, SPacket> >, std::less<unsigned short>, std::allocator<std::pair<unsigned short const, SPacket> > >::_M_destroy_node(std::_Rb_tree_node<std::pair<unsigned short const, SPacket> >*) (stl_tree.h:396)
by 0x473AB8: std::_Rb_tree<unsigned short, std::pair<unsigned short const, SPacket>, std::_Select1st<std::pair<unsigned short const, SPacket> >, std::less<unsigned short>, std::allocator<std::pair<unsigned short const, SPacket> > >::_M_erase(std::_Rb_tree_node<std::pair<unsigned short const, SPacket> >*) (stl_tree.h:1127)
by 0x473C8C: std::_Rb_tree<unsigned short, std::pair<unsigned short const, SPacket>, std::_Select1st<std::pair<unsigned short const, SPacket> >, std::less<unsigned short>, std::allocator<std::pair<unsigned short const, SPacket> > >::clear() (stl_tree.h:860)
by 0x4754F1: std::_Rb_tree<unsigned short, std::pair<unsigned short const, SPacket>, std::_Select1st<std::pair<unsigned short const, SPacket> >, std::less<unsigned short>, std::allocator<std::pair<unsigned short const, SPacket> > >::_M_erase_aux(std::_Rb_tree_const_iterator<std::pair<unsigned short const, SPacket> >, std::_Rb_tree_const_iterator<std::pair<unsigned short const, SPacket> >) (stl_tree.h:1757)
by 0x474706: std::_Rb_tree<unsigned short, std::pair<unsigned short const, SPacket>, std::_Select1st<std::pair<unsigned short const, SPacket> >, std::less<unsigned short>, std::allocator<std::pair<unsigned short const, SPacket> > >::erase(std::_Rb_tree_iterator<std::pair<unsigned short const, SPacket> >, std::_Rb_tree_iterator<std::pair<unsigned short const, SPacket> >) (stl_tree.h:848)
by 0x47345E: std::map<unsigned short, SPacket, std::less<unsigned short>, std::allocator<std::pair<unsigned short const, SPacket> > >::erase(std::_Rb_tree_iterator<std::pair<unsigned short const, SPacket> >, std::_Rb_tree_iterator<std::pair<unsigned short const, SPacket> >) (stl_map.h:763)
by 0x46E7A3: RadioManager::EraseAcknowledgedPackets(unsigned short) (RadioManager.cpp:1113)
by 0x46D785: RadioManager::Decode(unsigned char*, unsigned long) (RadioManager.cpp:935)
by 0x4655DE: MsgDispatcher::Decode(UdpState*) (MsgDispatcher.cpp:20)
by 0x465976: SingleParamObjCallback<MsgDispatcher, UdpState*>::operator()(UdpState*) (CallbackFunctors.h:161)
by 0x464CBC: IsimUdpSocket::ReceiveHandler(void*) (IsimUdpSocket.cpp:41)
by 0x465410: SingleParamObjCallback<IsimUdpSocket, void*>::operator()(void*) (CallbackFunctors.h:161)
by 0x46538C: CSrClassThread<IsimUdpSocket, void*>::ThreadProcedure(void*) (SrClassThread.h:21)
by 0x46360A: SrThreadProcedure(void*) (SrThread.cpp:41)
我创建了这个 SSCCE Live On Coliru 来实现我所做的任何假设:
#include <boost/make_shared.hpp>
#include <boost/thread.hpp>
constexpr unsigned SEQUENCE_NUMBER_MIN = 100u;
struct SPacket {
boost::shared_ptr<uint8_t[]> data;
size_t size;
};
struct RadioManager {
void SendMessageToBt(uint8_t const* response, size_t responseSize);
void SetCrc32(uint8_t * buffer, size_t offset) {
buffer[offset++] = 0; // TODO
buffer[offset++] = 0;
buffer[offset++] = 0;
buffer[offset++] = 0;
}
void EraseAcknowledgedPackets(uint16_t acknowledgementNumber);
private:
mutable boost::mutex m_LockPhyToBt;
using CSrProtected = boost::mutex::scoped_lock;
std::map<uint16_t, SPacket> m_PhyToBtMap;
uint16_t m_NextReceiveSequenceNumber = SEQUENCE_NUMBER_MIN;
uint16_t m_AcknowledgementNumberSent;
uint16_t m_NextSendSequenceNumber = SEQUENCE_NUMBER_MIN;
uint16_t m_SequenceNumberSent;
enum {
DATAGRAM_ACKNOWLEDGEMENT_LSB = 0,
DATAGRAM_ACKNOWLEDGEMENT_MSB = 1,
DATAGRAM_HEADER_SIZE = 8,
SEQUENCE_NUMBER_LIST_SIZE_LSB = 0,
SEQUENCE_NUMBER_LIST_SIZE_MSB = 1,
MESSAGE_HEADER_OFFSET = 10,
//
CRC32_SIZE = 4
};
};
void RadioManager::EraseAcknowledgedPackets(uint16_t acknowledgementNumber) {
CSrProtected ThreadSafe(m_LockPhyToBt);
auto it = m_PhyToBtMap.upper_bound(acknowledgementNumber);
int begin = m_PhyToBtMap.begin()->first;
if (begin > acknowledgementNumber)
m_PhyToBtMap.erase(it, m_PhyToBtMap.end()); // acknowledgementNumber rollover
else
m_PhyToBtMap.erase(m_PhyToBtMap.begin(), it); // RadioManager.cpp:1113
}
void RadioManager::SendMessageToBt(uint8_t const* response, size_t responseSize)
{
CSrProtected ThreadSafe(m_LockPhyToBt);
SPacket sentPacket;
sentPacket.size = MESSAGE_HEADER_OFFSET + responseSize + CRC32_SIZE;
sentPacket.data = boost::make_shared<uint8_t[]>(sentPacket.size);
auto data = sentPacket.data.get();
assert(data);
memcpy(data + MESSAGE_HEADER_OFFSET, response, responseSize);
m_AcknowledgementNumberSent = m_NextReceiveSequenceNumber;
m_SequenceNumberSent = m_NextSendSequenceNumber;
if (0 == ++m_NextSendSequenceNumber)
m_NextSendSequenceNumber = SEQUENCE_NUMBER_MIN;
m_PhyToBtMap[m_SequenceNumberSent] = sentPacket; // RadioManager.cpp:246
data[DATAGRAM_ACKNOWLEDGEMENT_LSB] = m_AcknowledgementNumberSent;
data[DATAGRAM_ACKNOWLEDGEMENT_MSB] = m_AcknowledgementNumberSent >> 8;
data[DATAGRAM_HEADER_SIZE + SEQUENCE_NUMBER_LIST_SIZE_LSB] = m_SequenceNumberSent;
data[DATAGRAM_HEADER_SIZE + SEQUENCE_NUMBER_LIST_SIZE_MSB] = m_SequenceNumberSent >> 8;
SetCrc32(data, sentPacket.size - CRC32_SIZE);
//m_Socket->SendTo(data, sentPacket.size);
}
int main() {
RadioManager rm;
uint8_t message[] = "HELLO WORLD";
rm.SendMessageToBt(message, sizeof(message));
rm.SendMessageToBt(message, sizeof(message));
rm.EraseAcknowledgedPackets(SEQUENCE_NUMBER_MIN);
rm.SendMessageToBt(message, sizeof(message));
rm.SendMessageToBt(message, sizeof(message));
}
看着又长又累,只能看到
显示的代码没有问题(假设 CSrProtected
确实是 "critical section" 的作用域锁,所以像 std::lock_guard<std::mutex>
)。
SendMessageToBt
里面的那一行读到触发,其实应该是误报出界了。换句话说,该消息表示它在分配 SPacket
的 data
成员时尝试读取 8 个字节。
它还报告地址是 40 字节到先前由 shared-ptr 分配的 64 字节块中(因此存在的东西是 inside char[]
分配给另一个共享指针)。
因为我们知道sentPacket
是完全在栈上的,所以"something"不可能是shared_ptr<>
对象本身,而且uint8_t[]
数据在复制的时候并没有被复制共享指针,我们知道它必须是控制块。
但这没有任何意义,因为 shared_ptr
保证控制块不会移动或消失,直到最后一个 shared_ptr<>
实例消失。我们至少有 1 个在堆栈上,所以...
所有这一切只会导致 1 种可能的结果:其他地方有 Undefined Behaviour(可能会破坏堆栈中的 SPacket
实例?)。 UB 的一个不太可能的来源确实是可能缺乏线程同步。
- 检查
CSrProtected
是否如您所愿
- 检查 ALL 对成员数据的操作是否在正确的关键部分同步
其他说明:
- 将
size
分开有点奇怪。现在, data
的生命周期是共享的,但大小不是。为什么不将 SPacket
替换为 shared_ptr<vector<uint8_t> >
这样的东西,这样你
- 没有那种奇怪的分裂
- 可以使用
vector::at
而不是未经检查的 operator[]
索引。这将保护您免受越界(如果 DATAGRAM_HEADER_SIZE + SEQUENCE_NUMBER_LIST_SIZE_LSB
恰好 > MESSAGE_HEADER_OFFSET
等)。
您已经在使用 Valgrind。也许 ASan/UBSan 编译器开关可以提供帮助。
m_PhyToBtMap 中 SPacket 中的 shared_ptr 似乎导致 "Invalid read of size 8 - 40 bytes inside a block of size 64 free'd." 注意:这个 运行 在 valgrind(下面的日志)发布之前有将近 22 小时的数百万条消息此错误消息,但我也在 EraseAcknowledgedPackets(下方)中收到 SIGSEGV 崩溃,并怀疑这是原因。我正在使用 Boost 1.63,因为交叉编译器不支持 shared_ptr。 SendMessageToBt(大小为 8 的无效读取)和 EraseAcknowledgedPackets(大小为 64 的块内的 40 个字节已释放)在 valgrind 日志中被调出。
- 我使用 shared_ptr 或 make_shared 不当?
- 在执行 map.erase 或插入之前是否需要添加任何检查?
- 我是否正确理解了 valgrind 日志?
SPacket 和 m_PhyToBtMap
typedef struct SPacket
{
boost::shared_ptr<uint8_t[]> data;
size_t size;
} SPacket;
map<uint16_t, SPacket> m_PhyToBtMap;
SendMessageToBt
void RadioManager::SendMessageToBt(uint8_t * response, size_t responseSize)
{
CSrProtected ThreadSafe(m_LockPhyToBt);
SPacket sentPacket;
sentPacket.size = MESSAGE_HEADER_OFFSET + responseSize + CRC32_SIZE;
sentPacket.data = boost::make_shared<uint8_t[]>(sentPacket.size);
assert(sentPacket.data.get());
memcpy(&sentPacket.data.get()[MESSAGE_HEADER_OFFSET], response, responseSize);
m_AcknowledgementNumberSent = m_NextReceiveSequenceNumber;
m_SequenceNumberSent = m_NextSendSequenceNumber;
++m_NextSendSequenceNumber;
if (0 == m_NextSendSequenceNumber) m_NextSendSequenceNumber = SEQUENCE_NUMBER_MIN;
m_PhyToBtMap[m_SequenceNumberSent] = sentPacket; // RadioManager.cpp:246
sentPacket.data.get()[DATAGRAM_ACKNOWLEDGEMENT_LSB] = m_AcknowledgementNumberSent;
sentPacket.data.get()[DATAGRAM_ACKNOWLEDGEMENT_MSB] = m_AcknowledgementNumberSent >> 8;
sentPacket.data.get()[DATAGRAM_HEADER_SIZE + SEQUENCE_NUMBER_LIST_SIZE_LSB] = m_SequenceNumberSent;
sentPacket.data.get()[DATAGRAM_HEADER_SIZE + SEQUENCE_NUMBER_LIST_SIZE_MSB] = m_SequenceNumberSent >> 8;
SetCrc32(sentPacket.data.get(), sentPacket.size - CRC32_SIZE);
m_Socket->SendTo(sentPacket.data.get(), sentPacket.size);
}
EraseAcknowledgedPackets
void RadioManager::EraseAcknowledgedPackets(uint16_t acknowledgementNumber)
{
CSrProtected ThreadSafe(m_LockPhyToBt);
if (!m_PhyToBtMap.empty())
{
map<uint16_t, SPacket>::iterator it = m_PhyToBtMap.upper_bound(acknowledgementNumber);
int begin = m_PhyToBtMap.begin()->first;
if (begin > acknowledgementNumber) m_PhyToBtMap.erase(it, m_PhyToBtMap.end()); // acknowledgementNumber rollover
else m_PhyToBtMap.erase(m_PhyToBtMap.begin(), it); // RadioManager.cpp:1113
}
}
valgrind 日志
Invalid read of size 8
at 0x474F84: void std::swap<unsigned char*>(unsigned char*&, unsigned char*&) (move.h:176)
by 0x47430A: boost::shared_ptr<unsigned char []>::swap(boost::shared_ptr<unsigned char []>&) (shared_ptr.hpp:743)
by 0x473042: boost::shared_ptr<unsigned char []>::operator=(boost::shared_ptr<unsigned char []> const&) (shared_ptr.hpp:526)
by 0x4729B8: SPacket::operator=(SPacket const&) (RadioManager.h:32)
by 0x467C8E: RadioManager::SendMessageToBt(unsigned char*, unsigned long) (RadioManager.cpp:246)
by 0x46B64C: RadioManager::TransmitFecBlockResponseEvent(unsigned char*, unsigned long) (RadioManager.cpp:683)
by 0x4A5FDD: BtToRadioInterfaceClient::ProcessMessage(unsigned char*, unsigned long) (BtToRadioInterface.cpp:174)
by 0x4AB6AD: SrZmqPubSubInterface::ReadThread(void*) (SrZmqPubSubInterface.cpp:220)
by 0x4AC258: SingleParamObjCallback<SrZmqPubSubInterface, void*>::operator()(void*) (CallbackFunctors.h:161)
by 0x4AC1D4: CSrClassThread<SrZmqPubSubInterface, void*>::ThreadProcedure(void*) (SrClassThread.h:21)
by 0x46360A: SrThreadProcedure(void*) (SrThread.cpp:41)
by 0x50BCDC4: start_thread (pthread_create.c:308)
by 0x610273C: clone (clone.S:113)
Address 0xb93c638 is 40 bytes inside a block of size 64 free'd
at 0x4C29131: operator delete(void*) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
by 0x476841: __gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<unsigned short const, SPacket> > >::deallocate(std::_Rb_tree_node<std::pair<unsigned short const, SPacket> >*, unsigned long) (new_allocator.h:110)
by 0x47562D: std::_Rb_tree<unsigned short, std::pair<unsigned short const, SPacket>, std::_Select1st<std::pair<unsigned short const, SPacket> >, std::less<unsigned short>, std::allocator<std::pair<unsigned short const, SPacket> > >::_M_put_node(std::_Rb_tree_node<std::pair<unsigned short const, SPacket> >*) (stl_tree.h:374)
by 0x4748BC: std::_Rb_tree<unsigned short, std::pair<unsigned short const, SPacket>, std::_Select1st<std::pair<unsigned short const, SPacket> >, std::less<unsigned short>, std::allocator<std::pair<unsigned short const, SPacket> > >::_M_destroy_node(std::_Rb_tree_node<std::pair<unsigned short const, SPacket> >*) (stl_tree.h:396)
by 0x473AB8: std::_Rb_tree<unsigned short, std::pair<unsigned short const, SPacket>, std::_Select1st<std::pair<unsigned short const, SPacket> >, std::less<unsigned short>, std::allocator<std::pair<unsigned short const, SPacket> > >::_M_erase(std::_Rb_tree_node<std::pair<unsigned short const, SPacket> >*) (stl_tree.h:1127)
by 0x473C8C: std::_Rb_tree<unsigned short, std::pair<unsigned short const, SPacket>, std::_Select1st<std::pair<unsigned short const, SPacket> >, std::less<unsigned short>, std::allocator<std::pair<unsigned short const, SPacket> > >::clear() (stl_tree.h:860)
by 0x4754F1: std::_Rb_tree<unsigned short, std::pair<unsigned short const, SPacket>, std::_Select1st<std::pair<unsigned short const, SPacket> >, std::less<unsigned short>, std::allocator<std::pair<unsigned short const, SPacket> > >::_M_erase_aux(std::_Rb_tree_const_iterator<std::pair<unsigned short const, SPacket> >, std::_Rb_tree_const_iterator<std::pair<unsigned short const, SPacket> >) (stl_tree.h:1757)
by 0x474706: std::_Rb_tree<unsigned short, std::pair<unsigned short const, SPacket>, std::_Select1st<std::pair<unsigned short const, SPacket> >, std::less<unsigned short>, std::allocator<std::pair<unsigned short const, SPacket> > >::erase(std::_Rb_tree_iterator<std::pair<unsigned short const, SPacket> >, std::_Rb_tree_iterator<std::pair<unsigned short const, SPacket> >) (stl_tree.h:848)
by 0x47345E: std::map<unsigned short, SPacket, std::less<unsigned short>, std::allocator<std::pair<unsigned short const, SPacket> > >::erase(std::_Rb_tree_iterator<std::pair<unsigned short const, SPacket> >, std::_Rb_tree_iterator<std::pair<unsigned short const, SPacket> >) (stl_map.h:763)
by 0x46E7A3: RadioManager::EraseAcknowledgedPackets(unsigned short) (RadioManager.cpp:1113)
by 0x46D785: RadioManager::Decode(unsigned char*, unsigned long) (RadioManager.cpp:935)
by 0x4655DE: MsgDispatcher::Decode(UdpState*) (MsgDispatcher.cpp:20)
by 0x465976: SingleParamObjCallback<MsgDispatcher, UdpState*>::operator()(UdpState*) (CallbackFunctors.h:161)
by 0x464CBC: IsimUdpSocket::ReceiveHandler(void*) (IsimUdpSocket.cpp:41)
by 0x465410: SingleParamObjCallback<IsimUdpSocket, void*>::operator()(void*) (CallbackFunctors.h:161)
by 0x46538C: CSrClassThread<IsimUdpSocket, void*>::ThreadProcedure(void*) (SrClassThread.h:21)
by 0x46360A: SrThreadProcedure(void*) (SrThread.cpp:41)
我创建了这个 SSCCE Live On Coliru 来实现我所做的任何假设:
#include <boost/make_shared.hpp>
#include <boost/thread.hpp>
constexpr unsigned SEQUENCE_NUMBER_MIN = 100u;
struct SPacket {
boost::shared_ptr<uint8_t[]> data;
size_t size;
};
struct RadioManager {
void SendMessageToBt(uint8_t const* response, size_t responseSize);
void SetCrc32(uint8_t * buffer, size_t offset) {
buffer[offset++] = 0; // TODO
buffer[offset++] = 0;
buffer[offset++] = 0;
buffer[offset++] = 0;
}
void EraseAcknowledgedPackets(uint16_t acknowledgementNumber);
private:
mutable boost::mutex m_LockPhyToBt;
using CSrProtected = boost::mutex::scoped_lock;
std::map<uint16_t, SPacket> m_PhyToBtMap;
uint16_t m_NextReceiveSequenceNumber = SEQUENCE_NUMBER_MIN;
uint16_t m_AcknowledgementNumberSent;
uint16_t m_NextSendSequenceNumber = SEQUENCE_NUMBER_MIN;
uint16_t m_SequenceNumberSent;
enum {
DATAGRAM_ACKNOWLEDGEMENT_LSB = 0,
DATAGRAM_ACKNOWLEDGEMENT_MSB = 1,
DATAGRAM_HEADER_SIZE = 8,
SEQUENCE_NUMBER_LIST_SIZE_LSB = 0,
SEQUENCE_NUMBER_LIST_SIZE_MSB = 1,
MESSAGE_HEADER_OFFSET = 10,
//
CRC32_SIZE = 4
};
};
void RadioManager::EraseAcknowledgedPackets(uint16_t acknowledgementNumber) {
CSrProtected ThreadSafe(m_LockPhyToBt);
auto it = m_PhyToBtMap.upper_bound(acknowledgementNumber);
int begin = m_PhyToBtMap.begin()->first;
if (begin > acknowledgementNumber)
m_PhyToBtMap.erase(it, m_PhyToBtMap.end()); // acknowledgementNumber rollover
else
m_PhyToBtMap.erase(m_PhyToBtMap.begin(), it); // RadioManager.cpp:1113
}
void RadioManager::SendMessageToBt(uint8_t const* response, size_t responseSize)
{
CSrProtected ThreadSafe(m_LockPhyToBt);
SPacket sentPacket;
sentPacket.size = MESSAGE_HEADER_OFFSET + responseSize + CRC32_SIZE;
sentPacket.data = boost::make_shared<uint8_t[]>(sentPacket.size);
auto data = sentPacket.data.get();
assert(data);
memcpy(data + MESSAGE_HEADER_OFFSET, response, responseSize);
m_AcknowledgementNumberSent = m_NextReceiveSequenceNumber;
m_SequenceNumberSent = m_NextSendSequenceNumber;
if (0 == ++m_NextSendSequenceNumber)
m_NextSendSequenceNumber = SEQUENCE_NUMBER_MIN;
m_PhyToBtMap[m_SequenceNumberSent] = sentPacket; // RadioManager.cpp:246
data[DATAGRAM_ACKNOWLEDGEMENT_LSB] = m_AcknowledgementNumberSent;
data[DATAGRAM_ACKNOWLEDGEMENT_MSB] = m_AcknowledgementNumberSent >> 8;
data[DATAGRAM_HEADER_SIZE + SEQUENCE_NUMBER_LIST_SIZE_LSB] = m_SequenceNumberSent;
data[DATAGRAM_HEADER_SIZE + SEQUENCE_NUMBER_LIST_SIZE_MSB] = m_SequenceNumberSent >> 8;
SetCrc32(data, sentPacket.size - CRC32_SIZE);
//m_Socket->SendTo(data, sentPacket.size);
}
int main() {
RadioManager rm;
uint8_t message[] = "HELLO WORLD";
rm.SendMessageToBt(message, sizeof(message));
rm.SendMessageToBt(message, sizeof(message));
rm.EraseAcknowledgedPackets(SEQUENCE_NUMBER_MIN);
rm.SendMessageToBt(message, sizeof(message));
rm.SendMessageToBt(message, sizeof(message));
}
看着又长又累,只能看到
显示的代码没有问题(假设
CSrProtected
确实是 "critical section" 的作用域锁,所以像std::lock_guard<std::mutex>
)。SendMessageToBt
里面的那一行读到触发,其实应该是误报出界了。换句话说,该消息表示它在分配SPacket
的data
成员时尝试读取 8 个字节。它还报告地址是 40 字节到先前由 shared-ptr 分配的 64 字节块中(因此存在的东西是 inside
char[]
分配给另一个共享指针)。因为我们知道
sentPacket
是完全在栈上的,所以"something"不可能是shared_ptr<>
对象本身,而且uint8_t[]
数据在复制的时候并没有被复制共享指针,我们知道它必须是控制块。但这没有任何意义,因为
shared_ptr
保证控制块不会移动或消失,直到最后一个shared_ptr<>
实例消失。我们至少有 1 个在堆栈上,所以...
所有这一切只会导致 1 种可能的结果:其他地方有 Undefined Behaviour(可能会破坏堆栈中的 SPacket
实例?)。 UB 的一个不太可能的来源确实是可能缺乏线程同步。
- 检查
CSrProtected
是否如您所愿 - 检查 ALL 对成员数据的操作是否在正确的关键部分同步
其他说明:
- 将
size
分开有点奇怪。现在,data
的生命周期是共享的,但大小不是。为什么不将SPacket
替换为shared_ptr<vector<uint8_t> >
这样的东西,这样你- 没有那种奇怪的分裂
- 可以使用
vector::at
而不是未经检查的operator[]
索引。这将保护您免受越界(如果DATAGRAM_HEADER_SIZE + SEQUENCE_NUMBER_LIST_SIZE_LSB
恰好 >MESSAGE_HEADER_OFFSET
等)。
您已经在使用 Valgrind。也许 ASan/UBSan 编译器开关可以提供帮助。