Matt Byrd created CASSANDRA-8052:
------------------------------------

             Summary: OOMs from allocating large arrays when deserializing (e.g 
probably corrupted EstimatedHistogram data)
                 Key: CASSANDRA-8052
                 URL: https://issues.apache.org/jira/browse/CASSANDRA-8052
             Project: Cassandra
          Issue Type: Bug
          Components: Core
         Environment: linux
            Reporter: Matt Byrd



We've seen nodes with what are presumably corrupted sstables repeatedly OOM on 
attempted startup with such a message:
{code}
java.lang.OutOfMemoryError: Java heap space
 at 
org.apache.cassandra.utils.EstimatedHistogram$EstimatedHistogramSerializer.deserialize(EstimatedHistogram.java:266)
 
at 
org.apache.cassandra.io.sstable.SSTableMetadata$SSTableMetadataSerializer.deserialize(SSTableMetadata.java:292)
 at 
org.apache.cassandra.io.sstable.SSTableMetadata$SSTableMetadataSerializer.deserialize(SSTableMetadata.java:282)
 at 
org.apache.cassandra.io.sstable.SSTableReader.openMetadata(SSTableReader.java:234)
 at org.apache.cassandra.io.sstable.SSTableReader.open(SSTableReader.java:194)
 at org.apache.cassandra.io.sstable.SSTableReader.open(SSTableReader.java:157)
 at org.apache.cassandra.io.sstable.SSTableReader$1.run(SSTableReader.java:273)
 at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
 at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
 at java.util.concurrent.FutureTask.run(FutureTask.java:166)
 at 
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
 at 
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
 at java.lang.Thread.run(Thread.java:722)
{code}

It's probably not a coincidence that it's throwing an exception here since this 
seems to be the first byte of the file read.

Presumably the correct operational process is just to replace the node, 
however I was wondering if generally we might want to validate lengths when we 
deserialise things?
This could avoid allocating large byte buffers causing unpredictable OOMs and 
instead throw an exception to be handled as appropriate.

In this particular instance, there is no need for an unduly large size for the 
estimated histogram.
Admittedly things are slightly different in 2.1, though I suspect a similar 
thing might have happened with:
{code}
       int numComponents = in.readInt();
       // read toc
       Map<MetadataType, Integer> toc = new HashMap<>(numComponents); 
{code}
Doing a find usages of DataInputStream.readInt() reveals quite a few places 
where an int is read in and then an ArrayList, array or map of that size is 
created.
In some cases this size might validly vary over a java int,
or be in a performance critical or delicate piece of code where one doesn't 
want such checks.
Also there are other checksums and mechanisms at play which make some input 
less likely to be corrupted.

However, is it maybe worth a pass over instances of this type of input, to try 
and avoid such cases where it makes sense?
Perhaps there are less likely but worse failure modes present and hidden? 
E.g if the deserialisation is happens to be for a message sent to some or all 
nodes say.



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

Reply via email to