summaryrefslogtreecommitdiff
path: root/cpp/src/Ice/BasicStream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/Ice/BasicStream.cpp')
-rw-r--r--cpp/src/Ice/BasicStream.cpp228
1 files changed, 174 insertions, 54 deletions
diff --git a/cpp/src/Ice/BasicStream.cpp b/cpp/src/Ice/BasicStream.cpp
index 49db851d1d9..ef9b58d172c 100644
--- a/cpp/src/Ice/BasicStream.cpp
+++ b/cpp/src/Ice/BasicStream.cpp
@@ -56,6 +56,7 @@ IceInternal::BasicStream::BasicStream(Instance* instance) :
_traceSlicing(-1),
_sliceObjects(true),
_messageSizeMax(_instance->messageSizeMax()), // Cached for efficiency.
+ _seqDataStack(0),
_objectList(0)
{
}
@@ -76,6 +77,13 @@ IceInternal::BasicStream::~BasicStream()
delete oldEncaps;
}
+ while(_seqDataStack)
+ {
+ SeqData* oldSeqData = _seqDataStack;
+ _seqDataStack = _seqDataStack->previous;
+ delete oldSeqData;
+ }
+
delete _objectList;
}
@@ -94,6 +102,7 @@ IceInternal::BasicStream::swap(BasicStream& other)
std::swap(i, other.i);
std::swap(_currentReadEncaps, other._currentReadEncaps);
std::swap(_currentWriteEncaps, other._currentWriteEncaps);
+ std::swap(_seqDataStack, other._seqDataStack);
std::swap(_objectList, other._objectList);
}
@@ -108,6 +117,135 @@ IceInternal::BasicStream::reserve(Container::size_type sz)
b.reserve(sz);
}
+//
+// startSeq() and endSeq() sanity-check sequence sizes during
+// unmarshaling and prevent malicious messages with incorrect
+// sequence sizes from causing the receiver to use up all
+// available memory by allocating sequences with an impossibly
+// large number of elements.
+//
+// The code generator inserts calls to startSeq() and endSeq()
+// around the code to unmarshal a sequence. startSeq() is called
+// immediately after reading the sequence size, and endSeq() is
+// called after reading the final element of a sequence.
+//
+// For sequences that contain constructed types that, in turn,
+// contain sequences, the code generator also inserts a call
+// to endElement() (inlined in BasicStream.h) after unmarshaling
+// each element.
+//
+// startSeq() is passed the unmarshaled element count, plus
+// the minimum size (in bytes) occupied by the sequence's
+// element type. numElements * minSize is the smallest
+// possible number of bytes that the sequence will occupy
+// on the wire.
+//
+// Every time startSeq() is called, it pushes the element
+// count and the minimum size on a stack. Every time endSeq()
+// is called, it pops the stack.
+//
+// For an ordinary sequence (one that does not (recursively)
+// contain nested sequences), numElements * minSize must be
+// less than the number of bytes remaining in the stream.
+//
+// For a sequence that is nested within some other sequence,
+// there must be enough bytes remaining in the stream for
+// this sequence (numElements + minSize), plus the sum of
+// the bytes required by the remaining elements of all
+// the enclosing sequences.
+//
+// For the enclosing sequences, numElements - 1 is the
+// number of elements for which unmarshaling has not started
+// yet. (The call to endElement() in the generated code
+// decrements that number whenever a sequence element is
+// unmarshaled.)
+//
+// For sequence that variable-length elements, checkSeq() is called
+// whenever an element is unmarshaled. checkSeq() also checks
+// whether the stream has a sufficient number of bytes remaining.
+// This means that, for messages with bogus sequence sizes,
+// unmarshaling is aborted at the earliest possible point.
+//
+
+void
+IceInternal::BasicStream::startSeq(int numElements, int minSize)
+{
+ if(numElements == 0) // Optimization to avoid pushing a useless stack frame.
+ {
+ return;
+ }
+
+ //
+ // Push the current sequence details on the stack.
+ //
+ SeqData* sd = new SeqData(numElements, minSize);
+ sd->previous = _seqDataStack;
+ _seqDataStack = sd;
+
+ int bytesLeft = b.end() - i;
+ if(_seqDataStack == 0) // Outermost sequence
+ {
+ //
+ // The sequence must fit within the message.
+ //
+ if(numElements * minSize > bytesLeft)
+ {
+ throw UnmarshalOutOfBoundsException(__FILE__, __LINE__);
+ }
+ }
+ else // Nested sequence
+ {
+ checkSeq(bytesLeft);
+ }
+
+}
+
+//
+// Check, given the number of elements requested for this sequence,
+// that this sequence, plus the sum of the sizes of the remaining
+// number of elements of all enclosing sequences, would still fit within the message.
+//
+void
+IceInternal::BasicStream::checkSeq()
+{
+ checkSeq(b.end() - i);
+}
+
+void
+IceInternal::BasicStream::checkSeq(int bytesLeft)
+{
+ int size = 0;
+ SeqData* sd = _seqDataStack;
+ do
+ {
+ size += (sd->numElements - 1) * sd->minSize;
+ sd = sd->previous;
+ }
+ while(sd);
+
+ if(size > bytesLeft)
+ {
+ throw UnmarshalOutOfBoundsException(__FILE__, __LINE__);
+ }
+}
+
+void
+IceInternal::BasicStream::endSeq(int sz)
+{
+ if(sz == 0) // Pop only if something was pushed previously.
+ {
+ return;
+ }
+
+ //
+ // Pop the sequence stack.
+ //
+ SeqData* oldSeqData = _seqDataStack;
+ assert(oldSeqData);
+ _seqDataStack = oldSeqData->previous;
+ delete oldSeqData;
+}
+
IceInternal::BasicStream::WriteEncaps::WriteEncaps()
: writeIndex(0), toBeMarshaledMap(0), marshaledMap(0), typeIdMap(0), typeIdIndex(0), previous(0)
{
@@ -276,14 +414,6 @@ void
IceInternal::BasicStream::endWriteSlice()
{
Int sz = static_cast<Int>(b.size() - _writeSlice + sizeof(Int));
-#if 0
- const Byte* p = reinterpret_cast<const Byte*>(&sz);
-#ifdef ICE_BIG_ENDIAN
- reverse_copy(p, p + sizeof(Int), b.begin() + _writeSlice - sizeof(Int));
-#else
- copy(p, p + sizeof(Int), b.begin() + _writeSlice - sizeof(Int));
-#endif
-#else
Byte* dest = &(*(b.begin() + _writeSlice - sizeof(Int)));
#ifdef ICE_BIG_ENDIAN
const Byte* src = reinterpret_cast<const Byte*>(&sz) + sizeof(Int) - 1;
@@ -298,7 +428,6 @@ IceInternal::BasicStream::endWriteSlice()
*dest++ = *src++;
*dest = *src;
#endif
-#endif
}
void
@@ -465,14 +594,12 @@ IceInternal::BasicStream::read(vector<Byte>& v)
{
Int sz;
readSize(sz);
- if(b.end() - i < sz)
- {
- throw UnmarshalOutOfBoundsException(__FILE__, __LINE__);
- }
+ startSeq(sz, 1);
Container::iterator begin = i;
i += sz;
v.resize(sz);
ice_copy(begin, i, v.begin());
+ endSeq(sz);
}
void
@@ -490,14 +617,12 @@ IceInternal::BasicStream::read(vector<bool>& v)
{
Int sz;
readSize(sz);
- if(b.end() - i < sz)
- {
- throw UnmarshalOutOfBoundsException(__FILE__, __LINE__);
- }
+ startSeq(sz, 1);
Container::iterator begin = i;
i += sz;
v.resize(sz);
ice_copy(begin, i, v.begin());
+ endSeq(sz);
}
void
@@ -568,13 +693,9 @@ IceInternal::BasicStream::read(vector<Short>& v)
{
Int sz;
readSize(sz);
- const int length = sz * static_cast<int>(sizeof(Short));
- if(b.end() - i < length)
- {
- throw UnmarshalOutOfBoundsException(__FILE__, __LINE__);
- }
+ startSeq(sz, sizeof(Short));
Container::iterator begin = i;
- i += length;
+ i += sz * static_cast<int>(sizeof(Short));
v.resize(sz);
if(sz > 0)
{
@@ -591,6 +712,7 @@ IceInternal::BasicStream::read(vector<Short>& v)
ice_copy(begin, i, reinterpret_cast<Byte*>(&v[0]));
#endif
}
+ endSeq(sz);
}
void
@@ -671,13 +793,9 @@ IceInternal::BasicStream::read(vector<Int>& v)
{
Int sz;
readSize(sz);
- const int length = sz * static_cast<int>(sizeof(Int));
- if(b.end() - i < length)
- {
- throw UnmarshalOutOfBoundsException(__FILE__, __LINE__);
- }
+ startSeq(sz, sizeof(Int));
Container::iterator begin = i;
- i += length;
+ i += sz * static_cast<int>(sizeof(Int));
v.resize(sz);
if(sz > 0)
{
@@ -696,6 +814,7 @@ IceInternal::BasicStream::read(vector<Int>& v)
ice_copy(begin, i, reinterpret_cast<Byte*>(&v[0]));
#endif
}
+ endSeq(sz);
}
void
@@ -796,13 +915,9 @@ IceInternal::BasicStream::read(vector<Long>& v)
{
Int sz;
readSize(sz);
- const int length = sz * static_cast<int>(sizeof(Long));
- if(b.end() - i < length)
- {
- throw UnmarshalOutOfBoundsException(__FILE__, __LINE__);
- }
+ startSeq(sz, sizeof(Long));
Container::iterator begin = i;
- i += length;
+ i += sz * static_cast<int>(sizeof(Long));
v.resize(sz);
if(sz > 0)
{
@@ -825,6 +940,7 @@ IceInternal::BasicStream::read(vector<Long>& v)
ice_copy(begin, i, reinterpret_cast<Byte*>(&v[0]));
#endif
}
+ endSeq(sz);
}
void
@@ -905,13 +1021,9 @@ IceInternal::BasicStream::read(vector<Float>& v)
{
Int sz;
readSize(sz);
- const int length = sz * static_cast<int>(sizeof(Float));
- if(b.end() - i < length)
- {
- throw UnmarshalOutOfBoundsException(__FILE__, __LINE__);
- }
+ startSeq(sz, sizeof(Float));
Container::iterator begin = i;
- i += length;
+ i += sz * static_cast<int>(sizeof(Float));
v.resize(sz);
if(sz > 0)
{
@@ -930,6 +1042,7 @@ IceInternal::BasicStream::read(vector<Float>& v)
ice_copy(begin, i, reinterpret_cast<Byte*>(&v[0]));
#endif
}
+ endSeq(sz);
}
void
@@ -1030,13 +1143,9 @@ IceInternal::BasicStream::read(vector<Double>& v)
{
Int sz;
readSize(sz);
- const int length = sz * static_cast<int>(sizeof(Double));
- if(b.end() - i < length)
- {
- throw UnmarshalOutOfBoundsException(__FILE__, __LINE__);
- }
+ startSeq(sz, sizeof(Double));
Container::iterator begin = i;
- i += length;
+ i += sz * static_cast<int>(sizeof(Double));
v.resize(sz);
if(sz > 0)
{
@@ -1059,12 +1168,15 @@ IceInternal::BasicStream::read(vector<Double>& v)
ice_copy(begin, i, reinterpret_cast<Byte*>(&v[0]));
#endif
}
+ endSeq(sz);
}
//
// NOTE: This member function is intentionally omitted in order to
-// cause a link error if it is used. See BasicStream.h for more
-// information.
+// cause a link error if it is used. This is for efficiency
+// reasons: writing a const char * requires a traversal of the string
+// to get the string length first, which takes O(n) time, whereas getting
+// the string length from a std::string takes constant time.
//
/*
void
@@ -1122,18 +1234,22 @@ IceInternal::BasicStream::read(vector<string>& v)
{
Int sz;
readSize(sz);
+ startSeq(sz, 1);
v.clear();
//
- // Don't use v.resize(sz) or v.reserve(sz) here, as it cannot be
- // checked whether sz is a reasonable value.
+ // For efficiency, we use reserve() here to avoid having the vector
+ // reallocate repeatedly.
//
-
- while(sz--)
+ v.reserve(sz);
+ for(int i = 0; i < sz; ++i)
{
- v.resize(v.size() + 1);
+ v.resize(i + 1);
read(v.back());
+ checkSeq();
+ endElement();
}
+ endSeq(sz);
}
void
@@ -1613,3 +1729,7 @@ IceInternal::BasicStream::patchPointers(Int index, IndexToPtrMap::const_iterator
//
_currentReadEncaps->patchMap->erase(patchPos);
}
+
+IceInternal::BasicStream::SeqData::SeqData(int num, int sz) : numElements(num), minSize(sz)
+{
+}