[558633] Validate classes on deserialization

Change-Id: I7c9ac823f543aeba14ce181b47f24984549bfd24
diff --git a/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/SnapshotImpl.java b/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/SnapshotImpl.java
index 3622d0f..1a178a5 100644
--- a/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/SnapshotImpl.java
+++ b/plugins/org.eclipse.mat.parser/src/org/eclipse/mat/parser/internal/SnapshotImpl.java
@@ -17,8 +17,10 @@
 import java.io.FileInputStream;

 import java.io.FileOutputStream;

 import java.io.IOException;

+import java.io.InvalidClassException;
 import java.io.ObjectInputStream;

 import java.io.ObjectOutputStream;

+import java.io.ObjectStreamClass;
 import java.util.ArrayList;

 import java.util.Arrays;

 import java.util.Collection;

@@ -102,7 +104,75 @@
             File indexFile = new File(prefix + "index"); //$NON-NLS-1$

             fis = new FileInputStream(indexFile);

             listener.worked(1);

-            ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(fis));

+            /**
+             * Classes deserialized:
+             * org.eclipse.mat.parser.model.XSnapshotInfo
+             * org.eclipse.mat.parser.model.AbstractObjectImpl
+             * org.eclipse.mat.parser.model.XGCRootInfo
+             * org.eclipse.mat.parser.model.ClassImpl
+             * [Lorg.eclipse.mat.parser.model.XGCRootInfo
+             * org.eclipse.mat.parser.model.XGCRootInfo
+             *
+             * org.eclipse.mat.snapshot.SnapshotInfo
+             * org.eclipse.mat.snapshot.UnreachableObjectsHistogram
+             *
+             * org.eclipse.mat.snapshot.model.GCRootInfo
+             * org.eclipse.mat.snapshot.model.FieldDescriptor
+             * org.eclipse.mat.snapshot.model.Field
+             * [Lorg.eclipse.mat.snapshot.model.FieldDescriptor
+             * [Lorg.eclipse.mat.snapshot.model.Field
+             * org.eclipse.mat.snapshot.model.ObjectReference
+             *
+             * org.eclipse.mat.collect.HashMapIntObject
+             * org.eclipse.mat.collect.BitField
+             *
+             * java.util.ArrayList:
+             * java.util.Date
+             * java.util.HashMap
+             *
+             * java.lang.Boolean
+             * java.lang.Long
+             * java.lang.Number
+             * java.lang.Byte
+             * java.lang.Short
+             * java.lang.Character
+             * java.lang.Double
+             * java.lang.Float
+             * [I
+             *
+             */
+            ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(fis)) {
+                @Override
+                protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
+                    // similar to system property jdk.serialFilter
+                    String match="java.lang.*;java.util.*;org.eclipse.mat.parser.model.*;org.eclipse.mat.snapshot.*;org.eclipse.mat.snapshot.model.*;org.eclipse.mat.collect.*;!*"; //$NON-NLS-1$
+                    String nm = desc.getName();
+                    if (!nm.startsWith("[")) //$NON-NLS-1$
+                    {
+                        for (String pt : match.split(";")) //$NON-NLS-1$
+                        {
+                            boolean not = pt.startsWith("!"); //$NON-NLS-1$
+                            if (not)
+                                pt = pt.substring(1);
+                            boolean m;
+                            if (pt.endsWith(".**")) //$NON-NLS-1$
+                                m = nm.startsWith(pt.substring(0, pt.length() - 2));
+                            else if (pt.endsWith(".*")) //$NON-NLS-1$
+                                m = nm.startsWith(pt.substring(0, pt.length() - 1))
+                                                && !nm.substring(pt.length() - 1).contains("."); //$NON-NLS-1$
+                            else if (pt.endsWith("*")) //$NON-NLS-1$
+                                m = nm.startsWith(pt.substring(0, pt.length() - 1));
+                            else
+                                m = nm.equals(pt);
+                            if (not && m)
+                                throw new InvalidClassException(nm, match);
+                            if (m)
+                                break;
+                        }
+                    }
+                    return super.resolveClass(desc);
+                }
+            };
 

             String version = in.readUTF();

             if (!VERSION.equals(version))

@@ -155,12 +225,26 @@
             }

 

             IndexManager indexManager = new IndexManager();

-            indexManager.init(prefix);

+            boolean done = false;
+            try
+            {
+                indexManager.init(prefix);
 

-            SnapshotImpl ret = new SnapshotImpl(snapshotInfo, heapObjectReader, classCache, roots, rootsPerThread, loaderLabels,

-                            arrayObjects, indexManager);

-            listener.worked(3);

-            return ret;

+                SnapshotImpl ret = new SnapshotImpl(snapshotInfo, heapObjectReader, classCache, roots, rootsPerThread, loaderLabels,
+                                arrayObjects, indexManager);
+                listener.worked(3);
+                done = true;
+
+                return ret;
+            }
+            finally
+            {
+                if (!done)
+                {
+                    // Close files on error to allow delete
+                    indexManager.close();
+                }
+            }
         }

         catch (ClassNotFoundException e)

         {

diff --git a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/SnapshotHistoryService.java b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/SnapshotHistoryService.java
index f753ae1..0d138bd 100644
--- a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/SnapshotHistoryService.java
+++ b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/SnapshotHistoryService.java
@@ -14,8 +14,10 @@
 import java.io.FileInputStream;

 import java.io.FileOutputStream;

 import java.io.IOException;

+import java.io.InvalidClassException;
 import java.io.ObjectInputStream;

 import java.io.ObjectOutputStream;

+import java.io.ObjectStreamClass;
 import java.io.Serializable;

 import java.util.ArrayList;

 import java.util.Iterator;

@@ -197,7 +199,47 @@
 

                 list = new LinkedList<Entry>();

 

-                ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));

