Avoid undefined behavior using CFlatData in CScript serialization

`&vch[vch.size()]` and even `&vch[0]` on vectors can cause assertion
errors with VC in debug mode. This is the problem mentioned in #4239.
The deeper problem with this is that we rely on undefined behavior.

- Add `begin_ptr` and `end_ptr` functions that get the beginning and end
  pointer of vector in a reliable way that copes with empty vectors and
  doesn't reference outside the vector
(see https://stackoverflow.com/questions/1339470/how-to-get-the-address-of-the-stdvector-buffer-start-most-elegantly/1339767#1339767).
- Add a convenience constructor to CFlatData that wraps a vector.

I added `begin_ptr` and `end_ptr` as separate functions as I imagine
they will be useful in more places.
This commit is contained in:
Wladimir J. van der Laan 2014-06-05 10:10:52 +02:00
parent 52d4abfdef
commit fa126effc2
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
2 changed files with 38 additions and 4 deletions

View File

@ -770,12 +770,12 @@ public:
void Serialize(Stream &s, int nType, int nVersion) const { void Serialize(Stream &s, int nType, int nVersion) const {
std::vector<unsigned char> compr; std::vector<unsigned char> compr;
if (Compress(compr)) { if (Compress(compr)) {
s << CFlatData(&compr[0], &compr[compr.size()]); s << CFlatData(compr);
return; return;
} }
unsigned int nSize = script.size() + nSpecialScripts; unsigned int nSize = script.size() + nSpecialScripts;
s << VARINT(nSize); s << VARINT(nSize);
s << CFlatData(&script[0], &script[script.size()]); s << CFlatData(script);
} }
template<typename Stream> template<typename Stream>
@ -784,13 +784,13 @@ public:
s >> VARINT(nSize); s >> VARINT(nSize);
if (nSize < nSpecialScripts) { if (nSize < nSpecialScripts) {
std::vector<unsigned char> vch(GetSpecialSize(nSize), 0x00); std::vector<unsigned char> vch(GetSpecialSize(nSize), 0x00);
s >> REF(CFlatData(&vch[0], &vch[vch.size()])); s >> REF(CFlatData(vch));
Decompress(nSize, vch); Decompress(nSize, vch);
return; return;
} }
nSize -= nSpecialScripts; nSize -= nSpecialScripts;
script.resize(nSize); script.resize(nSize);
s >> REF(CFlatData(&script[0], &script[script.size()])); s >> REF(CFlatData(script));
} }
}; };

View File

@ -37,6 +37,34 @@ inline T& REF(const T& val)
return const_cast<T&>(val); return const_cast<T&>(val);
} }
/** Get begin pointer of vector (non-const version).
* @note These functions avoid the undefined case of indexing into an empty
* vector, as well as that of indexing after the end of the vector.
*/
template <class T, class TAl>
inline T* begin_ptr(std::vector<T,TAl>& v)
{
return v.empty() ? NULL : &v[0];
}
/** Get begin pointer of vector (const version) */
template <class T, class TAl>
inline const T* begin_ptr(const std::vector<T,TAl>& v)
{
return v.empty() ? NULL : &v[0];
}
/** Get end pointer of vector (non-const version) */
template <class T, class TAl>
inline T* end_ptr(std::vector<T,TAl>& v)
{
return v.empty() ? NULL : (&v[0] + v.size());
}
/** Get end pointer of vector (const version) */
template <class T, class TAl>
inline const T* end_ptr(const std::vector<T,TAl>& v)
{
return v.empty() ? NULL : (&v[0] + v.size());
}
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// //
// Templates for serializing to anything that looks like a stream, // Templates for serializing to anything that looks like a stream,
@ -318,6 +346,12 @@ protected:
char* pend; char* pend;
public: public:
CFlatData(void* pbeginIn, void* pendIn) : pbegin((char*)pbeginIn), pend((char*)pendIn) { } CFlatData(void* pbeginIn, void* pendIn) : pbegin((char*)pbeginIn), pend((char*)pendIn) { }
template <class T, class TAl>
explicit CFlatData(std::vector<T,TAl> &v)
{
pbegin = (char*)begin_ptr(v);
pend = (char*)end_ptr(v);
}
char* begin() { return pbegin; } char* begin() { return pbegin; }
const char* begin() const { return pbegin; } const char* begin() const { return pbegin; }
char* end() { return pend; } char* end() { return pend; }