diff options
Diffstat (limited to 'java/src')
-rw-r--r-- | java/src/IceInternal/BasicStream.java | 175 |
1 files changed, 170 insertions, 5 deletions
diff --git a/java/src/IceInternal/BasicStream.java b/java/src/IceInternal/BasicStream.java index ede70265d55..126be6eeba6 100644 --- a/java/src/IceInternal/BasicStream.java +++ b/java/src/IceInternal/BasicStream.java @@ -38,6 +38,7 @@ public class BasicStream _messageSizeMax = _instance.messageSizeMax(); // Cached for efficiency. + _seqDataStack = null; _objectList = null; } @@ -125,6 +126,10 @@ public class BasicStream other._writeSlice = _writeSlice; _writeSlice = tmpWriteSlice; + SeqData tmpSeqDataStack = other._seqDataStack; + other._seqDataStack = _seqDataStack; + _seqDataStack = tmpSeqDataStack; + java.util.ArrayList tmpObjectList = other._objectList; other._objectList = _objectList; _objectList = tmpObjectList; @@ -179,6 +184,138 @@ public class BasicStream return _buf; } + // + // 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() 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. + // + + public void + 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 = _buf.remaining(); + if(_seqDataStack == null) // Outermost sequence + { + // + // The sequence must fit within the message. + // + if(numElements * minSize > bytesLeft) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + 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. + // + public void + checkSeq() + { + checkSeq(_buf.remaining()); + } + + public void + checkSeq(int bytesLeft) + { + int size = 0; + SeqData sd = _seqDataStack; + do + { + size += (sd.numElements - 1) * sd.minSize; + sd = sd.previous; + } + while(sd != null); + + if(size > bytesLeft) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + + public void + endSeq(int sz) + { + if(sz == 0) // Pop only if something was pushed previously. + { + return; + } + + // + // Pop the sequence stack. + // + SeqData oldSeqData = _seqDataStack; + assert(oldSeqData != null); + _seqDataStack = oldSeqData.previous; + } + + public void endElement() + { + assert(_seqDataStack != null); + --_seqDataStack.numElements; + } + public void startWriteEncaps() { @@ -268,7 +405,7 @@ public class BasicStream /* if(sz - 4 > _buf.length()) { - throw UnmarshalOutOfBoundsException(__FILE__, __LINE__); + throw new Ice.UnmarshalOutOfBoundsException(); } */ _readEncapsStack.sz = sz; @@ -539,8 +676,10 @@ public class BasicStream try { final int sz = readSize(); + startSeq(sz, 1); byte[] v = new byte[sz]; _buf.get(v); + endSeq(sz); return v; } catch(java.nio.BufferUnderflowException ex) @@ -593,11 +732,13 @@ public class BasicStream try { final int sz = readSize(); + startSeq(sz, 1); boolean[] v = new boolean[sz]; for(int i = 0; i < sz; i++) { v[i] = _buf.get() == 1; } + endSeq(sz); return v; } catch(java.nio.BufferUnderflowException ex) @@ -649,10 +790,12 @@ public class BasicStream try { final int sz = readSize(); + startSeq(sz, 2); short[] v = new short[sz]; java.nio.ShortBuffer shortBuf = _buf.asShortBuffer(); shortBuf.get(v); _buf.position(_buf.position() + sz * 2); + endSeq(sz); return v; } catch(java.nio.BufferUnderflowException ex) @@ -704,10 +847,12 @@ public class BasicStream try { final int sz = readSize(); + startSeq(sz, 4); int[] v = new int[sz]; java.nio.IntBuffer intBuf = _buf.asIntBuffer(); intBuf.get(v); _buf.position(_buf.position() + sz * 4); + endSeq(sz); return v; } catch(java.nio.BufferUnderflowException ex) @@ -759,10 +904,12 @@ public class BasicStream try { final int sz = readSize(); + startSeq(sz, 8); long[] v = new long[sz]; java.nio.LongBuffer longBuf = _buf.asLongBuffer(); longBuf.get(v); _buf.position(_buf.position() + sz * 8); + endSeq(sz); return v; } catch(java.nio.BufferUnderflowException ex) @@ -814,10 +961,12 @@ public class BasicStream try { final int sz = readSize(); + startSeq(sz, 4); float[] v = new float[sz]; java.nio.FloatBuffer floatBuf = _buf.asFloatBuffer(); floatBuf.get(v); _buf.position(_buf.position() + sz * 4); + endSeq(sz); return v; } catch(java.nio.BufferUnderflowException ex) @@ -869,10 +1018,12 @@ public class BasicStream try { final int sz = readSize(); + startSeq(sz, 8); double[] v = new double[sz]; java.nio.DoubleBuffer doubleBuf = _buf.asDoubleBuffer(); doubleBuf.get(v); _buf.position(_buf.position() + sz * 8); + endSeq(sz); return v; } catch(java.nio.BufferUnderflowException ex) @@ -990,16 +1141,16 @@ public class BasicStream public String[] readStringSeq() { - // - // TODO: This code is dangerous, because it cannot be - // checked whether sz is a reasonable value. - // final int sz = readSize(); + startSeq(sz, 1); String[] v = new String[sz]; for(int i = 0; i < sz; i++) { v[i] = readString(); + checkSeq(); + endElement(); } + endSeq(sz); return v; } @@ -1757,5 +1908,19 @@ public class BasicStream private int _messageSizeMax; + private static final class SeqData + { + public SeqData(int numElements, int minSize) + { + this.numElements = numElements; + this.minSize = minSize; + } + + public int numElements; + public int minSize; + public SeqData previous; + } + SeqData _seqDataStack; + private java.util.ArrayList _objectList; } |