+                /*
+                 * org.eclipse.mat.ui.SnapshotHistoryService$Entry
+                 * java.lang.Long
+                 * java.lang.Number
+                 * org.eclipse.mat.snapshot.SnapshotInfo
+                 * java.util.Date
+                 * java.util.HashMap
+                 * java.lang.Boolean
+                 */
+                ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file)) {
+                    @Override
+                    protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
+                        // similar to system property jdk.serialFilter
+                        String match="java.lang.*;java.util.*;org.eclipse.mat.snapshot.SnapshotInfo;org.eclipse.mat.ui.SnapshotHistoryService$Entry;!*"; //$NON-NLS-1$
+                        String nm = desc.getName();
+                        if (!nm.startsWith("[")) //$NON-NLS-1$
+                        {
+                            for (String pt : match.split(";")) //$NON-NLS-1$
+                            {
+                                boolean not = pt.startsWith("!"); //$NON-NLS-1$
+                                if (not)
+                                    pt = pt.substring(1);
+                                boolean m;
+                                if (pt.endsWith(".**")) //$NON-NLS-1$
+                                    m = nm.startsWith(pt.substring(0, pt.length() - 2));
+                                else if (pt.endsWith(".*")) //$NON-NLS-1$
+                                    m = nm.startsWith(pt.substring(0, pt.length() - 1))
+                                    && !nm.substring(pt.length() - 1).contains("."); //$NON-NLS-1$
+                                else if (pt.endsWith("*")) //$NON-NLS-1$
+                                    m = nm.startsWith(pt.substring(0, pt.length() - 1));
+                                else
+                                    m = nm.equals(pt);
+                                if (not && m)
+                                    throw new InvalidClassException(nm, match);
+                                if (m)
+                                    break;
+                            }
+                        }
+                        return super.resolveClass(desc);
+                    }
+                };
                 try

                 {

                     int size = oin.readInt();

diff --git a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/browser/QueryHistory.java b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/browser/QueryHistory.java
index 4864eb0..1d99640 100644
--- a/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/browser/QueryHistory.java
+++ b/plugins/org.eclipse.mat.ui/src/org/eclipse/mat/ui/internal/browser/QueryHistory.java
@@ -14,8 +14,10 @@
 import java.io.FileInputStream;

 import java.io.FileOutputStream;

 import java.io.IOException;

+import java.io.InvalidClassException;
 import java.io.ObjectInputStream;

 import java.io.ObjectOutputStream;

+import java.io.ObjectStreamClass;
 import java.util.ArrayList;

 import java.util.Iterator;

 import java.util.LinkedList;

@@ -72,7 +74,47 @@
         {

             try

             {

-                ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));

+                /**
+                 * org.eclipse.mat.ui.SnapshotHistoryService$Entry
+                 * java.lang.Long
+                 * java.lang.Number
+                 * org.eclipse.mat.snapshot.SnapshotInfo
+                 * java.util.Date
+                 * java.util.HashMap
+                 * java.lang.Boolean
+                 */
+                ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file)) {
+                    @Override
+                    protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
+                        // similar to system property jdk.serialFilter
+                        String match="java.lang.*;java.util.*;org.eclipse.mat.snapshot.*;org.eclipse.mat.ui.SnapshotHistoryService$Entry;!*"; //$NON-NLS-1$
+                        String nm = desc.getName();
+                        if (!nm.startsWith("[")) //$NON-NLS-1$
+                        {
+                            for (String pt : match.split(";")) //$NON-NLS-1$
+                            {
+                                boolean not = pt.startsWith("!"); //$NON-NLS-1$
+                                if (not)
+                                    pt = pt.substring(1);
+                                boolean m;
+                                if (pt.endsWith(".**")) //$NON-NLS-1$
+                                    m = nm.startsWith(pt.substring(0, pt.length() - 2));
+                                else if (pt.endsWith(".*")) //$NON-NLS-1$
+                                    m = nm.startsWith(pt.substring(0, pt.length() - 1))
+                                    && !nm.substring(pt.length() - 1).contains("."); //$NON-NLS-1$
+                                else if (pt.endsWith("*")) //$NON-NLS-1$
+                                    m = nm.startsWith(pt.substring(0, pt.length() - 1));
+                                else
+                                    m = nm.equals(pt);
+                                if (not && m)
+                                    throw new InvalidClassException(nm, match);
+                                if (m)
+                                    break;
+                            }
+                        }
+                        return super.resolveClass(desc);
+                    }
+                };
                 try

                 {

                     history = (LinkedList<String>) oin.readObject();