├── pom.xml ├── .gitignore ├── README.md └── src └── main └── java └── java └── io └── ObjectInputStream.java /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.tersesystems 8 | paranoid-java-serialization 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | org.apache.maven.plugins 15 | maven-compiler-plugin 16 | 3.1 17 | 18 | 1.8 19 | 1.8 20 | 21 | -XDignore.symbol.file 22 | 23 | true 24 | 25 | 26 | 27 | org.apache.maven.plugins 28 | maven-jar-plugin 29 | 2.4 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Maven template 3 | target/ 4 | pom.xml.tag 5 | pom.xml.releaseBackup 6 | pom.xml.versionsBackup 7 | pom.xml.next 8 | release.properties 9 | dependency-reduced-pom.xml 10 | buildNumber.properties 11 | .mvn/timing.properties 12 | ### JetBrains template 13 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 14 | 15 | *.iml 16 | 17 | ## Directory-based project format: 18 | .idea/ 19 | # if you remove the above rule, at least ignore the following: 20 | 21 | # User-specific stuff: 22 | # .idea/workspace.xml 23 | # .idea/tasks.xml 24 | # .idea/dictionaries 25 | 26 | # Sensitive or high-churn files: 27 | # .idea/dataSources.ids 28 | # .idea/dataSources.xml 29 | # .idea/sqlDataSources.xml 30 | # .idea/dynamic.xml 31 | # .idea/uiDesigner.xml 32 | 33 | # Gradle: 34 | # .idea/gradle.xml 35 | # .idea/libraries 36 | 37 | # Mongo Explorer plugin: 38 | # .idea/mongoSettings.xml 39 | 40 | ## File-based project format: 41 | *.ipr 42 | *.iws 43 | 44 | ## Plugin-specific files: 45 | 46 | # IntelliJ 47 | /out/ 48 | 49 | # mpeltonen/sbt-idea plugin 50 | .idea_modules/ 51 | 52 | # JIRA plugin 53 | atlassian-ide-plugin.xml 54 | 55 | # Crashlytics plugin (for Android Studio and IntelliJ) 56 | com_crashlytics_export_strings.xml 57 | crashlytics.properties 58 | crashlytics-build.properties 59 | 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Paranoid Java Serialization 2 | 3 | > **LEGAL WARNING**: This project modifies the boot classpath, which is fine locally, but cannot be deployed as it contravenes section F of the Oracle Binary Code License Agreement. If you see the java executable [Non-Standard Options](https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html#BABHDABI), there is a note saying "Do not deploy applications that use this option to override a class in rt.jar because this violates the Java Runtime Environment binary code license." 4 | > 5 | > **Please consider using an agent-based solution like [NotSoSerial](https://github.com/kantega/notsoserial) instead.** 6 | 7 | ## Overview 8 | 9 | This is a proof of concept that hacks `java.io.ObjectInputStream` to provide JVM level control over Java object serialization. Other solutions are user level -- they will work individually, but they don't change the behavior of internal libraries or application servers. This will enforce behavior at the lowest level. 10 | 11 | See [the blog post](https://tersesystems.com/2015/11/08/closing-the-open-door-of-java-object-serialization/) and the [original talk](https://frohoff.github.io/appseccali-marshalling-pickles/) for details. 12 | 13 | ## Building 14 | 15 | ``` 16 | mvn package 17 | ``` 18 | 19 | ## Running 20 | 21 | Create a file `deserialization.properties` in your local application directory: 22 | 23 | ``` 24 | paranoid.serialization.enabled=true 25 | #paranoid.serialization.blacklist=com.evil.Nastygram 26 | #paranoid.serialization.whitelist=com.good.VirtualPackage 27 | ``` 28 | 29 | And copy the `paranoid-java-serialization-1.0-SNAPSHOT.jar` created from the package to your local application. 30 | 31 | Then run Java with: 32 | 33 | ``` 34 | java \ 35 | -Xbootclasspath/p:paranoid-java-serialization-1.0-SNAPSHOT.jar \ 36 | -Djava.security.properties=deserialization.properties 37 | ``` 38 | 39 | ## Code 40 | 41 | Because this is a hack of ObjectInputStream, it's harder to see what's changed between this and the stock version. 42 | 43 | First, you can disable serialization entirely: 44 | 45 | ``` java 46 | public final Object readObject() 47 | throws IOException, ClassNotFoundException 48 | { 49 | String enabled = java.security.Security.getProperty("paranoid.serialization.enabled"); 50 | if (! Boolean.parseBoolean(enabled)) { 51 | throw new InvalidClassException("Object deserialization is disabled!"); 52 | } 53 | 54 | ... 55 | } 56 | ``` 57 | 58 | Second: you can whitelist and blacklist based on class name: 59 | 60 | 61 | ``` java 62 | /** Set of blacklisted class name patterns. */ 63 | private static final java.util.Set blacklistPatterns; 64 | 65 | /** Set of whitelisted class name patterns. */ 66 | private static final java.util.Set whitelistPatterns; 67 | 68 | // JUL isn't ideal, but it's the out of the box one. 69 | private static final Logger logger = Logger.getLogger("java.io.ObjectInputStream"); 70 | 71 | static { 72 | ... 73 | 74 | final String blacklist = Security.getProperty("paranoid.serialization.blacklist"); 75 | blacklistPatterns = parsePatterns(blacklist); 76 | 77 | final String whitelist = Security.getProperty("paranoid.serialization.whitelist"); 78 | whitelistPatterns = parsePatterns(whitelist); 79 | } 80 | 81 | private static Set parsePatterns(String listString) { 82 | final Set listSet = new HashSet<>(); 83 | if (listString != null) { 84 | final String[] regexArray = listString.split(",\\s*"); 85 | for (String regex : regexArray) { 86 | Pattern whitePattern = Pattern.compile(regex); 87 | listSet.add(whitePattern); 88 | } 89 | } 90 | return java.util.Collections.unmodifiableSet(listSet); 91 | } 92 | 93 | ... 94 | 95 | protected Class resolveClass(ObjectStreamClass desc) 96 | throws IOException, ClassNotFoundException 97 | { 98 | String name = desc.getName(); 99 | logger.fine("resolveClass: resolving " + name); 100 | 101 | // From https://github.com/ikkisoft/SerialKiller by luca.carettoni@ikkisoft.com 102 | 103 | //Enforce blacklist 104 | for (Pattern blackPattern : blacklistPatterns) { 105 | Matcher blackMatcher = blackPattern.matcher(name); 106 | if (blackMatcher.find()) { 107 | logger.warning("resolveClass: rejecting blacklisted class " + name); 108 | String msg = "[!] Blocked by blacklist '" 109 | + blackPattern.pattern() + "'. Match found for '" + name + "'"; 110 | throw new InvalidClassException(msg); 111 | } 112 | } 113 | 114 | //Enforce whitelist if it exists. 115 | if (! whitelistPatterns.isEmpty()) { 116 | boolean safeClass = false; 117 | for (Pattern whitePattern: whitelistPatterns) { 118 | Matcher whiteMatcher = whitePattern.matcher(name); 119 | if (whiteMatcher.find()) { 120 | safeClass = true; 121 | break; 122 | } 123 | } 124 | 125 | if (!safeClass) { 126 | logger.warning("resolveClass: rejecting class " + name + " not found in whitelist."); 127 | String msg = "[!] Blocked by whitelist. No match found for '" + name + "'"; 128 | throw new InvalidClassException(msg); 129 | } 130 | } 131 | 132 | try { 133 | logger.fine("resolveClass: accepting class " + name); 134 | return Class.forName(name, false, latestUserDefinedLoader()); 135 | } catch (ClassNotFoundException ex) { 136 | Class cl = primClasses.get(name); 137 | if (cl != null) { 138 | return cl; 139 | } else { 140 | throw ex; 141 | } 142 | } 143 | } 144 | ``` 145 | 146 | ## Logging 147 | 148 | If you are whitelisting, it can be helpful to run through the application first with a log of all the existing classes. 149 | 150 | While you can use configure `java.util.logging` through a properties file, it's probably better to use SLF4J and the [JUL to SLF4J bridge](http://mvnrepository.com/artifact/org.slf4j/jul-to-slf4j), after which you can use [Logback](http://mvnrepository.com/artifact/ch.qos.logback). You will need to add the following as initialization: 151 | 152 | ``` java 153 | LogManager.getLogManager().reset(); 154 | SLF4JBridgeHandler.removeHandlersForRootLogger(); 155 | SLF4JBridgeHandler.install(); 156 | Logger.getLogger("global").setLevel(Level.FINEST); 157 | ``` 158 | 159 | ## License 160 | 161 | Because this is a modification of OpenJDK code, the [GPL v2 license](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) applies to all of this code as well. 162 | 163 | Also, because this is a modification of Oracle code, I will not be providing a binary distribution and uploading it to Maven unless I have their express permission. Sorry about that. 164 | 165 | ``` 166 | /* 167 | * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. 168 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 169 | * 170 | * This code is free software; you can redistribute it and/or modify it 171 | * under the terms of the GNU General Public License version 2 only, as 172 | * published by the Free Software Foundation. Oracle designates this 173 | * particular file as subject to the "Classpath" exception as provided 174 | * by Oracle in the LICENSE file that accompanied this code. 175 | * 176 | * This code is distributed in the hope that it will be useful, but WITHOUT 177 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 178 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 179 | * version 2 for more details (a copy is included in the LICENSE file that 180 | * accompanied this code). 181 | * 182 | * You should have received a copy of the GNU General Public License version 183 | * 2 along with this work; if not, write to the Free Software Foundation, 184 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 185 | * 186 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 187 | * or visit www.oracle.com if you need additional information or have any 188 | * questions. 189 | */ 190 | ``` 191 | 192 | ## Questions 193 | 194 | Email [will.sargent+github@gmail.com](mailto:will.sargent+github@gmail.com). 195 | -------------------------------------------------------------------------------- /src/main/java/java/io/ObjectInputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package java.io; 27 | 28 | import java.io.ObjectStreamClass.WeakClassKey; 29 | import java.lang.ref.ReferenceQueue; 30 | import java.lang.reflect.Array; 31 | import java.lang.reflect.Modifier; 32 | import java.lang.reflect.Proxy; 33 | import java.security.*; 34 | import java.util.Arrays; 35 | import java.util.HashMap; 36 | import java.util.HashSet; 37 | import java.util.Set; 38 | import java.util.concurrent.ConcurrentHashMap; 39 | import java.util.concurrent.ConcurrentMap; 40 | import java.util.concurrent.atomic.AtomicBoolean; 41 | import java.util.logging.Logger; 42 | import java.util.regex.Matcher; 43 | import java.util.regex.Pattern; 44 | 45 | import static java.io.ObjectStreamClass.processQueue; 46 | import sun.reflect.misc.ReflectUtil; 47 | 48 | /** 49 | * An ObjectInputStream deserializes primitive data and objects previously 50 | * written using an ObjectOutputStream. 51 | * 52 | *

ObjectOutputStream and ObjectInputStream can provide an application with 53 | * persistent storage for graphs of objects when used with a FileOutputStream 54 | * and FileInputStream respectively. ObjectInputStream is used to recover 55 | * those objects previously serialized. Other uses include passing objects 56 | * between hosts using a socket stream or for marshaling and unmarshaling 57 | * arguments and parameters in a remote communication system. 58 | * 59 | *

ObjectInputStream ensures that the types of all objects in the graph 60 | * created from the stream match the classes present in the Java Virtual 61 | * Machine. Classes are loaded as required using the standard mechanisms. 62 | * 63 | *

Only objects that support the java.io.Serializable or 64 | * java.io.Externalizable interface can be read from streams. 65 | * 66 | *

The method readObject is used to read an object from the 67 | * stream. Java's safe casting should be used to get the desired type. In 68 | * Java, strings and arrays are objects and are treated as objects during 69 | * serialization. When read they need to be cast to the expected type. 70 | * 71 | *

Primitive data types can be read from the stream using the appropriate 72 | * method on DataInput. 73 | * 74 | *

The default deserialization mechanism for objects restores the contents 75 | * of each field to the value and type it had when it was written. Fields 76 | * declared as transient or static are ignored by the deserialization process. 77 | * References to other objects cause those objects to be read from the stream 78 | * as necessary. Graphs of objects are restored correctly using a reference 79 | * sharing mechanism. New objects are always allocated when deserializing, 80 | * which prevents existing objects from being overwritten. 81 | * 82 | *

Reading an object is analogous to running the constructors of a new 83 | * object. Memory is allocated for the object and initialized to zero (NULL). 84 | * No-arg constructors are invoked for the non-serializable classes and then 85 | * the fields of the serializable classes are restored from the stream starting 86 | * with the serializable class closest to java.lang.object and finishing with 87 | * the object's most specific class. 88 | * 89 | *

For example to read from a stream as written by the example in 90 | * ObjectOutputStream: 91 | *
92 | *

  93 |  *      FileInputStream fis = new FileInputStream("t.tmp");
  94 |  *      ObjectInputStream ois = new ObjectInputStream(fis);
  95 |  *
  96 |  *      int i = ois.readInt();
  97 |  *      String today = (String) ois.readObject();
  98 |  *      Date date = (Date) ois.readObject();
  99 |  *
 100 |  *      ois.close();
 101 |  * 
102 | * 103 | *

Classes control how they are serialized by implementing either the 104 | * java.io.Serializable or java.io.Externalizable interfaces. 105 | * 106 | *

Implementing the Serializable interface allows object serialization to 107 | * save and restore the entire state of the object and it allows classes to 108 | * evolve between the time the stream is written and the time it is read. It 109 | * automatically traverses references between objects, saving and restoring 110 | * entire graphs. 111 | * 112 | *

Serializable classes that require special handling during the 113 | * serialization and deserialization process should implement the following 114 | * methods: 115 | * 116 | *

 117 |  * private void writeObject(java.io.ObjectOutputStream stream)
 118 |  *     throws IOException;
 119 |  * private void readObject(java.io.ObjectInputStream stream)
 120 |  *     throws IOException, ClassNotFoundException;
 121 |  * private void readObjectNoData()
 122 |  *     throws ObjectStreamException;
 123 |  * 
124 | * 125 | *

The readObject method is responsible for reading and restoring the state 126 | * of the object for its particular class using data written to the stream by 127 | * the corresponding writeObject method. The method does not need to concern 128 | * itself with the state belonging to its superclasses or subclasses. State is 129 | * restored by reading data from the ObjectInputStream for the individual 130 | * fields and making assignments to the appropriate fields of the object. 131 | * Reading primitive data types is supported by DataInput. 132 | * 133 | *

Any attempt to read object data which exceeds the boundaries of the 134 | * custom data written by the corresponding writeObject method will cause an 135 | * OptionalDataException to be thrown with an eof field value of true. 136 | * Non-object reads which exceed the end of the allotted data will reflect the 137 | * end of data in the same way that they would indicate the end of the stream: 138 | * bytewise reads will return -1 as the byte read or number of bytes read, and 139 | * primitive reads will throw EOFExceptions. If there is no corresponding 140 | * writeObject method, then the end of default serialized data marks the end of 141 | * the allotted data. 142 | * 143 | *

Primitive and object read calls issued from within a readExternal method 144 | * behave in the same manner--if the stream is already positioned at the end of 145 | * data written by the corresponding writeExternal method, object reads will 146 | * throw OptionalDataExceptions with eof set to true, bytewise reads will 147 | * return -1, and primitive reads will throw EOFExceptions. Note that this 148 | * behavior does not hold for streams written with the old 149 | * ObjectStreamConstants.PROTOCOL_VERSION_1 protocol, in which the 150 | * end of data written by writeExternal methods is not demarcated, and hence 151 | * cannot be detected. 152 | * 153 | *

The readObjectNoData method is responsible for initializing the state of 154 | * the object for its particular class in the event that the serialization 155 | * stream does not list the given class as a superclass of the object being 156 | * deserialized. This may occur in cases where the receiving party uses a 157 | * different version of the deserialized instance's class than the sending 158 | * party, and the receiver's version extends classes that are not extended by 159 | * the sender's version. This may also occur if the serialization stream has 160 | * been tampered; hence, readObjectNoData is useful for initializing 161 | * deserialized objects properly despite a "hostile" or incomplete source 162 | * stream. 163 | * 164 | *

Serialization does not read or assign values to the fields of any object 165 | * that does not implement the java.io.Serializable interface. Subclasses of 166 | * Objects that are not serializable can be serializable. In this case the 167 | * non-serializable class must have a no-arg constructor to allow its fields to 168 | * be initialized. In this case it is the responsibility of the subclass to 169 | * save and restore the state of the non-serializable class. It is frequently 170 | * the case that the fields of that class are accessible (public, package, or 171 | * protected) or that there are get and set methods that can be used to restore 172 | * the state. 173 | * 174 | *

Any exception that occurs while deserializing an object will be caught by 175 | * the ObjectInputStream and abort the reading process. 176 | * 177 | *

Implementing the Externalizable interface allows the object to assume 178 | * complete control over the contents and format of the object's serialized 179 | * form. The methods of the Externalizable interface, writeExternal and 180 | * readExternal, are called to save and restore the objects state. When 181 | * implemented by a class they can write and read their own state using all of 182 | * the methods of ObjectOutput and ObjectInput. It is the responsibility of 183 | * the objects to handle any versioning that occurs. 184 | * 185 | *

Enum constants are deserialized differently than ordinary serializable or 186 | * externalizable objects. The serialized form of an enum constant consists 187 | * solely of its name; field values of the constant are not transmitted. To 188 | * deserialize an enum constant, ObjectInputStream reads the constant name from 189 | * the stream; the deserialized constant is then obtained by calling the static 190 | * method Enum.valueOf(Class, String) with the enum constant's 191 | * base type and the received constant name as arguments. Like other 192 | * serializable or externalizable objects, enum constants can function as the 193 | * targets of back references appearing subsequently in the serialization 194 | * stream. The process by which enum constants are deserialized cannot be 195 | * customized: any class-specific readObject, readObjectNoData, and readResolve 196 | * methods defined by enum types are ignored during deserialization. 197 | * Similarly, any serialPersistentFields or serialVersionUID field declarations 198 | * are also ignored--all enum types have a fixed serialVersionUID of 0L. 199 | * 200 | * @author Mike Warres 201 | * @author Roger Riggs 202 | * @see java.io.DataInput 203 | * @see java.io.ObjectOutputStream 204 | * @see java.io.Serializable 205 | * @see Object Serialization Specification, Section 3, Object Input Classes 206 | * @since JDK1.1 207 | */ 208 | public class ObjectInputStream 209 | extends InputStream implements ObjectInput, ObjectStreamConstants 210 | { 211 | /** handle value representing null */ 212 | private static final int NULL_HANDLE = -1; 213 | 214 | /** marker for unshared objects in internal handle table */ 215 | private static final Object unsharedMarker = new Object(); 216 | 217 | /** table mapping primitive type names to corresponding class objects */ 218 | private static final HashMap> primClasses 219 | = new HashMap<>(8, 1.0F); 220 | 221 | /** Set of blacklisted class name patterns. */ 222 | private static final java.util.Set blacklistPatterns; 223 | 224 | /** Set of whitelisted class name patterns. */ 225 | private static final java.util.Set whitelistPatterns; 226 | 227 | // JUL isn't ideal, but it's the out of the box one. 228 | private static final Logger logger = Logger.getLogger("java.io.ObjectInputStream"); 229 | 230 | static { 231 | primClasses.put("boolean", boolean.class); 232 | primClasses.put("byte", byte.class); 233 | primClasses.put("char", char.class); 234 | primClasses.put("short", short.class); 235 | primClasses.put("int", int.class); 236 | primClasses.put("long", long.class); 237 | primClasses.put("float", float.class); 238 | primClasses.put("double", double.class); 239 | primClasses.put("void", void.class); 240 | 241 | final String blacklist = Security.getProperty("paranoid.serialization.blacklist"); 242 | blacklistPatterns = parsePatterns(blacklist); 243 | 244 | final String whitelist = Security.getProperty("paranoid.serialization.whitelist"); 245 | whitelistPatterns = parsePatterns(whitelist); 246 | } 247 | 248 | private static Set parsePatterns(String listString) { 249 | final Set listSet = new HashSet<>(); 250 | if (listString != null) { 251 | final String[] regexArray = listString.split(",\\s*"); 252 | for (String regex : regexArray) { 253 | Pattern whitePattern = Pattern.compile(regex); 254 | listSet.add(whitePattern); 255 | } 256 | } 257 | return java.util.Collections.unmodifiableSet(listSet); 258 | } 259 | 260 | private static class Caches { 261 | /** cache of subclass security audit results */ 262 | static final ConcurrentMap subclassAudits = 263 | new ConcurrentHashMap<>(); 264 | 265 | /** queue for WeakReferences to audited subclasses */ 266 | static final ReferenceQueue> subclassAuditsQueue = 267 | new ReferenceQueue<>(); 268 | } 269 | 270 | /** filter stream for handling block data conversion */ 271 | private final BlockDataInputStream bin; 272 | /** validation callback list */ 273 | private final ValidationList vlist; 274 | /** recursion depth */ 275 | private int depth; 276 | /** whether stream is closed */ 277 | private boolean closed; 278 | 279 | /** wire handle -> obj/exception map */ 280 | private final HandleTable handles; 281 | /** scratch field for passing handle values up/down call stack */ 282 | private int passHandle = NULL_HANDLE; 283 | /** flag set when at end of field value block with no TC_ENDBLOCKDATA */ 284 | private boolean defaultDataEnd = false; 285 | 286 | /** buffer for reading primitive field values */ 287 | private byte[] primVals; 288 | 289 | /** if true, invoke readObjectOverride() instead of readObject() */ 290 | private final boolean enableOverride; 291 | /** if true, invoke resolveObject() */ 292 | private boolean enableResolve; 293 | 294 | /** 295 | * Context during upcalls to class-defined readObject methods; holds 296 | * object currently being deserialized and descriptor for current class. 297 | * Null when not during readObject upcall. 298 | */ 299 | private SerialCallbackContext curContext; 300 | 301 | /** 302 | * Creates an ObjectInputStream that reads from the specified InputStream. 303 | * A serialization stream header is read from the stream and verified. 304 | * This constructor will block until the corresponding ObjectOutputStream 305 | * has written and flushed the header. 306 | * 307 | *

If a security manager is installed, this constructor will check for 308 | * the "enableSubclassImplementation" SerializablePermission when invoked 309 | * directly or indirectly by the constructor of a subclass which overrides 310 | * the ObjectInputStream.readFields or ObjectInputStream.readUnshared 311 | * methods. 312 | * 313 | * @param in input stream to read from 314 | * @throws StreamCorruptedException if the stream header is incorrect 315 | * @throws IOException if an I/O error occurs while reading stream header 316 | * @throws SecurityException if untrusted subclass illegally overrides 317 | * security-sensitive methods 318 | * @throws NullPointerException if in is null 319 | * @see ObjectInputStream#ObjectInputStream() 320 | * @see ObjectInputStream#readFields() 321 | * @see ObjectOutputStream#ObjectOutputStream(OutputStream) 322 | */ 323 | public ObjectInputStream(InputStream in) throws IOException { 324 | verifySubclass(); 325 | bin = new BlockDataInputStream(in); 326 | handles = new HandleTable(10); 327 | vlist = new ValidationList(); 328 | enableOverride = false; 329 | readStreamHeader(); 330 | bin.setBlockDataMode(true); 331 | } 332 | 333 | /** 334 | * Provide a way for subclasses that are completely reimplementing 335 | * ObjectInputStream to not have to allocate private data just used by this 336 | * implementation of ObjectInputStream. 337 | * 338 | *

If there is a security manager installed, this method first calls the 339 | * security manager's checkPermission method with the 340 | * SerializablePermission("enableSubclassImplementation") 341 | * permission to ensure it's ok to enable subclassing. 342 | * 343 | * @throws SecurityException if a security manager exists and its 344 | * checkPermission method denies enabling 345 | * subclassing. 346 | * @throws IOException if an I/O error occurs while creating this stream 347 | * @see SecurityManager#checkPermission 348 | * @see java.io.SerializablePermission 349 | */ 350 | protected ObjectInputStream() throws IOException, SecurityException { 351 | SecurityManager sm = System.getSecurityManager(); 352 | if (sm != null) { 353 | sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); 354 | } 355 | bin = null; 356 | handles = null; 357 | vlist = null; 358 | enableOverride = true; 359 | } 360 | 361 | /** 362 | * Read an object from the ObjectInputStream. The class of the object, the 363 | * signature of the class, and the values of the non-transient and 364 | * non-static fields of the class and all of its supertypes are read. 365 | * Default deserializing for a class can be overriden using the writeObject 366 | * and readObject methods. Objects referenced by this object are read 367 | * transitively so that a complete equivalent graph of objects is 368 | * reconstructed by readObject. 369 | * 370 | *

The root object is completely restored when all of its fields and the 371 | * objects it references are completely restored. At this point the object 372 | * validation callbacks are executed in order based on their registered 373 | * priorities. The callbacks are registered by objects (in the readObject 374 | * special methods) as they are individually restored. 375 | * 376 | *

Exceptions are thrown for problems with the InputStream and for 377 | * classes that should not be deserialized. All exceptions are fatal to 378 | * the InputStream and leave it in an indeterminate state; it is up to the 379 | * caller to ignore or recover the stream state. 380 | * 381 | * @throws ClassNotFoundException Class of a serialized object cannot be 382 | * found. 383 | * @throws InvalidClassException Something is wrong with a class used by 384 | * serialization. 385 | * @throws StreamCorruptedException Control information in the 386 | * stream is inconsistent. 387 | * @throws OptionalDataException Primitive data was found in the 388 | * stream instead of objects. 389 | * @throws IOException Any of the usual Input/Output related exceptions. 390 | */ 391 | public final Object readObject() 392 | throws IOException, ClassNotFoundException 393 | { 394 | String enabled = java.security.Security.getProperty("paranoid.serialization.enabled"); 395 | if (! Boolean.parseBoolean(enabled)) { 396 | throw new InvalidClassException("Object deserialization is disabled!"); 397 | } 398 | 399 | if (enableOverride) { 400 | return readObjectOverride(); 401 | } 402 | 403 | // if nested read, passHandle contains handle of enclosing object 404 | int outerHandle = passHandle; 405 | try { 406 | Object obj = readObject0(false); 407 | handles.markDependency(outerHandle, passHandle); 408 | ClassNotFoundException ex = handles.lookupException(passHandle); 409 | if (ex != null) { 410 | throw ex; 411 | } 412 | if (depth == 0) { 413 | vlist.doCallbacks(); 414 | } 415 | return obj; 416 | } finally { 417 | passHandle = outerHandle; 418 | if (closed && depth == 0) { 419 | clear(); 420 | } 421 | } 422 | } 423 | 424 | /** 425 | * This method is called by trusted subclasses of ObjectOutputStream that 426 | * constructed ObjectOutputStream using the protected no-arg constructor. 427 | * The subclass is expected to provide an override method with the modifier 428 | * "final". 429 | * 430 | * @return the Object read from the stream. 431 | * @throws ClassNotFoundException Class definition of a serialized object 432 | * cannot be found. 433 | * @throws OptionalDataException Primitive data was found in the stream 434 | * instead of objects. 435 | * @throws IOException if I/O errors occurred while reading from the 436 | * underlying stream 437 | * @see #ObjectInputStream() 438 | * @see #readObject() 439 | * @since 1.2 440 | */ 441 | protected Object readObjectOverride() 442 | throws IOException, ClassNotFoundException 443 | { 444 | return null; 445 | } 446 | 447 | /** 448 | * Reads an "unshared" object from the ObjectInputStream. This method is 449 | * identical to readObject, except that it prevents subsequent calls to 450 | * readObject and readUnshared from returning additional references to the 451 | * deserialized instance obtained via this call. Specifically: 452 | *

    453 | *
  • If readUnshared is called to deserialize a back-reference (the 454 | * stream representation of an object which has been written 455 | * previously to the stream), an ObjectStreamException will be 456 | * thrown. 457 | * 458 | *
  • If readUnshared returns successfully, then any subsequent attempts 459 | * to deserialize back-references to the stream handle deserialized 460 | * by readUnshared will cause an ObjectStreamException to be thrown. 461 | *
462 | * Deserializing an object via readUnshared invalidates the stream handle 463 | * associated with the returned object. Note that this in itself does not 464 | * always guarantee that the reference returned by readUnshared is unique; 465 | * the deserialized object may define a readResolve method which returns an 466 | * object visible to other parties, or readUnshared may return a Class 467 | * object or enum constant obtainable elsewhere in the stream or through 468 | * external means. If the deserialized object defines a readResolve method 469 | * and the invocation of that method returns an array, then readUnshared 470 | * returns a shallow clone of that array; this guarantees that the returned 471 | * array object is unique and cannot be obtained a second time from an 472 | * invocation of readObject or readUnshared on the ObjectInputStream, 473 | * even if the underlying data stream has been manipulated. 474 | * 475 | *

ObjectInputStream subclasses which override this method can only be 476 | * constructed in security contexts possessing the 477 | * "enableSubclassImplementation" SerializablePermission; any attempt to 478 | * instantiate such a subclass without this permission will cause a 479 | * SecurityException to be thrown. 480 | * 481 | * @return reference to deserialized object 482 | * @throws ClassNotFoundException if class of an object to deserialize 483 | * cannot be found 484 | * @throws StreamCorruptedException if control information in the stream 485 | * is inconsistent 486 | * @throws ObjectStreamException if object to deserialize has already 487 | * appeared in stream 488 | * @throws OptionalDataException if primitive data is next in stream 489 | * @throws IOException if an I/O error occurs during deserialization 490 | * @since 1.4 491 | */ 492 | public Object readUnshared() throws IOException, ClassNotFoundException { 493 | // if nested read, passHandle contains handle of enclosing object 494 | int outerHandle = passHandle; 495 | try { 496 | Object obj = readObject0(true); 497 | handles.markDependency(outerHandle, passHandle); 498 | ClassNotFoundException ex = handles.lookupException(passHandle); 499 | if (ex != null) { 500 | throw ex; 501 | } 502 | if (depth == 0) { 503 | vlist.doCallbacks(); 504 | } 505 | return obj; 506 | } finally { 507 | passHandle = outerHandle; 508 | if (closed && depth == 0) { 509 | clear(); 510 | } 511 | } 512 | } 513 | 514 | /** 515 | * Read the non-static and non-transient fields of the current class from 516 | * this stream. This may only be called from the readObject method of the 517 | * class being deserialized. It will throw the NotActiveException if it is 518 | * called otherwise. 519 | * 520 | * @throws ClassNotFoundException if the class of a serialized object 521 | * could not be found. 522 | * @throws IOException if an I/O error occurs. 523 | * @throws NotActiveException if the stream is not currently reading 524 | * objects. 525 | */ 526 | public void defaultReadObject() 527 | throws IOException, ClassNotFoundException 528 | { 529 | SerialCallbackContext ctx = curContext; 530 | if (ctx == null) { 531 | throw new NotActiveException("not in call to readObject"); 532 | } 533 | Object curObj = ctx.getObj(); 534 | ObjectStreamClass curDesc = ctx.getDesc(); 535 | bin.setBlockDataMode(false); 536 | defaultReadFields(curObj, curDesc); 537 | bin.setBlockDataMode(true); 538 | if (!curDesc.hasWriteObjectData()) { 539 | /* 540 | * Fix for 4360508: since stream does not contain terminating 541 | * TC_ENDBLOCKDATA tag, set flag so that reading code elsewhere 542 | * knows to simulate end-of-custom-data behavior. 543 | */ 544 | defaultDataEnd = true; 545 | } 546 | ClassNotFoundException ex = handles.lookupException(passHandle); 547 | if (ex != null) { 548 | throw ex; 549 | } 550 | } 551 | 552 | /** 553 | * Reads the persistent fields from the stream and makes them available by 554 | * name. 555 | * 556 | * @return the GetField object representing the persistent 557 | * fields of the object being deserialized 558 | * @throws ClassNotFoundException if the class of a serialized object 559 | * could not be found. 560 | * @throws IOException if an I/O error occurs. 561 | * @throws NotActiveException if the stream is not currently reading 562 | * objects. 563 | * @since 1.2 564 | */ 565 | public ObjectInputStream.GetField readFields() 566 | throws IOException, ClassNotFoundException 567 | { 568 | SerialCallbackContext ctx = curContext; 569 | if (ctx == null) { 570 | throw new NotActiveException("not in call to readObject"); 571 | } 572 | Object curObj = ctx.getObj(); 573 | ObjectStreamClass curDesc = ctx.getDesc(); 574 | bin.setBlockDataMode(false); 575 | GetFieldImpl getField = new GetFieldImpl(curDesc); 576 | getField.readFields(); 577 | bin.setBlockDataMode(true); 578 | if (!curDesc.hasWriteObjectData()) { 579 | /* 580 | * Fix for 4360508: since stream does not contain terminating 581 | * TC_ENDBLOCKDATA tag, set flag so that reading code elsewhere 582 | * knows to simulate end-of-custom-data behavior. 583 | */ 584 | defaultDataEnd = true; 585 | } 586 | 587 | return getField; 588 | } 589 | 590 | /** 591 | * Register an object to be validated before the graph is returned. While 592 | * similar to resolveObject these validations are called after the entire 593 | * graph has been reconstituted. Typically, a readObject method will 594 | * register the object with the stream so that when all of the objects are 595 | * restored a final set of validations can be performed. 596 | * 597 | * @param obj the object to receive the validation callback. 598 | * @param prio controls the order of callbacks;zero is a good default. 599 | * Use higher numbers to be called back earlier, lower numbers for 600 | * later callbacks. Within a priority, callbacks are processed in 601 | * no particular order. 602 | * @throws NotActiveException The stream is not currently reading objects 603 | * so it is invalid to register a callback. 604 | * @throws InvalidObjectException The validation object is null. 605 | */ 606 | public void registerValidation(ObjectInputValidation obj, int prio) 607 | throws NotActiveException, InvalidObjectException 608 | { 609 | if (depth == 0) { 610 | throw new NotActiveException("stream inactive"); 611 | } 612 | vlist.register(obj, prio); 613 | } 614 | 615 | /** 616 | * Load the local class equivalent of the specified stream class 617 | * description. Subclasses may implement this method to allow classes to 618 | * be fetched from an alternate source. 619 | * 620 | *

The corresponding method in ObjectOutputStream is 621 | * annotateClass. This method will be invoked only once for 622 | * each unique class in the stream. This method can be implemented by 623 | * subclasses to use an alternate loading mechanism but must return a 624 | * Class object. Once returned, if the class is not an array 625 | * class, its serialVersionUID is compared to the serialVersionUID of the 626 | * serialized class, and if there is a mismatch, the deserialization fails 627 | * and an {@link InvalidClassException} is thrown. 628 | * 629 | *

The default implementation of this method in 630 | * ObjectInputStream returns the result of calling 631 | *

 632 |      *     Class.forName(desc.getName(), false, loader)
 633 |      * 
634 | * where loader is determined as follows: if there is a 635 | * method on the current thread's stack whose declaring class was 636 | * defined by a user-defined class loader (and was not a generated to 637 | * implement reflective invocations), then loader is class 638 | * loader corresponding to the closest such method to the currently 639 | * executing frame; otherwise, loader is 640 | * null. If this call results in a 641 | * ClassNotFoundException and the name of the passed 642 | * ObjectStreamClass instance is the Java language keyword 643 | * for a primitive type or void, then the Class object 644 | * representing that primitive type or void will be returned 645 | * (e.g., an ObjectStreamClass with the name 646 | * "int" will be resolved to Integer.TYPE). 647 | * Otherwise, the ClassNotFoundException will be thrown to 648 | * the caller of this method. 649 | * 650 | * @param desc an instance of class ObjectStreamClass 651 | * @return a Class object corresponding to desc 652 | * @throws IOException any of the usual Input/Output exceptions. 653 | * @throws ClassNotFoundException if class of a serialized object cannot 654 | * be found. 655 | */ 656 | protected Class resolveClass(ObjectStreamClass desc) 657 | throws IOException, ClassNotFoundException 658 | { 659 | String name = desc.getName(); 660 | logger.fine("resolveClass: resolving " + name); 661 | 662 | // From https://github.com/ikkisoft/SerialKiller by luca.carettoni@ikkisoft.com 663 | 664 | //Enforce blacklist 665 | for (Pattern blackPattern : blacklistPatterns) { 666 | Matcher blackMatcher = blackPattern.matcher(name); 667 | if (blackMatcher.find()) { 668 | logger.warning("resolveClass: rejecting blacklisted class " + name); 669 | String msg = "[!] Blocked by blacklist '" 670 | + blackPattern.pattern() + "'. Match found for '" + name + "'"; 671 | throw new InvalidClassException(msg); 672 | } 673 | } 674 | 675 | //Enforce whitelist if it exists. 676 | if (! whitelistPatterns.isEmpty()) { 677 | boolean safeClass = false; 678 | for (Pattern whitePattern: whitelistPatterns) { 679 | Matcher whiteMatcher = whitePattern.matcher(name); 680 | if (whiteMatcher.find()) { 681 | safeClass = true; 682 | break; 683 | } 684 | } 685 | 686 | if (!safeClass) { 687 | logger.warning("resolveClass: rejecting class " + name + " not found in whitelist."); 688 | String msg = "[!] Blocked by whitelist. No match found for '" + name + "'"; 689 | throw new InvalidClassException(msg); 690 | } 691 | } 692 | 693 | try { 694 | logger.fine("resolveClass: accepting class " + name); 695 | return Class.forName(name, false, latestUserDefinedLoader()); 696 | } catch (ClassNotFoundException ex) { 697 | Class cl = primClasses.get(name); 698 | if (cl != null) { 699 | return cl; 700 | } else { 701 | throw ex; 702 | } 703 | } 704 | } 705 | 706 | /** 707 | * Returns a proxy class that implements the interfaces named in a proxy 708 | * class descriptor; subclasses may implement this method to read custom 709 | * data from the stream along with the descriptors for dynamic proxy 710 | * classes, allowing them to use an alternate loading mechanism for the 711 | * interfaces and the proxy class. 712 | * 713 | *

This method is called exactly once for each unique proxy class 714 | * descriptor in the stream. 715 | * 716 | *

The corresponding method in ObjectOutputStream is 717 | * annotateProxyClass. For a given subclass of 718 | * ObjectInputStream that overrides this method, the 719 | * annotateProxyClass method in the corresponding subclass of 720 | * ObjectOutputStream must write any data or objects read by 721 | * this method. 722 | * 723 | *

The default implementation of this method in 724 | * ObjectInputStream returns the result of calling 725 | * Proxy.getProxyClass with the list of Class 726 | * objects for the interfaces that are named in the interfaces 727 | * parameter. The Class object for each interface name 728 | * i is the value returned by calling 729 | *

 730 |      *     Class.forName(i, false, loader)
 731 |      * 
732 | * where loader is that of the first non-null 733 | * class loader up the execution stack, or null if no 734 | * non-null class loaders are on the stack (the same class 735 | * loader choice used by the resolveClass method). Unless any 736 | * of the resolved interfaces are non-public, this same value of 737 | * loader is also the class loader passed to 738 | * Proxy.getProxyClass; if non-public interfaces are present, 739 | * their class loader is passed instead (if more than one non-public 740 | * interface class loader is encountered, an 741 | * IllegalAccessError is thrown). 742 | * If Proxy.getProxyClass throws an 743 | * IllegalArgumentException, resolveProxyClass 744 | * will throw a ClassNotFoundException containing the 745 | * IllegalArgumentException. 746 | * 747 | * @param interfaces the list of interface names that were 748 | * deserialized in the proxy class descriptor 749 | * @return a proxy class for the specified interfaces 750 | * @throws IOException any exception thrown by the underlying 751 | * InputStream 752 | * @throws ClassNotFoundException if the proxy class or any of the 753 | * named interfaces could not be found 754 | * @see ObjectOutputStream#annotateProxyClass(Class) 755 | * @since 1.3 756 | */ 757 | protected Class resolveProxyClass(String[] interfaces) 758 | throws IOException, ClassNotFoundException 759 | { 760 | ClassLoader latestLoader = latestUserDefinedLoader(); 761 | ClassLoader nonPublicLoader = null; 762 | boolean hasNonPublicInterface = false; 763 | 764 | // define proxy in class loader of non-public interface(s), if any 765 | Class[] classObjs = new Class[interfaces.length]; 766 | for (int i = 0; i < interfaces.length; i++) { 767 | Class cl = Class.forName(interfaces[i], false, latestLoader); 768 | if ((cl.getModifiers() & Modifier.PUBLIC) == 0) { 769 | if (hasNonPublicInterface) { 770 | if (nonPublicLoader != cl.getClassLoader()) { 771 | throw new IllegalAccessError( 772 | "conflicting non-public interface class loaders"); 773 | } 774 | } else { 775 | nonPublicLoader = cl.getClassLoader(); 776 | hasNonPublicInterface = true; 777 | } 778 | } 779 | classObjs[i] = cl; 780 | } 781 | try { 782 | return Proxy.getProxyClass( 783 | hasNonPublicInterface ? nonPublicLoader : latestLoader, 784 | classObjs); 785 | } catch (IllegalArgumentException e) { 786 | throw new ClassNotFoundException(null, e); 787 | } 788 | } 789 | 790 | /** 791 | * This method will allow trusted subclasses of ObjectInputStream to 792 | * substitute one object for another during deserialization. Replacing 793 | * objects is disabled until enableResolveObject is called. The 794 | * enableResolveObject method checks that the stream requesting to resolve 795 | * object can be trusted. Every reference to serializable objects is passed 796 | * to resolveObject. To insure that the private state of objects is not 797 | * unintentionally exposed only trusted streams may use resolveObject. 798 | * 799 | *

This method is called after an object has been read but before it is 800 | * returned from readObject. The default resolveObject method just returns 801 | * the same object. 802 | * 803 | *

When a subclass is replacing objects it must insure that the 804 | * substituted object is compatible with every field where the reference 805 | * will be stored. Objects whose type is not a subclass of the type of the 806 | * field or array element abort the serialization by raising an exception 807 | * and the object is not be stored. 808 | * 809 | *

This method is called only once when each object is first 810 | * encountered. All subsequent references to the object will be redirected 811 | * to the new object. 812 | * 813 | * @param obj object to be substituted 814 | * @return the substituted object 815 | * @throws IOException Any of the usual Input/Output exceptions. 816 | */ 817 | protected Object resolveObject(Object obj) throws IOException { 818 | return obj; 819 | } 820 | 821 | /** 822 | * Enable the stream to allow objects read from the stream to be replaced. 823 | * When enabled, the resolveObject method is called for every object being 824 | * deserialized. 825 | * 826 | *

If enable is true, and there is a security manager installed, 827 | * this method first calls the security manager's 828 | * checkPermission method with the 829 | * SerializablePermission("enableSubstitution") permission to 830 | * ensure it's ok to enable the stream to allow objects read from the 831 | * stream to be replaced. 832 | * 833 | * @param enable true for enabling use of resolveObject for 834 | * every object being deserialized 835 | * @return the previous setting before this method was invoked 836 | * @throws SecurityException if a security manager exists and its 837 | * checkPermission method denies enabling the stream 838 | * to allow objects read from the stream to be replaced. 839 | * @see SecurityManager#checkPermission 840 | * @see java.io.SerializablePermission 841 | */ 842 | protected boolean enableResolveObject(boolean enable) 843 | throws SecurityException 844 | { 845 | if (enable == enableResolve) { 846 | return enable; 847 | } 848 | if (enable) { 849 | SecurityManager sm = System.getSecurityManager(); 850 | if (sm != null) { 851 | sm.checkPermission(SUBSTITUTION_PERMISSION); 852 | } 853 | } 854 | enableResolve = enable; 855 | return !enableResolve; 856 | } 857 | 858 | /** 859 | * The readStreamHeader method is provided to allow subclasses to read and 860 | * verify their own stream headers. It reads and verifies the magic number 861 | * and version number. 862 | * 863 | * @throws IOException if there are I/O errors while reading from the 864 | * underlying InputStream 865 | * @throws StreamCorruptedException if control information in the stream 866 | * is inconsistent 867 | */ 868 | protected void readStreamHeader() 869 | throws IOException, StreamCorruptedException 870 | { 871 | short s0 = bin.readShort(); 872 | short s1 = bin.readShort(); 873 | if (s0 != STREAM_MAGIC || s1 != STREAM_VERSION) { 874 | throw new StreamCorruptedException( 875 | String.format("invalid stream header: %04X%04X", s0, s1)); 876 | } 877 | } 878 | 879 | /** 880 | * Read a class descriptor from the serialization stream. This method is 881 | * called when the ObjectInputStream expects a class descriptor as the next 882 | * item in the serialization stream. Subclasses of ObjectInputStream may 883 | * override this method to read in class descriptors that have been written 884 | * in non-standard formats (by subclasses of ObjectOutputStream which have 885 | * overridden the writeClassDescriptor method). By default, 886 | * this method reads class descriptors according to the format defined in 887 | * the Object Serialization specification. 888 | * 889 | * @return the class descriptor read 890 | * @throws IOException If an I/O error has occurred. 891 | * @throws ClassNotFoundException If the Class of a serialized object used 892 | * in the class descriptor representation cannot be found 893 | * @see java.io.ObjectOutputStream#writeClassDescriptor(java.io.ObjectStreamClass) 894 | * @since 1.3 895 | */ 896 | protected ObjectStreamClass readClassDescriptor() 897 | throws IOException, ClassNotFoundException 898 | { 899 | ObjectStreamClass desc = new ObjectStreamClass(); 900 | desc.readNonProxy(this); 901 | return desc; 902 | } 903 | 904 | /** 905 | * Reads a byte of data. This method will block if no input is available. 906 | * 907 | * @return the byte read, or -1 if the end of the stream is reached. 908 | * @throws IOException If an I/O error has occurred. 909 | */ 910 | public int read() throws IOException { 911 | return bin.read(); 912 | } 913 | 914 | /** 915 | * Reads into an array of bytes. This method will block until some input 916 | * is available. Consider using java.io.DataInputStream.readFully to read 917 | * exactly 'length' bytes. 918 | * 919 | * @param buf the buffer into which the data is read 920 | * @param off the start offset of the data 921 | * @param len the maximum number of bytes read 922 | * @return the actual number of bytes read, -1 is returned when the end of 923 | * the stream is reached. 924 | * @throws IOException If an I/O error has occurred. 925 | * @see java.io.DataInputStream#readFully(byte[],int,int) 926 | */ 927 | public int read(byte[] buf, int off, int len) throws IOException { 928 | if (buf == null) { 929 | throw new NullPointerException(); 930 | } 931 | int endoff = off + len; 932 | if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) { 933 | throw new IndexOutOfBoundsException(); 934 | } 935 | return bin.read(buf, off, len, false); 936 | } 937 | 938 | /** 939 | * Returns the number of bytes that can be read without blocking. 940 | * 941 | * @return the number of available bytes. 942 | * @throws IOException if there are I/O errors while reading from the 943 | * underlying InputStream 944 | */ 945 | public int available() throws IOException { 946 | return bin.available(); 947 | } 948 | 949 | /** 950 | * Closes the input stream. Must be called to release any resources 951 | * associated with the stream. 952 | * 953 | * @throws IOException If an I/O error has occurred. 954 | */ 955 | public void close() throws IOException { 956 | /* 957 | * Even if stream already closed, propagate redundant close to 958 | * underlying stream to stay consistent with previous implementations. 959 | */ 960 | closed = true; 961 | if (depth == 0) { 962 | clear(); 963 | } 964 | bin.close(); 965 | } 966 | 967 | /** 968 | * Reads in a boolean. 969 | * 970 | * @return the boolean read. 971 | * @throws EOFException If end of file is reached. 972 | * @throws IOException If other I/O error has occurred. 973 | */ 974 | public boolean readBoolean() throws IOException { 975 | return bin.readBoolean(); 976 | } 977 | 978 | /** 979 | * Reads an 8 bit byte. 980 | * 981 | * @return the 8 bit byte read. 982 | * @throws EOFException If end of file is reached. 983 | * @throws IOException If other I/O error has occurred. 984 | */ 985 | public byte readByte() throws IOException { 986 | return bin.readByte(); 987 | } 988 | 989 | /** 990 | * Reads an unsigned 8 bit byte. 991 | * 992 | * @return the 8 bit byte read. 993 | * @throws EOFException If end of file is reached. 994 | * @throws IOException If other I/O error has occurred. 995 | */ 996 | public int readUnsignedByte() throws IOException { 997 | return bin.readUnsignedByte(); 998 | } 999 | 1000 | /** 1001 | * Reads a 16 bit char. 1002 | * 1003 | * @return the 16 bit char read. 1004 | * @throws EOFException If end of file is reached. 1005 | * @throws IOException If other I/O error has occurred. 1006 | */ 1007 | public char readChar() throws IOException { 1008 | return bin.readChar(); 1009 | } 1010 | 1011 | /** 1012 | * Reads a 16 bit short. 1013 | * 1014 | * @return the 16 bit short read. 1015 | * @throws EOFException If end of file is reached. 1016 | * @throws IOException If other I/O error has occurred. 1017 | */ 1018 | public short readShort() throws IOException { 1019 | return bin.readShort(); 1020 | } 1021 | 1022 | /** 1023 | * Reads an unsigned 16 bit short. 1024 | * 1025 | * @return the 16 bit short read. 1026 | * @throws EOFException If end of file is reached. 1027 | * @throws IOException If other I/O error has occurred. 1028 | */ 1029 | public int readUnsignedShort() throws IOException { 1030 | return bin.readUnsignedShort(); 1031 | } 1032 | 1033 | /** 1034 | * Reads a 32 bit int. 1035 | * 1036 | * @return the 32 bit integer read. 1037 | * @throws EOFException If end of file is reached. 1038 | * @throws IOException If other I/O error has occurred. 1039 | */ 1040 | public int readInt() throws IOException { 1041 | return bin.readInt(); 1042 | } 1043 | 1044 | /** 1045 | * Reads a 64 bit long. 1046 | * 1047 | * @return the read 64 bit long. 1048 | * @throws EOFException If end of file is reached. 1049 | * @throws IOException If other I/O error has occurred. 1050 | */ 1051 | public long readLong() throws IOException { 1052 | return bin.readLong(); 1053 | } 1054 | 1055 | /** 1056 | * Reads a 32 bit float. 1057 | * 1058 | * @return the 32 bit float read. 1059 | * @throws EOFException If end of file is reached. 1060 | * @throws IOException If other I/O error has occurred. 1061 | */ 1062 | public float readFloat() throws IOException { 1063 | return bin.readFloat(); 1064 | } 1065 | 1066 | /** 1067 | * Reads a 64 bit double. 1068 | * 1069 | * @return the 64 bit double read. 1070 | * @throws EOFException If end of file is reached. 1071 | * @throws IOException If other I/O error has occurred. 1072 | */ 1073 | public double readDouble() throws IOException { 1074 | return bin.readDouble(); 1075 | } 1076 | 1077 | /** 1078 | * Reads bytes, blocking until all bytes are read. 1079 | * 1080 | * @param buf the buffer into which the data is read 1081 | * @throws EOFException If end of file is reached. 1082 | * @throws IOException If other I/O error has occurred. 1083 | */ 1084 | public void readFully(byte[] buf) throws IOException { 1085 | bin.readFully(buf, 0, buf.length, false); 1086 | } 1087 | 1088 | /** 1089 | * Reads bytes, blocking until all bytes are read. 1090 | * 1091 | * @param buf the buffer into which the data is read 1092 | * @param off the start offset of the data 1093 | * @param len the maximum number of bytes to read 1094 | * @throws EOFException If end of file is reached. 1095 | * @throws IOException If other I/O error has occurred. 1096 | */ 1097 | public void readFully(byte[] buf, int off, int len) throws IOException { 1098 | int endoff = off + len; 1099 | if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) { 1100 | throw new IndexOutOfBoundsException(); 1101 | } 1102 | bin.readFully(buf, off, len, false); 1103 | } 1104 | 1105 | /** 1106 | * Skips bytes. 1107 | * 1108 | * @param len the number of bytes to be skipped 1109 | * @return the actual number of bytes skipped. 1110 | * @throws IOException If an I/O error has occurred. 1111 | */ 1112 | public int skipBytes(int len) throws IOException { 1113 | return bin.skipBytes(len); 1114 | } 1115 | 1116 | /** 1117 | * Reads in a line that has been terminated by a \n, \r, \r\n or EOF. 1118 | * 1119 | * @return a String copy of the line. 1120 | * @throws IOException if there are I/O errors while reading from the 1121 | * underlying InputStream 1122 | * @deprecated This method does not properly convert bytes to characters. 1123 | * see DataInputStream for the details and alternatives. 1124 | */ 1125 | @Deprecated 1126 | public String readLine() throws IOException { 1127 | return bin.readLine(); 1128 | } 1129 | 1130 | /** 1131 | * Reads a String in 1132 | * modified UTF-8 1133 | * format. 1134 | * 1135 | * @return the String. 1136 | * @throws IOException if there are I/O errors while reading from the 1137 | * underlying InputStream 1138 | * @throws UTFDataFormatException if read bytes do not represent a valid 1139 | * modified UTF-8 encoding of a string 1140 | */ 1141 | public String readUTF() throws IOException { 1142 | return bin.readUTF(); 1143 | } 1144 | 1145 | /** 1146 | * Provide access to the persistent fields read from the input stream. 1147 | */ 1148 | public static abstract class GetField { 1149 | 1150 | /** 1151 | * Get the ObjectStreamClass that describes the fields in the stream. 1152 | * 1153 | * @return the descriptor class that describes the serializable fields 1154 | */ 1155 | public abstract ObjectStreamClass getObjectStreamClass(); 1156 | 1157 | /** 1158 | * Return true if the named field is defaulted and has no value in this 1159 | * stream. 1160 | * 1161 | * @param name the name of the field 1162 | * @return true, if and only if the named field is defaulted 1163 | * @throws IOException if there are I/O errors while reading from 1164 | * the underlying InputStream 1165 | * @throws IllegalArgumentException if name does not 1166 | * correspond to a serializable field 1167 | */ 1168 | public abstract boolean defaulted(String name) throws IOException; 1169 | 1170 | /** 1171 | * Get the value of the named boolean field from the persistent field. 1172 | * 1173 | * @param name the name of the field 1174 | * @param val the default value to use if name does not 1175 | * have a value 1176 | * @return the value of the named boolean field 1177 | * @throws IOException if there are I/O errors while reading from the 1178 | * underlying InputStream 1179 | * @throws IllegalArgumentException if type of name is 1180 | * not serializable or if the field type is incorrect 1181 | */ 1182 | public abstract boolean get(String name, boolean val) 1183 | throws IOException; 1184 | 1185 | /** 1186 | * Get the value of the named byte field from the persistent field. 1187 | * 1188 | * @param name the name of the field 1189 | * @param val the default value to use if name does not 1190 | * have a value 1191 | * @return the value of the named byte field 1192 | * @throws IOException if there are I/O errors while reading from the 1193 | * underlying InputStream 1194 | * @throws IllegalArgumentException if type of name is 1195 | * not serializable or if the field type is incorrect 1196 | */ 1197 | public abstract byte get(String name, byte val) throws IOException; 1198 | 1199 | /** 1200 | * Get the value of the named char field from the persistent field. 1201 | * 1202 | * @param name the name of the field 1203 | * @param val the default value to use if name does not 1204 | * have a value 1205 | * @return the value of the named char field 1206 | * @throws IOException if there are I/O errors while reading from the 1207 | * underlying InputStream 1208 | * @throws IllegalArgumentException if type of name is 1209 | * not serializable or if the field type is incorrect 1210 | */ 1211 | public abstract char get(String name, char val) throws IOException; 1212 | 1213 | /** 1214 | * Get the value of the named short field from the persistent field. 1215 | * 1216 | * @param name the name of the field 1217 | * @param val the default value to use if name does not 1218 | * have a value 1219 | * @return the value of the named short field 1220 | * @throws IOException if there are I/O errors while reading from the 1221 | * underlying InputStream 1222 | * @throws IllegalArgumentException if type of name is 1223 | * not serializable or if the field type is incorrect 1224 | */ 1225 | public abstract short get(String name, short val) throws IOException; 1226 | 1227 | /** 1228 | * Get the value of the named int field from the persistent field. 1229 | * 1230 | * @param name the name of the field 1231 | * @param val the default value to use if name does not 1232 | * have a value 1233 | * @return the value of the named int field 1234 | * @throws IOException if there are I/O errors while reading from the 1235 | * underlying InputStream 1236 | * @throws IllegalArgumentException if type of name is 1237 | * not serializable or if the field type is incorrect 1238 | */ 1239 | public abstract int get(String name, int val) throws IOException; 1240 | 1241 | /** 1242 | * Get the value of the named long field from the persistent field. 1243 | * 1244 | * @param name the name of the field 1245 | * @param val the default value to use if name does not 1246 | * have a value 1247 | * @return the value of the named long field 1248 | * @throws IOException if there are I/O errors while reading from the 1249 | * underlying InputStream 1250 | * @throws IllegalArgumentException if type of name is 1251 | * not serializable or if the field type is incorrect 1252 | */ 1253 | public abstract long get(String name, long val) throws IOException; 1254 | 1255 | /** 1256 | * Get the value of the named float field from the persistent field. 1257 | * 1258 | * @param name the name of the field 1259 | * @param val the default value to use if name does not 1260 | * have a value 1261 | * @return the value of the named float field 1262 | * @throws IOException if there are I/O errors while reading from the 1263 | * underlying InputStream 1264 | * @throws IllegalArgumentException if type of name is 1265 | * not serializable or if the field type is incorrect 1266 | */ 1267 | public abstract float get(String name, float val) throws IOException; 1268 | 1269 | /** 1270 | * Get the value of the named double field from the persistent field. 1271 | * 1272 | * @param name the name of the field 1273 | * @param val the default value to use if name does not 1274 | * have a value 1275 | * @return the value of the named double field 1276 | * @throws IOException if there are I/O errors while reading from the 1277 | * underlying InputStream 1278 | * @throws IllegalArgumentException if type of name is 1279 | * not serializable or if the field type is incorrect 1280 | */ 1281 | public abstract double get(String name, double val) throws IOException; 1282 | 1283 | /** 1284 | * Get the value of the named Object field from the persistent field. 1285 | * 1286 | * @param name the name of the field 1287 | * @param val the default value to use if name does not 1288 | * have a value 1289 | * @return the value of the named Object field 1290 | * @throws IOException if there are I/O errors while reading from the 1291 | * underlying InputStream 1292 | * @throws IllegalArgumentException if type of name is 1293 | * not serializable or if the field type is incorrect 1294 | */ 1295 | public abstract Object get(String name, Object val) throws IOException; 1296 | } 1297 | 1298 | /** 1299 | * Verifies that this (possibly subclass) instance can be constructed 1300 | * without violating security constraints: the subclass must not override 1301 | * security-sensitive non-final methods, or else the 1302 | * "enableSubclassImplementation" SerializablePermission is checked. 1303 | */ 1304 | private void verifySubclass() { 1305 | Class cl = getClass(); 1306 | if (cl == ObjectInputStream.class) { 1307 | return; 1308 | } 1309 | SecurityManager sm = System.getSecurityManager(); 1310 | if (sm == null) { 1311 | return; 1312 | } 1313 | processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits); 1314 | WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue); 1315 | Boolean result = Caches.subclassAudits.get(key); 1316 | if (result == null) { 1317 | result = Boolean.valueOf(auditSubclass(cl)); 1318 | Caches.subclassAudits.putIfAbsent(key, result); 1319 | } 1320 | if (result.booleanValue()) { 1321 | return; 1322 | } 1323 | sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); 1324 | } 1325 | 1326 | /** 1327 | * Performs reflective checks on given subclass to verify that it doesn't 1328 | * override security-sensitive non-final methods. Returns true if subclass 1329 | * is "safe", false otherwise. 1330 | */ 1331 | private static boolean auditSubclass(final Class subcl) { 1332 | Boolean result = AccessController.doPrivileged( 1333 | new PrivilegedAction() { 1334 | public Boolean run() { 1335 | for (Class cl = subcl; 1336 | cl != ObjectInputStream.class; 1337 | cl = cl.getSuperclass()) 1338 | { 1339 | try { 1340 | cl.getDeclaredMethod( 1341 | "readUnshared", (Class[]) null); 1342 | return Boolean.FALSE; 1343 | } catch (NoSuchMethodException ex) { 1344 | } 1345 | try { 1346 | cl.getDeclaredMethod("readFields", (Class[]) null); 1347 | return Boolean.FALSE; 1348 | } catch (NoSuchMethodException ex) { 1349 | } 1350 | } 1351 | return Boolean.TRUE; 1352 | } 1353 | } 1354 | ); 1355 | return result.booleanValue(); 1356 | } 1357 | 1358 | /** 1359 | * Clears internal data structures. 1360 | */ 1361 | private void clear() { 1362 | handles.clear(); 1363 | vlist.clear(); 1364 | } 1365 | 1366 | /** 1367 | * Underlying readObject implementation. 1368 | */ 1369 | private Object readObject0(boolean unshared) throws IOException { 1370 | boolean oldMode = bin.getBlockDataMode(); 1371 | if (oldMode) { 1372 | int remain = bin.currentBlockRemaining(); 1373 | if (remain > 0) { 1374 | throw new OptionalDataException(remain); 1375 | } else if (defaultDataEnd) { 1376 | /* 1377 | * Fix for 4360508: stream is currently at the end of a field 1378 | * value block written via default serialization; since there 1379 | * is no terminating TC_ENDBLOCKDATA tag, simulate 1380 | * end-of-custom-data behavior explicitly. 1381 | */ 1382 | throw new OptionalDataException(true); 1383 | } 1384 | bin.setBlockDataMode(false); 1385 | } 1386 | 1387 | byte tc; 1388 | while ((tc = bin.peekByte()) == TC_RESET) { 1389 | bin.readByte(); 1390 | handleReset(); 1391 | } 1392 | 1393 | depth++; 1394 | try { 1395 | switch (tc) { 1396 | case TC_NULL: 1397 | return readNull(); 1398 | 1399 | case TC_REFERENCE: 1400 | return readHandle(unshared); 1401 | 1402 | case TC_CLASS: 1403 | return readClass(unshared); 1404 | 1405 | case TC_CLASSDESC: 1406 | case TC_PROXYCLASSDESC: 1407 | return readClassDesc(unshared); 1408 | 1409 | case TC_STRING: 1410 | case TC_LONGSTRING: 1411 | return checkResolve(readString(unshared)); 1412 | 1413 | case TC_ARRAY: 1414 | return checkResolve(readArray(unshared)); 1415 | 1416 | case TC_ENUM: 1417 | return checkResolve(readEnum(unshared)); 1418 | 1419 | case TC_OBJECT: 1420 | return checkResolve(readOrdinaryObject(unshared)); 1421 | 1422 | case TC_EXCEPTION: 1423 | IOException ex = readFatalException(); 1424 | throw new WriteAbortedException("writing aborted", ex); 1425 | 1426 | case TC_BLOCKDATA: 1427 | case TC_BLOCKDATALONG: 1428 | if (oldMode) { 1429 | bin.setBlockDataMode(true); 1430 | bin.peek(); // force header read 1431 | throw new OptionalDataException( 1432 | bin.currentBlockRemaining()); 1433 | } else { 1434 | throw new StreamCorruptedException( 1435 | "unexpected block data"); 1436 | } 1437 | 1438 | case TC_ENDBLOCKDATA: 1439 | if (oldMode) { 1440 | throw new OptionalDataException(true); 1441 | } else { 1442 | throw new StreamCorruptedException( 1443 | "unexpected end of block data"); 1444 | } 1445 | 1446 | default: 1447 | throw new StreamCorruptedException( 1448 | String.format("invalid type code: %02X", tc)); 1449 | } 1450 | } finally { 1451 | depth--; 1452 | bin.setBlockDataMode(oldMode); 1453 | } 1454 | } 1455 | 1456 | /** 1457 | * If resolveObject has been enabled and given object does not have an 1458 | * exception associated with it, calls resolveObject to determine 1459 | * replacement for object, and updates handle table accordingly. Returns 1460 | * replacement object, or echoes provided object if no replacement 1461 | * occurred. Expects that passHandle is set to given object's handle prior 1462 | * to calling this method. 1463 | */ 1464 | private Object checkResolve(Object obj) throws IOException { 1465 | if (!enableResolve || handles.lookupException(passHandle) != null) { 1466 | return obj; 1467 | } 1468 | Object rep = resolveObject(obj); 1469 | if (rep != obj) { 1470 | handles.setObject(passHandle, rep); 1471 | } 1472 | return rep; 1473 | } 1474 | 1475 | /** 1476 | * Reads string without allowing it to be replaced in stream. Called from 1477 | * within ObjectStreamClass.read(). 1478 | */ 1479 | String readTypeString() throws IOException { 1480 | int oldHandle = passHandle; 1481 | try { 1482 | byte tc = bin.peekByte(); 1483 | switch (tc) { 1484 | case TC_NULL: 1485 | return (String) readNull(); 1486 | 1487 | case TC_REFERENCE: 1488 | return (String) readHandle(false); 1489 | 1490 | case TC_STRING: 1491 | case TC_LONGSTRING: 1492 | return readString(false); 1493 | 1494 | default: 1495 | throw new StreamCorruptedException( 1496 | String.format("invalid type code: %02X", tc)); 1497 | } 1498 | } finally { 1499 | passHandle = oldHandle; 1500 | } 1501 | } 1502 | 1503 | /** 1504 | * Reads in null code, sets passHandle to NULL_HANDLE and returns null. 1505 | */ 1506 | private Object readNull() throws IOException { 1507 | if (bin.readByte() != TC_NULL) { 1508 | throw new InternalError(); 1509 | } 1510 | passHandle = NULL_HANDLE; 1511 | return null; 1512 | } 1513 | 1514 | /** 1515 | * Reads in object handle, sets passHandle to the read handle, and returns 1516 | * object associated with the handle. 1517 | */ 1518 | private Object readHandle(boolean unshared) throws IOException { 1519 | if (bin.readByte() != TC_REFERENCE) { 1520 | throw new InternalError(); 1521 | } 1522 | passHandle = bin.readInt() - baseWireHandle; 1523 | if (passHandle < 0 || passHandle >= handles.size()) { 1524 | throw new StreamCorruptedException( 1525 | String.format("invalid handle value: %08X", passHandle + 1526 | baseWireHandle)); 1527 | } 1528 | if (unshared) { 1529 | // REMIND: what type of exception to throw here? 1530 | throw new InvalidObjectException( 1531 | "cannot read back reference as unshared"); 1532 | } 1533 | 1534 | Object obj = handles.lookupObject(passHandle); 1535 | if (obj == unsharedMarker) { 1536 | // REMIND: what type of exception to throw here? 1537 | throw new InvalidObjectException( 1538 | "cannot read back reference to unshared object"); 1539 | } 1540 | return obj; 1541 | } 1542 | 1543 | /** 1544 | * Reads in and returns class object. Sets passHandle to class object's 1545 | * assigned handle. Returns null if class is unresolvable (in which case a 1546 | * ClassNotFoundException will be associated with the class' handle in the 1547 | * handle table). 1548 | */ 1549 | private Class readClass(boolean unshared) throws IOException { 1550 | if (bin.readByte() != TC_CLASS) { 1551 | throw new InternalError(); 1552 | } 1553 | ObjectStreamClass desc = readClassDesc(false); 1554 | Class cl = desc.forClass(); 1555 | passHandle = handles.assign(unshared ? unsharedMarker : cl); 1556 | 1557 | ClassNotFoundException resolveEx = desc.getResolveException(); 1558 | if (resolveEx != null) { 1559 | handles.markException(passHandle, resolveEx); 1560 | } 1561 | 1562 | handles.finish(passHandle); 1563 | return cl; 1564 | } 1565 | 1566 | /** 1567 | * Reads in and returns (possibly null) class descriptor. Sets passHandle 1568 | * to class descriptor's assigned handle. If class descriptor cannot be 1569 | * resolved to a class in the local VM, a ClassNotFoundException is 1570 | * associated with the class descriptor's handle. 1571 | */ 1572 | private ObjectStreamClass readClassDesc(boolean unshared) 1573 | throws IOException 1574 | { 1575 | byte tc = bin.peekByte(); 1576 | switch (tc) { 1577 | case TC_NULL: 1578 | return (ObjectStreamClass) readNull(); 1579 | 1580 | case TC_REFERENCE: 1581 | return (ObjectStreamClass) readHandle(unshared); 1582 | 1583 | case TC_PROXYCLASSDESC: 1584 | return readProxyDesc(unshared); 1585 | 1586 | case TC_CLASSDESC: 1587 | return readNonProxyDesc(unshared); 1588 | 1589 | default: 1590 | throw new StreamCorruptedException( 1591 | String.format("invalid type code: %02X", tc)); 1592 | } 1593 | } 1594 | 1595 | private boolean isCustomSubclass() { 1596 | // Return true if this class is a custom subclass of ObjectInputStream 1597 | return getClass().getClassLoader() 1598 | != ObjectInputStream.class.getClassLoader(); 1599 | } 1600 | 1601 | /** 1602 | * Reads in and returns class descriptor for a dynamic proxy class. Sets 1603 | * passHandle to proxy class descriptor's assigned handle. If proxy class 1604 | * descriptor cannot be resolved to a class in the local VM, a 1605 | * ClassNotFoundException is associated with the descriptor's handle. 1606 | */ 1607 | private ObjectStreamClass readProxyDesc(boolean unshared) 1608 | throws IOException 1609 | { 1610 | if (bin.readByte() != TC_PROXYCLASSDESC) { 1611 | throw new InternalError(); 1612 | } 1613 | 1614 | ObjectStreamClass desc = new ObjectStreamClass(); 1615 | int descHandle = handles.assign(unshared ? unsharedMarker : desc); 1616 | passHandle = NULL_HANDLE; 1617 | 1618 | int numIfaces = bin.readInt(); 1619 | String[] ifaces = new String[numIfaces]; 1620 | for (int i = 0; i < numIfaces; i++) { 1621 | ifaces[i] = bin.readUTF(); 1622 | } 1623 | 1624 | Class cl = null; 1625 | ClassNotFoundException resolveEx = null; 1626 | bin.setBlockDataMode(true); 1627 | try { 1628 | if ((cl = resolveProxyClass(ifaces)) == null) { 1629 | resolveEx = new ClassNotFoundException("null class"); 1630 | } else if (!Proxy.isProxyClass(cl)) { 1631 | throw new InvalidClassException("Not a proxy"); 1632 | } else { 1633 | // ReflectUtil.checkProxyPackageAccess makes a test 1634 | // equivalent to isCustomSubclass so there's no need 1635 | // to condition this call to isCustomSubclass == true here. 1636 | ReflectUtil.checkProxyPackageAccess( 1637 | getClass().getClassLoader(), 1638 | cl.getInterfaces()); 1639 | } 1640 | } catch (ClassNotFoundException ex) { 1641 | resolveEx = ex; 1642 | } 1643 | skipCustomData(); 1644 | 1645 | desc.initProxy(cl, resolveEx, readClassDesc(false)); 1646 | 1647 | handles.finish(descHandle); 1648 | passHandle = descHandle; 1649 | return desc; 1650 | } 1651 | 1652 | /** 1653 | * Reads in and returns class descriptor for a class that is not a dynamic 1654 | * proxy class. Sets passHandle to class descriptor's assigned handle. If 1655 | * class descriptor cannot be resolved to a class in the local VM, a 1656 | * ClassNotFoundException is associated with the descriptor's handle. 1657 | */ 1658 | private ObjectStreamClass readNonProxyDesc(boolean unshared) 1659 | throws IOException 1660 | { 1661 | if (bin.readByte() != TC_CLASSDESC) { 1662 | throw new InternalError(); 1663 | } 1664 | 1665 | ObjectStreamClass desc = new ObjectStreamClass(); 1666 | int descHandle = handles.assign(unshared ? unsharedMarker : desc); 1667 | passHandle = NULL_HANDLE; 1668 | 1669 | ObjectStreamClass readDesc = null; 1670 | try { 1671 | readDesc = readClassDescriptor(); 1672 | } catch (ClassNotFoundException ex) { 1673 | throw (IOException) new InvalidClassException( 1674 | "failed to read class descriptor").initCause(ex); 1675 | } 1676 | 1677 | Class cl = null; 1678 | ClassNotFoundException resolveEx = null; 1679 | bin.setBlockDataMode(true); 1680 | final boolean checksRequired = isCustomSubclass(); 1681 | try { 1682 | if ((cl = resolveClass(readDesc)) == null) { 1683 | resolveEx = new ClassNotFoundException("null class"); 1684 | } else if (checksRequired) { 1685 | ReflectUtil.checkPackageAccess(cl); 1686 | } 1687 | } catch (ClassNotFoundException ex) { 1688 | resolveEx = ex; 1689 | } 1690 | skipCustomData(); 1691 | 1692 | desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false)); 1693 | 1694 | handles.finish(descHandle); 1695 | passHandle = descHandle; 1696 | return desc; 1697 | } 1698 | 1699 | /** 1700 | * Reads in and returns new string. Sets passHandle to new string's 1701 | * assigned handle. 1702 | */ 1703 | private String readString(boolean unshared) throws IOException { 1704 | String str; 1705 | byte tc = bin.readByte(); 1706 | switch (tc) { 1707 | case TC_STRING: 1708 | str = bin.readUTF(); 1709 | break; 1710 | 1711 | case TC_LONGSTRING: 1712 | str = bin.readLongUTF(); 1713 | break; 1714 | 1715 | default: 1716 | throw new StreamCorruptedException( 1717 | String.format("invalid type code: %02X", tc)); 1718 | } 1719 | passHandle = handles.assign(unshared ? unsharedMarker : str); 1720 | handles.finish(passHandle); 1721 | return str; 1722 | } 1723 | 1724 | /** 1725 | * Reads in and returns array object, or null if array class is 1726 | * unresolvable. Sets passHandle to array's assigned handle. 1727 | */ 1728 | private Object readArray(boolean unshared) throws IOException { 1729 | if (bin.readByte() != TC_ARRAY) { 1730 | throw new InternalError(); 1731 | } 1732 | 1733 | ObjectStreamClass desc = readClassDesc(false); 1734 | int len = bin.readInt(); 1735 | 1736 | Object array = null; 1737 | Class cl, ccl = null; 1738 | if ((cl = desc.forClass()) != null) { 1739 | ccl = cl.getComponentType(); 1740 | array = Array.newInstance(ccl, len); 1741 | } 1742 | 1743 | int arrayHandle = handles.assign(unshared ? unsharedMarker : array); 1744 | ClassNotFoundException resolveEx = desc.getResolveException(); 1745 | if (resolveEx != null) { 1746 | handles.markException(arrayHandle, resolveEx); 1747 | } 1748 | 1749 | if (ccl == null) { 1750 | for (int i = 0; i < len; i++) { 1751 | readObject0(false); 1752 | } 1753 | } else if (ccl.isPrimitive()) { 1754 | if (ccl == Integer.TYPE) { 1755 | bin.readInts((int[]) array, 0, len); 1756 | } else if (ccl == Byte.TYPE) { 1757 | bin.readFully((byte[]) array, 0, len, true); 1758 | } else if (ccl == Long.TYPE) { 1759 | bin.readLongs((long[]) array, 0, len); 1760 | } else if (ccl == Float.TYPE) { 1761 | bin.readFloats((float[]) array, 0, len); 1762 | } else if (ccl == Double.TYPE) { 1763 | bin.readDoubles((double[]) array, 0, len); 1764 | } else if (ccl == Short.TYPE) { 1765 | bin.readShorts((short[]) array, 0, len); 1766 | } else if (ccl == Character.TYPE) { 1767 | bin.readChars((char[]) array, 0, len); 1768 | } else if (ccl == Boolean.TYPE) { 1769 | bin.readBooleans((boolean[]) array, 0, len); 1770 | } else { 1771 | throw new InternalError(); 1772 | } 1773 | } else { 1774 | Object[] oa = (Object[]) array; 1775 | for (int i = 0; i < len; i++) { 1776 | oa[i] = readObject0(false); 1777 | handles.markDependency(arrayHandle, passHandle); 1778 | } 1779 | } 1780 | 1781 | handles.finish(arrayHandle); 1782 | passHandle = arrayHandle; 1783 | return array; 1784 | } 1785 | 1786 | /** 1787 | * Reads in and returns enum constant, or null if enum type is 1788 | * unresolvable. Sets passHandle to enum constant's assigned handle. 1789 | */ 1790 | private Enum readEnum(boolean unshared) throws IOException { 1791 | if (bin.readByte() != TC_ENUM) { 1792 | throw new InternalError(); 1793 | } 1794 | 1795 | ObjectStreamClass desc = readClassDesc(false); 1796 | if (!desc.isEnum()) { 1797 | throw new InvalidClassException("non-enum class: " + desc); 1798 | } 1799 | 1800 | int enumHandle = handles.assign(unshared ? unsharedMarker : null); 1801 | ClassNotFoundException resolveEx = desc.getResolveException(); 1802 | if (resolveEx != null) { 1803 | handles.markException(enumHandle, resolveEx); 1804 | } 1805 | 1806 | String name = readString(false); 1807 | Enum result = null; 1808 | Class cl = desc.forClass(); 1809 | if (cl != null) { 1810 | try { 1811 | @SuppressWarnings("unchecked") 1812 | Enum en = Enum.valueOf((Class)cl, name); 1813 | result = en; 1814 | } catch (IllegalArgumentException ex) { 1815 | throw (IOException) new InvalidObjectException( 1816 | "enum constant " + name + " does not exist in " + 1817 | cl).initCause(ex); 1818 | } 1819 | if (!unshared) { 1820 | handles.setObject(enumHandle, result); 1821 | } 1822 | } 1823 | 1824 | handles.finish(enumHandle); 1825 | passHandle = enumHandle; 1826 | return result; 1827 | } 1828 | 1829 | /** 1830 | * Reads and returns "ordinary" (i.e., not a String, Class, 1831 | * ObjectStreamClass, array, or enum constant) object, or null if object's 1832 | * class is unresolvable (in which case a ClassNotFoundException will be 1833 | * associated with object's handle). Sets passHandle to object's assigned 1834 | * handle. 1835 | */ 1836 | private Object readOrdinaryObject(boolean unshared) 1837 | throws IOException 1838 | { 1839 | if (bin.readByte() != TC_OBJECT) { 1840 | throw new InternalError(); 1841 | } 1842 | 1843 | ObjectStreamClass desc = readClassDesc(false); 1844 | desc.checkDeserialize(); 1845 | 1846 | Class cl = desc.forClass(); 1847 | if (cl == String.class || cl == Class.class 1848 | || cl == ObjectStreamClass.class) { 1849 | throw new InvalidClassException("invalid class descriptor"); 1850 | } 1851 | 1852 | Object obj; 1853 | try { 1854 | obj = desc.isInstantiable() ? desc.newInstance() : null; 1855 | } catch (Exception ex) { 1856 | throw (IOException) new InvalidClassException( 1857 | desc.forClass().getName(), 1858 | "unable to create instance").initCause(ex); 1859 | } 1860 | 1861 | passHandle = handles.assign(unshared ? unsharedMarker : obj); 1862 | ClassNotFoundException resolveEx = desc.getResolveException(); 1863 | if (resolveEx != null) { 1864 | handles.markException(passHandle, resolveEx); 1865 | } 1866 | 1867 | if (desc.isExternalizable()) { 1868 | readExternalData((Externalizable) obj, desc); 1869 | } else { 1870 | readSerialData(obj, desc); 1871 | } 1872 | 1873 | handles.finish(passHandle); 1874 | 1875 | if (obj != null && 1876 | handles.lookupException(passHandle) == null && 1877 | desc.hasReadResolveMethod()) 1878 | { 1879 | Object rep = desc.invokeReadResolve(obj); 1880 | if (unshared && rep.getClass().isArray()) { 1881 | rep = cloneArray(rep); 1882 | } 1883 | if (rep != obj) { 1884 | handles.setObject(passHandle, obj = rep); 1885 | } 1886 | } 1887 | 1888 | return obj; 1889 | } 1890 | 1891 | /** 1892 | * If obj is non-null, reads externalizable data by invoking readExternal() 1893 | * method of obj; otherwise, attempts to skip over externalizable data. 1894 | * Expects that passHandle is set to obj's handle before this method is 1895 | * called. 1896 | */ 1897 | private void readExternalData(Externalizable obj, ObjectStreamClass desc) 1898 | throws IOException 1899 | { 1900 | SerialCallbackContext oldContext = curContext; 1901 | if (oldContext != null) 1902 | oldContext.check(); 1903 | curContext = null; 1904 | try { 1905 | boolean blocked = desc.hasBlockExternalData(); 1906 | if (blocked) { 1907 | bin.setBlockDataMode(true); 1908 | } 1909 | if (obj != null) { 1910 | try { 1911 | obj.readExternal(this); 1912 | } catch (ClassNotFoundException ex) { 1913 | /* 1914 | * In most cases, the handle table has already propagated 1915 | * a CNFException to passHandle at this point; this mark 1916 | * call is included to address cases where the readExternal 1917 | * method has cons'ed and thrown a new CNFException of its 1918 | * own. 1919 | */ 1920 | handles.markException(passHandle, ex); 1921 | } 1922 | } 1923 | if (blocked) { 1924 | skipCustomData(); 1925 | } 1926 | } finally { 1927 | if (oldContext != null) 1928 | oldContext.check(); 1929 | curContext = oldContext; 1930 | } 1931 | /* 1932 | * At this point, if the externalizable data was not written in 1933 | * block-data form and either the externalizable class doesn't exist 1934 | * locally (i.e., obj == null) or readExternal() just threw a 1935 | * CNFException, then the stream is probably in an inconsistent state, 1936 | * since some (or all) of the externalizable data may not have been 1937 | * consumed. Since there's no "correct" action to take in this case, 1938 | * we mimic the behavior of past serialization implementations and 1939 | * blindly hope that the stream is in sync; if it isn't and additional 1940 | * externalizable data remains in the stream, a subsequent read will 1941 | * most likely throw a StreamCorruptedException. 1942 | */ 1943 | } 1944 | 1945 | /** 1946 | * Reads (or attempts to skip, if obj is null or is tagged with a 1947 | * ClassNotFoundException) instance data for each serializable class of 1948 | * object in stream, from superclass to subclass. Expects that passHandle 1949 | * is set to obj's handle before this method is called. 1950 | */ 1951 | private void readSerialData(Object obj, ObjectStreamClass desc) 1952 | throws IOException 1953 | { 1954 | ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout(); 1955 | for (int i = 0; i < slots.length; i++) { 1956 | ObjectStreamClass slotDesc = slots[i].desc; 1957 | 1958 | if (slots[i].hasData) { 1959 | if (obj == null || handles.lookupException(passHandle) != null) { 1960 | defaultReadFields(null, slotDesc); // skip field values 1961 | } else if (slotDesc.hasReadObjectMethod()) { 1962 | SerialCallbackContext oldContext = curContext; 1963 | if (oldContext != null) 1964 | oldContext.check(); 1965 | try { 1966 | curContext = new SerialCallbackContext(obj, slotDesc); 1967 | 1968 | bin.setBlockDataMode(true); 1969 | slotDesc.invokeReadObject(obj, this); 1970 | } catch (ClassNotFoundException ex) { 1971 | /* 1972 | * In most cases, the handle table has already 1973 | * propagated a CNFException to passHandle at this 1974 | * point; this mark call is included to address cases 1975 | * where the custom readObject method has cons'ed and 1976 | * thrown a new CNFException of its own. 1977 | */ 1978 | handles.markException(passHandle, ex); 1979 | } finally { 1980 | curContext.setUsed(); 1981 | if (oldContext!= null) 1982 | oldContext.check(); 1983 | curContext = oldContext; 1984 | } 1985 | 1986 | /* 1987 | * defaultDataEnd may have been set indirectly by custom 1988 | * readObject() method when calling defaultReadObject() or 1989 | * readFields(); clear it to restore normal read behavior. 1990 | */ 1991 | defaultDataEnd = false; 1992 | } else { 1993 | defaultReadFields(obj, slotDesc); 1994 | } 1995 | 1996 | if (slotDesc.hasWriteObjectData()) { 1997 | skipCustomData(); 1998 | } else { 1999 | bin.setBlockDataMode(false); 2000 | } 2001 | } else { 2002 | if (obj != null && 2003 | slotDesc.hasReadObjectNoDataMethod() && 2004 | handles.lookupException(passHandle) == null) 2005 | { 2006 | slotDesc.invokeReadObjectNoData(obj); 2007 | } 2008 | } 2009 | } 2010 | } 2011 | 2012 | /** 2013 | * Skips over all block data and objects until TC_ENDBLOCKDATA is 2014 | * encountered. 2015 | */ 2016 | private void skipCustomData() throws IOException { 2017 | int oldHandle = passHandle; 2018 | for (;;) { 2019 | if (bin.getBlockDataMode()) { 2020 | bin.skipBlockData(); 2021 | bin.setBlockDataMode(false); 2022 | } 2023 | switch (bin.peekByte()) { 2024 | case TC_BLOCKDATA: 2025 | case TC_BLOCKDATALONG: 2026 | bin.setBlockDataMode(true); 2027 | break; 2028 | 2029 | case TC_ENDBLOCKDATA: 2030 | bin.readByte(); 2031 | passHandle = oldHandle; 2032 | return; 2033 | 2034 | default: 2035 | readObject0(false); 2036 | break; 2037 | } 2038 | } 2039 | } 2040 | 2041 | /** 2042 | * Reads in values of serializable fields declared by given class 2043 | * descriptor. If obj is non-null, sets field values in obj. Expects that 2044 | * passHandle is set to obj's handle before this method is called. 2045 | */ 2046 | private void defaultReadFields(Object obj, ObjectStreamClass desc) 2047 | throws IOException 2048 | { 2049 | Class cl = desc.forClass(); 2050 | if (cl != null && obj != null && !cl.isInstance(obj)) { 2051 | throw new ClassCastException(); 2052 | } 2053 | 2054 | int primDataSize = desc.getPrimDataSize(); 2055 | if (primVals == null || primVals.length < primDataSize) { 2056 | primVals = new byte[primDataSize]; 2057 | } 2058 | bin.readFully(primVals, 0, primDataSize, false); 2059 | if (obj != null) { 2060 | desc.setPrimFieldValues(obj, primVals); 2061 | } 2062 | 2063 | int objHandle = passHandle; 2064 | ObjectStreamField[] fields = desc.getFields(false); 2065 | Object[] objVals = new Object[desc.getNumObjFields()]; 2066 | int numPrimFields = fields.length - objVals.length; 2067 | for (int i = 0; i < objVals.length; i++) { 2068 | ObjectStreamField f = fields[numPrimFields + i]; 2069 | objVals[i] = readObject0(f.isUnshared()); 2070 | if (f.getField() != null) { 2071 | handles.markDependency(objHandle, passHandle); 2072 | } 2073 | } 2074 | if (obj != null) { 2075 | desc.setObjFieldValues(obj, objVals); 2076 | } 2077 | passHandle = objHandle; 2078 | } 2079 | 2080 | /** 2081 | * Reads in and returns IOException that caused serialization to abort. 2082 | * All stream state is discarded prior to reading in fatal exception. Sets 2083 | * passHandle to fatal exception's handle. 2084 | */ 2085 | private IOException readFatalException() throws IOException { 2086 | if (bin.readByte() != TC_EXCEPTION) { 2087 | throw new InternalError(); 2088 | } 2089 | clear(); 2090 | return (IOException) readObject0(false); 2091 | } 2092 | 2093 | /** 2094 | * If recursion depth is 0, clears internal data structures; otherwise, 2095 | * throws a StreamCorruptedException. This method is called when a 2096 | * TC_RESET typecode is encountered. 2097 | */ 2098 | private void handleReset() throws StreamCorruptedException { 2099 | if (depth > 0) { 2100 | throw new StreamCorruptedException( 2101 | "unexpected reset; recursion depth: " + depth); 2102 | } 2103 | clear(); 2104 | } 2105 | 2106 | /** 2107 | * Converts specified span of bytes into float values. 2108 | */ 2109 | // REMIND: remove once hotspot inlines Float.intBitsToFloat 2110 | private static native void bytesToFloats(byte[] src, int srcpos, 2111 | float[] dst, int dstpos, 2112 | int nfloats); 2113 | 2114 | /** 2115 | * Converts specified span of bytes into double values. 2116 | */ 2117 | // REMIND: remove once hotspot inlines Double.longBitsToDouble 2118 | private static native void bytesToDoubles(byte[] src, int srcpos, 2119 | double[] dst, int dstpos, 2120 | int ndoubles); 2121 | 2122 | /** 2123 | * Returns the first non-null class loader (not counting class loaders of 2124 | * generated reflection implementation classes) up the execution stack, or 2125 | * null if only code from the null class loader is on the stack. This 2126 | * method is also called via reflection by the following RMI-IIOP class: 2127 | * 2128 | * com.sun.corba.se.internal.util.JDKClassLoader 2129 | * 2130 | * This method should not be removed or its signature changed without 2131 | * corresponding modifications to the above class. 2132 | */ 2133 | private static ClassLoader latestUserDefinedLoader() { 2134 | return sun.misc.VM.latestUserDefinedLoader(); 2135 | } 2136 | 2137 | /** 2138 | * Default GetField implementation. 2139 | */ 2140 | private class GetFieldImpl extends GetField { 2141 | 2142 | /** class descriptor describing serializable fields */ 2143 | private final ObjectStreamClass desc; 2144 | /** primitive field values */ 2145 | private final byte[] primVals; 2146 | /** object field values */ 2147 | private final Object[] objVals; 2148 | /** object field value handles */ 2149 | private final int[] objHandles; 2150 | 2151 | /** 2152 | * Creates GetFieldImpl object for reading fields defined in given 2153 | * class descriptor. 2154 | */ 2155 | GetFieldImpl(ObjectStreamClass desc) { 2156 | this.desc = desc; 2157 | primVals = new byte[desc.getPrimDataSize()]; 2158 | objVals = new Object[desc.getNumObjFields()]; 2159 | objHandles = new int[objVals.length]; 2160 | } 2161 | 2162 | public ObjectStreamClass getObjectStreamClass() { 2163 | return desc; 2164 | } 2165 | 2166 | public boolean defaulted(String name) throws IOException { 2167 | return (getFieldOffset(name, null) < 0); 2168 | } 2169 | 2170 | public boolean get(String name, boolean val) throws IOException { 2171 | int off = getFieldOffset(name, Boolean.TYPE); 2172 | return (off >= 0) ? Bits.getBoolean(primVals, off) : val; 2173 | } 2174 | 2175 | public byte get(String name, byte val) throws IOException { 2176 | int off = getFieldOffset(name, Byte.TYPE); 2177 | return (off >= 0) ? primVals[off] : val; 2178 | } 2179 | 2180 | public char get(String name, char val) throws IOException { 2181 | int off = getFieldOffset(name, Character.TYPE); 2182 | return (off >= 0) ? Bits.getChar(primVals, off) : val; 2183 | } 2184 | 2185 | public short get(String name, short val) throws IOException { 2186 | int off = getFieldOffset(name, Short.TYPE); 2187 | return (off >= 0) ? Bits.getShort(primVals, off) : val; 2188 | } 2189 | 2190 | public int get(String name, int val) throws IOException { 2191 | int off = getFieldOffset(name, Integer.TYPE); 2192 | return (off >= 0) ? Bits.getInt(primVals, off) : val; 2193 | } 2194 | 2195 | public float get(String name, float val) throws IOException { 2196 | int off = getFieldOffset(name, Float.TYPE); 2197 | return (off >= 0) ? Bits.getFloat(primVals, off) : val; 2198 | } 2199 | 2200 | public long get(String name, long val) throws IOException { 2201 | int off = getFieldOffset(name, Long.TYPE); 2202 | return (off >= 0) ? Bits.getLong(primVals, off) : val; 2203 | } 2204 | 2205 | public double get(String name, double val) throws IOException { 2206 | int off = getFieldOffset(name, Double.TYPE); 2207 | return (off >= 0) ? Bits.getDouble(primVals, off) : val; 2208 | } 2209 | 2210 | public Object get(String name, Object val) throws IOException { 2211 | int off = getFieldOffset(name, Object.class); 2212 | if (off >= 0) { 2213 | int objHandle = objHandles[off]; 2214 | handles.markDependency(passHandle, objHandle); 2215 | return (handles.lookupException(objHandle) == null) ? 2216 | objVals[off] : null; 2217 | } else { 2218 | return val; 2219 | } 2220 | } 2221 | 2222 | /** 2223 | * Reads primitive and object field values from stream. 2224 | */ 2225 | void readFields() throws IOException { 2226 | bin.readFully(primVals, 0, primVals.length, false); 2227 | 2228 | int oldHandle = passHandle; 2229 | ObjectStreamField[] fields = desc.getFields(false); 2230 | int numPrimFields = fields.length - objVals.length; 2231 | for (int i = 0; i < objVals.length; i++) { 2232 | objVals[i] = 2233 | readObject0(fields[numPrimFields + i].isUnshared()); 2234 | objHandles[i] = passHandle; 2235 | } 2236 | passHandle = oldHandle; 2237 | } 2238 | 2239 | /** 2240 | * Returns offset of field with given name and type. A specified type 2241 | * of null matches all types, Object.class matches all non-primitive 2242 | * types, and any other non-null type matches assignable types only. 2243 | * If no matching field is found in the (incoming) class 2244 | * descriptor but a matching field is present in the associated local 2245 | * class descriptor, returns -1. Throws IllegalArgumentException if 2246 | * neither incoming nor local class descriptor contains a match. 2247 | */ 2248 | private int getFieldOffset(String name, Class type) { 2249 | ObjectStreamField field = desc.getField(name, type); 2250 | if (field != null) { 2251 | return field.getOffset(); 2252 | } else if (desc.getLocalDesc().getField(name, type) != null) { 2253 | return -1; 2254 | } else { 2255 | throw new IllegalArgumentException("no such field " + name + 2256 | " with type " + type); 2257 | } 2258 | } 2259 | } 2260 | 2261 | /** 2262 | * Prioritized list of callbacks to be performed once object graph has been 2263 | * completely deserialized. 2264 | */ 2265 | private static class ValidationList { 2266 | 2267 | private static class Callback { 2268 | final ObjectInputValidation obj; 2269 | final int priority; 2270 | Callback next; 2271 | final AccessControlContext acc; 2272 | 2273 | Callback(ObjectInputValidation obj, int priority, Callback next, 2274 | AccessControlContext acc) 2275 | { 2276 | this.obj = obj; 2277 | this.priority = priority; 2278 | this.next = next; 2279 | this.acc = acc; 2280 | } 2281 | } 2282 | 2283 | /** linked list of callbacks */ 2284 | private Callback list; 2285 | 2286 | /** 2287 | * Creates new (empty) ValidationList. 2288 | */ 2289 | ValidationList() { 2290 | } 2291 | 2292 | /** 2293 | * Registers callback. Throws InvalidObjectException if callback 2294 | * object is null. 2295 | */ 2296 | void register(ObjectInputValidation obj, int priority) 2297 | throws InvalidObjectException 2298 | { 2299 | if (obj == null) { 2300 | throw new InvalidObjectException("null callback"); 2301 | } 2302 | 2303 | Callback prev = null, cur = list; 2304 | while (cur != null && priority < cur.priority) { 2305 | prev = cur; 2306 | cur = cur.next; 2307 | } 2308 | AccessControlContext acc = AccessController.getContext(); 2309 | if (prev != null) { 2310 | prev.next = new Callback(obj, priority, cur, acc); 2311 | } else { 2312 | list = new Callback(obj, priority, list, acc); 2313 | } 2314 | } 2315 | 2316 | /** 2317 | * Invokes all registered callbacks and clears the callback list. 2318 | * Callbacks with higher priorities are called first; those with equal 2319 | * priorities may be called in any order. If any of the callbacks 2320 | * throws an InvalidObjectException, the callback process is terminated 2321 | * and the exception propagated upwards. 2322 | */ 2323 | void doCallbacks() throws InvalidObjectException { 2324 | try { 2325 | while (list != null) { 2326 | AccessController.doPrivileged( 2327 | new PrivilegedExceptionAction() 2328 | { 2329 | public Void run() throws InvalidObjectException { 2330 | list.obj.validateObject(); 2331 | return null; 2332 | } 2333 | }, list.acc); 2334 | list = list.next; 2335 | } 2336 | } catch (PrivilegedActionException ex) { 2337 | list = null; 2338 | throw (InvalidObjectException) ex.getException(); 2339 | } 2340 | } 2341 | 2342 | /** 2343 | * Resets the callback list to its initial (empty) state. 2344 | */ 2345 | public void clear() { 2346 | list = null; 2347 | } 2348 | } 2349 | 2350 | /** 2351 | * Input stream supporting single-byte peek operations. 2352 | */ 2353 | private static class PeekInputStream extends InputStream { 2354 | 2355 | /** underlying stream */ 2356 | private final InputStream in; 2357 | /** peeked byte */ 2358 | private int peekb = -1; 2359 | 2360 | /** 2361 | * Creates new PeekInputStream on top of given underlying stream. 2362 | */ 2363 | PeekInputStream(InputStream in) { 2364 | this.in = in; 2365 | } 2366 | 2367 | /** 2368 | * Peeks at next byte value in stream. Similar to read(), except 2369 | * that it does not consume the read value. 2370 | */ 2371 | int peek() throws IOException { 2372 | return (peekb >= 0) ? peekb : (peekb = in.read()); 2373 | } 2374 | 2375 | public int read() throws IOException { 2376 | if (peekb >= 0) { 2377 | int v = peekb; 2378 | peekb = -1; 2379 | return v; 2380 | } else { 2381 | return in.read(); 2382 | } 2383 | } 2384 | 2385 | public int read(byte[] b, int off, int len) throws IOException { 2386 | if (len == 0) { 2387 | return 0; 2388 | } else if (peekb < 0) { 2389 | return in.read(b, off, len); 2390 | } else { 2391 | b[off++] = (byte) peekb; 2392 | len--; 2393 | peekb = -1; 2394 | int n = in.read(b, off, len); 2395 | return (n >= 0) ? (n + 1) : 1; 2396 | } 2397 | } 2398 | 2399 | void readFully(byte[] b, int off, int len) throws IOException { 2400 | int n = 0; 2401 | while (n < len) { 2402 | int count = read(b, off + n, len - n); 2403 | if (count < 0) { 2404 | throw new EOFException(); 2405 | } 2406 | n += count; 2407 | } 2408 | } 2409 | 2410 | public long skip(long n) throws IOException { 2411 | if (n <= 0) { 2412 | return 0; 2413 | } 2414 | int skipped = 0; 2415 | if (peekb >= 0) { 2416 | peekb = -1; 2417 | skipped++; 2418 | n--; 2419 | } 2420 | return skipped + skip(n); 2421 | } 2422 | 2423 | public int available() throws IOException { 2424 | return in.available() + ((peekb >= 0) ? 1 : 0); 2425 | } 2426 | 2427 | public void close() throws IOException { 2428 | in.close(); 2429 | } 2430 | } 2431 | 2432 | /** 2433 | * Input stream with two modes: in default mode, inputs data written in the 2434 | * same format as DataOutputStream; in "block data" mode, inputs data 2435 | * bracketed by block data markers (see object serialization specification 2436 | * for details). Buffering depends on block data mode: when in default 2437 | * mode, no data is buffered in advance; when in block data mode, all data 2438 | * for the current data block is read in at once (and buffered). 2439 | */ 2440 | private class BlockDataInputStream 2441 | extends InputStream implements DataInput 2442 | { 2443 | /** maximum data block length */ 2444 | private static final int MAX_BLOCK_SIZE = 1024; 2445 | /** maximum data block header length */ 2446 | private static final int MAX_HEADER_SIZE = 5; 2447 | /** (tunable) length of char buffer (for reading strings) */ 2448 | private static final int CHAR_BUF_SIZE = 256; 2449 | /** readBlockHeader() return value indicating header read may block */ 2450 | private static final int HEADER_BLOCKED = -2; 2451 | 2452 | /** buffer for reading general/block data */ 2453 | private final byte[] buf = new byte[MAX_BLOCK_SIZE]; 2454 | /** buffer for reading block data headers */ 2455 | private final byte[] hbuf = new byte[MAX_HEADER_SIZE]; 2456 | /** char buffer for fast string reads */ 2457 | private final char[] cbuf = new char[CHAR_BUF_SIZE]; 2458 | 2459 | /** block data mode */ 2460 | private boolean blkmode = false; 2461 | 2462 | // block data state fields; values meaningful only when blkmode true 2463 | /** current offset into buf */ 2464 | private int pos = 0; 2465 | /** end offset of valid data in buf, or -1 if no more block data */ 2466 | private int end = -1; 2467 | /** number of bytes in current block yet to be read from stream */ 2468 | private int unread = 0; 2469 | 2470 | /** underlying stream (wrapped in peekable filter stream) */ 2471 | private final PeekInputStream in; 2472 | /** loopback stream (for data reads that span data blocks) */ 2473 | private final DataInputStream din; 2474 | 2475 | /** 2476 | * Creates new BlockDataInputStream on top of given underlying stream. 2477 | * Block data mode is turned off by default. 2478 | */ 2479 | BlockDataInputStream(InputStream in) { 2480 | this.in = new PeekInputStream(in); 2481 | din = new DataInputStream(this); 2482 | } 2483 | 2484 | /** 2485 | * Sets block data mode to the given mode (true == on, false == off) 2486 | * and returns the previous mode value. If the new mode is the same as 2487 | * the old mode, no action is taken. Throws IllegalStateException if 2488 | * block data mode is being switched from on to off while unconsumed 2489 | * block data is still present in the stream. 2490 | */ 2491 | boolean setBlockDataMode(boolean newmode) throws IOException { 2492 | if (blkmode == newmode) { 2493 | return blkmode; 2494 | } 2495 | if (newmode) { 2496 | pos = 0; 2497 | end = 0; 2498 | unread = 0; 2499 | } else if (pos < end) { 2500 | throw new IllegalStateException("unread block data"); 2501 | } 2502 | blkmode = newmode; 2503 | return !blkmode; 2504 | } 2505 | 2506 | /** 2507 | * Returns true if the stream is currently in block data mode, false 2508 | * otherwise. 2509 | */ 2510 | boolean getBlockDataMode() { 2511 | return blkmode; 2512 | } 2513 | 2514 | /** 2515 | * If in block data mode, skips to the end of the current group of data 2516 | * blocks (but does not unset block data mode). If not in block data 2517 | * mode, throws an IllegalStateException. 2518 | */ 2519 | void skipBlockData() throws IOException { 2520 | if (!blkmode) { 2521 | throw new IllegalStateException("not in block data mode"); 2522 | } 2523 | while (end >= 0) { 2524 | refill(); 2525 | } 2526 | } 2527 | 2528 | /** 2529 | * Attempts to read in the next block data header (if any). If 2530 | * canBlock is false and a full header cannot be read without possibly 2531 | * blocking, returns HEADER_BLOCKED, else if the next element in the 2532 | * stream is a block data header, returns the block data length 2533 | * specified by the header, else returns -1. 2534 | */ 2535 | private int readBlockHeader(boolean canBlock) throws IOException { 2536 | if (defaultDataEnd) { 2537 | /* 2538 | * Fix for 4360508: stream is currently at the end of a field 2539 | * value block written via default serialization; since there 2540 | * is no terminating TC_ENDBLOCKDATA tag, simulate 2541 | * end-of-custom-data behavior explicitly. 2542 | */ 2543 | return -1; 2544 | } 2545 | try { 2546 | for (;;) { 2547 | int avail = canBlock ? Integer.MAX_VALUE : in.available(); 2548 | if (avail == 0) { 2549 | return HEADER_BLOCKED; 2550 | } 2551 | 2552 | int tc = in.peek(); 2553 | switch (tc) { 2554 | case TC_BLOCKDATA: 2555 | if (avail < 2) { 2556 | return HEADER_BLOCKED; 2557 | } 2558 | in.readFully(hbuf, 0, 2); 2559 | return hbuf[1] & 0xFF; 2560 | 2561 | case TC_BLOCKDATALONG: 2562 | if (avail < 5) { 2563 | return HEADER_BLOCKED; 2564 | } 2565 | in.readFully(hbuf, 0, 5); 2566 | int len = Bits.getInt(hbuf, 1); 2567 | if (len < 0) { 2568 | throw new StreamCorruptedException( 2569 | "illegal block data header length: " + 2570 | len); 2571 | } 2572 | return len; 2573 | 2574 | /* 2575 | * TC_RESETs may occur in between data blocks. 2576 | * Unfortunately, this case must be parsed at a lower 2577 | * level than other typecodes, since primitive data 2578 | * reads may span data blocks separated by a TC_RESET. 2579 | */ 2580 | case TC_RESET: 2581 | in.read(); 2582 | handleReset(); 2583 | break; 2584 | 2585 | default: 2586 | if (tc >= 0 && (tc < TC_BASE || tc > TC_MAX)) { 2587 | throw new StreamCorruptedException( 2588 | String.format("invalid type code: %02X", 2589 | tc)); 2590 | } 2591 | return -1; 2592 | } 2593 | } 2594 | } catch (EOFException ex) { 2595 | throw new StreamCorruptedException( 2596 | "unexpected EOF while reading block data header"); 2597 | } 2598 | } 2599 | 2600 | /** 2601 | * Refills internal buffer buf with block data. Any data in buf at the 2602 | * time of the call is considered consumed. Sets the pos, end, and 2603 | * unread fields to reflect the new amount of available block data; if 2604 | * the next element in the stream is not a data block, sets pos and 2605 | * unread to 0 and end to -1. 2606 | */ 2607 | private void refill() throws IOException { 2608 | try { 2609 | do { 2610 | pos = 0; 2611 | if (unread > 0) { 2612 | int n = 2613 | in.read(buf, 0, Math.min(unread, MAX_BLOCK_SIZE)); 2614 | if (n >= 0) { 2615 | end = n; 2616 | unread -= n; 2617 | } else { 2618 | throw new StreamCorruptedException( 2619 | "unexpected EOF in middle of data block"); 2620 | } 2621 | } else { 2622 | int n = readBlockHeader(true); 2623 | if (n >= 0) { 2624 | end = 0; 2625 | unread = n; 2626 | } else { 2627 | end = -1; 2628 | unread = 0; 2629 | } 2630 | } 2631 | } while (pos == end); 2632 | } catch (IOException ex) { 2633 | pos = 0; 2634 | end = -1; 2635 | unread = 0; 2636 | throw ex; 2637 | } 2638 | } 2639 | 2640 | /** 2641 | * If in block data mode, returns the number of unconsumed bytes 2642 | * remaining in the current data block. If not in block data mode, 2643 | * throws an IllegalStateException. 2644 | */ 2645 | int currentBlockRemaining() { 2646 | if (blkmode) { 2647 | return (end >= 0) ? (end - pos) + unread : 0; 2648 | } else { 2649 | throw new IllegalStateException(); 2650 | } 2651 | } 2652 | 2653 | /** 2654 | * Peeks at (but does not consume) and returns the next byte value in 2655 | * the stream, or -1 if the end of the stream/block data (if in block 2656 | * data mode) has been reached. 2657 | */ 2658 | int peek() throws IOException { 2659 | if (blkmode) { 2660 | if (pos == end) { 2661 | refill(); 2662 | } 2663 | return (end >= 0) ? (buf[pos] & 0xFF) : -1; 2664 | } else { 2665 | return in.peek(); 2666 | } 2667 | } 2668 | 2669 | /** 2670 | * Peeks at (but does not consume) and returns the next byte value in 2671 | * the stream, or throws EOFException if end of stream/block data has 2672 | * been reached. 2673 | */ 2674 | byte peekByte() throws IOException { 2675 | int val = peek(); 2676 | if (val < 0) { 2677 | throw new EOFException(); 2678 | } 2679 | return (byte) val; 2680 | } 2681 | 2682 | 2683 | /* ----------------- generic input stream methods ------------------ */ 2684 | /* 2685 | * The following methods are equivalent to their counterparts in 2686 | * InputStream, except that they interpret data block boundaries and 2687 | * read the requested data from within data blocks when in block data 2688 | * mode. 2689 | */ 2690 | 2691 | public int read() throws IOException { 2692 | if (blkmode) { 2693 | if (pos == end) { 2694 | refill(); 2695 | } 2696 | return (end >= 0) ? (buf[pos++] & 0xFF) : -1; 2697 | } else { 2698 | return in.read(); 2699 | } 2700 | } 2701 | 2702 | public int read(byte[] b, int off, int len) throws IOException { 2703 | return read(b, off, len, false); 2704 | } 2705 | 2706 | public long skip(long len) throws IOException { 2707 | long remain = len; 2708 | while (remain > 0) { 2709 | if (blkmode) { 2710 | if (pos == end) { 2711 | refill(); 2712 | } 2713 | if (end < 0) { 2714 | break; 2715 | } 2716 | int nread = (int) Math.min(remain, end - pos); 2717 | remain -= nread; 2718 | pos += nread; 2719 | } else { 2720 | int nread = (int) Math.min(remain, MAX_BLOCK_SIZE); 2721 | if ((nread = in.read(buf, 0, nread)) < 0) { 2722 | break; 2723 | } 2724 | remain -= nread; 2725 | } 2726 | } 2727 | return len - remain; 2728 | } 2729 | 2730 | public int available() throws IOException { 2731 | if (blkmode) { 2732 | if ((pos == end) && (unread == 0)) { 2733 | int n; 2734 | while ((n = readBlockHeader(false)) == 0) ; 2735 | switch (n) { 2736 | case HEADER_BLOCKED: 2737 | break; 2738 | 2739 | case -1: 2740 | pos = 0; 2741 | end = -1; 2742 | break; 2743 | 2744 | default: 2745 | pos = 0; 2746 | end = 0; 2747 | unread = n; 2748 | break; 2749 | } 2750 | } 2751 | // avoid unnecessary call to in.available() if possible 2752 | int unreadAvail = (unread > 0) ? 2753 | Math.min(in.available(), unread) : 0; 2754 | return (end >= 0) ? (end - pos) + unreadAvail : 0; 2755 | } else { 2756 | return in.available(); 2757 | } 2758 | } 2759 | 2760 | public void close() throws IOException { 2761 | if (blkmode) { 2762 | pos = 0; 2763 | end = -1; 2764 | unread = 0; 2765 | } 2766 | in.close(); 2767 | } 2768 | 2769 | /** 2770 | * Attempts to read len bytes into byte array b at offset off. Returns 2771 | * the number of bytes read, or -1 if the end of stream/block data has 2772 | * been reached. If copy is true, reads values into an intermediate 2773 | * buffer before copying them to b (to avoid exposing a reference to 2774 | * b). 2775 | */ 2776 | int read(byte[] b, int off, int len, boolean copy) throws IOException { 2777 | if (len == 0) { 2778 | return 0; 2779 | } else if (blkmode) { 2780 | if (pos == end) { 2781 | refill(); 2782 | } 2783 | if (end < 0) { 2784 | return -1; 2785 | } 2786 | int nread = Math.min(len, end - pos); 2787 | System.arraycopy(buf, pos, b, off, nread); 2788 | pos += nread; 2789 | return nread; 2790 | } else if (copy) { 2791 | int nread = in.read(buf, 0, Math.min(len, MAX_BLOCK_SIZE)); 2792 | if (nread > 0) { 2793 | System.arraycopy(buf, 0, b, off, nread); 2794 | } 2795 | return nread; 2796 | } else { 2797 | return in.read(b, off, len); 2798 | } 2799 | } 2800 | 2801 | /* ----------------- primitive data input methods ------------------ */ 2802 | /* 2803 | * The following methods are equivalent to their counterparts in 2804 | * DataInputStream, except that they interpret data block boundaries 2805 | * and read the requested data from within data blocks when in block 2806 | * data mode. 2807 | */ 2808 | 2809 | public void readFully(byte[] b) throws IOException { 2810 | readFully(b, 0, b.length, false); 2811 | } 2812 | 2813 | public void readFully(byte[] b, int off, int len) throws IOException { 2814 | readFully(b, off, len, false); 2815 | } 2816 | 2817 | public void readFully(byte[] b, int off, int len, boolean copy) 2818 | throws IOException 2819 | { 2820 | while (len > 0) { 2821 | int n = read(b, off, len, copy); 2822 | if (n < 0) { 2823 | throw new EOFException(); 2824 | } 2825 | off += n; 2826 | len -= n; 2827 | } 2828 | } 2829 | 2830 | public int skipBytes(int n) throws IOException { 2831 | return din.skipBytes(n); 2832 | } 2833 | 2834 | public boolean readBoolean() throws IOException { 2835 | int v = read(); 2836 | if (v < 0) { 2837 | throw new EOFException(); 2838 | } 2839 | return (v != 0); 2840 | } 2841 | 2842 | public byte readByte() throws IOException { 2843 | int v = read(); 2844 | if (v < 0) { 2845 | throw new EOFException(); 2846 | } 2847 | return (byte) v; 2848 | } 2849 | 2850 | public int readUnsignedByte() throws IOException { 2851 | int v = read(); 2852 | if (v < 0) { 2853 | throw new EOFException(); 2854 | } 2855 | return v; 2856 | } 2857 | 2858 | public char readChar() throws IOException { 2859 | if (!blkmode) { 2860 | pos = 0; 2861 | in.readFully(buf, 0, 2); 2862 | } else if (end - pos < 2) { 2863 | return din.readChar(); 2864 | } 2865 | char v = Bits.getChar(buf, pos); 2866 | pos += 2; 2867 | return v; 2868 | } 2869 | 2870 | public short readShort() throws IOException { 2871 | if (!blkmode) { 2872 | pos = 0; 2873 | in.readFully(buf, 0, 2); 2874 | } else if (end - pos < 2) { 2875 | return din.readShort(); 2876 | } 2877 | short v = Bits.getShort(buf, pos); 2878 | pos += 2; 2879 | return v; 2880 | } 2881 | 2882 | public int readUnsignedShort() throws IOException { 2883 | if (!blkmode) { 2884 | pos = 0; 2885 | in.readFully(buf, 0, 2); 2886 | } else if (end - pos < 2) { 2887 | return din.readUnsignedShort(); 2888 | } 2889 | int v = Bits.getShort(buf, pos) & 0xFFFF; 2890 | pos += 2; 2891 | return v; 2892 | } 2893 | 2894 | public int readInt() throws IOException { 2895 | if (!blkmode) { 2896 | pos = 0; 2897 | in.readFully(buf, 0, 4); 2898 | } else if (end - pos < 4) { 2899 | return din.readInt(); 2900 | } 2901 | int v = Bits.getInt(buf, pos); 2902 | pos += 4; 2903 | return v; 2904 | } 2905 | 2906 | public float readFloat() throws IOException { 2907 | if (!blkmode) { 2908 | pos = 0; 2909 | in.readFully(buf, 0, 4); 2910 | } else if (end - pos < 4) { 2911 | return din.readFloat(); 2912 | } 2913 | float v = Bits.getFloat(buf, pos); 2914 | pos += 4; 2915 | return v; 2916 | } 2917 | 2918 | public long readLong() throws IOException { 2919 | if (!blkmode) { 2920 | pos = 0; 2921 | in.readFully(buf, 0, 8); 2922 | } else if (end - pos < 8) { 2923 | return din.readLong(); 2924 | } 2925 | long v = Bits.getLong(buf, pos); 2926 | pos += 8; 2927 | return v; 2928 | } 2929 | 2930 | public double readDouble() throws IOException { 2931 | if (!blkmode) { 2932 | pos = 0; 2933 | in.readFully(buf, 0, 8); 2934 | } else if (end - pos < 8) { 2935 | return din.readDouble(); 2936 | } 2937 | double v = Bits.getDouble(buf, pos); 2938 | pos += 8; 2939 | return v; 2940 | } 2941 | 2942 | public String readUTF() throws IOException { 2943 | return readUTFBody(readUnsignedShort()); 2944 | } 2945 | 2946 | @SuppressWarnings("deprecation") 2947 | public String readLine() throws IOException { 2948 | return din.readLine(); // deprecated, not worth optimizing 2949 | } 2950 | 2951 | /* -------------- primitive data array input methods --------------- */ 2952 | /* 2953 | * The following methods read in spans of primitive data values. 2954 | * Though equivalent to calling the corresponding primitive read 2955 | * methods repeatedly, these methods are optimized for reading groups 2956 | * of primitive data values more efficiently. 2957 | */ 2958 | 2959 | void readBooleans(boolean[] v, int off, int len) throws IOException { 2960 | int stop, endoff = off + len; 2961 | while (off < endoff) { 2962 | if (!blkmode) { 2963 | int span = Math.min(endoff - off, MAX_BLOCK_SIZE); 2964 | in.readFully(buf, 0, span); 2965 | stop = off + span; 2966 | pos = 0; 2967 | } else if (end - pos < 1) { 2968 | v[off++] = din.readBoolean(); 2969 | continue; 2970 | } else { 2971 | stop = Math.min(endoff, off + end - pos); 2972 | } 2973 | 2974 | while (off < stop) { 2975 | v[off++] = Bits.getBoolean(buf, pos++); 2976 | } 2977 | } 2978 | } 2979 | 2980 | void readChars(char[] v, int off, int len) throws IOException { 2981 | int stop, endoff = off + len; 2982 | while (off < endoff) { 2983 | if (!blkmode) { 2984 | int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 1); 2985 | in.readFully(buf, 0, span << 1); 2986 | stop = off + span; 2987 | pos = 0; 2988 | } else if (end - pos < 2) { 2989 | v[off++] = din.readChar(); 2990 | continue; 2991 | } else { 2992 | stop = Math.min(endoff, off + ((end - pos) >> 1)); 2993 | } 2994 | 2995 | while (off < stop) { 2996 | v[off++] = Bits.getChar(buf, pos); 2997 | pos += 2; 2998 | } 2999 | } 3000 | } 3001 | 3002 | void readShorts(short[] v, int off, int len) throws IOException { 3003 | int stop, endoff = off + len; 3004 | while (off < endoff) { 3005 | if (!blkmode) { 3006 | int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 1); 3007 | in.readFully(buf, 0, span << 1); 3008 | stop = off + span; 3009 | pos = 0; 3010 | } else if (end - pos < 2) { 3011 | v[off++] = din.readShort(); 3012 | continue; 3013 | } else { 3014 | stop = Math.min(endoff, off + ((end - pos) >> 1)); 3015 | } 3016 | 3017 | while (off < stop) { 3018 | v[off++] = Bits.getShort(buf, pos); 3019 | pos += 2; 3020 | } 3021 | } 3022 | } 3023 | 3024 | void readInts(int[] v, int off, int len) throws IOException { 3025 | int stop, endoff = off + len; 3026 | while (off < endoff) { 3027 | if (!blkmode) { 3028 | int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 2); 3029 | in.readFully(buf, 0, span << 2); 3030 | stop = off + span; 3031 | pos = 0; 3032 | } else if (end - pos < 4) { 3033 | v[off++] = din.readInt(); 3034 | continue; 3035 | } else { 3036 | stop = Math.min(endoff, off + ((end - pos) >> 2)); 3037 | } 3038 | 3039 | while (off < stop) { 3040 | v[off++] = Bits.getInt(buf, pos); 3041 | pos += 4; 3042 | } 3043 | } 3044 | } 3045 | 3046 | void readFloats(float[] v, int off, int len) throws IOException { 3047 | int span, endoff = off + len; 3048 | while (off < endoff) { 3049 | if (!blkmode) { 3050 | span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 2); 3051 | in.readFully(buf, 0, span << 2); 3052 | pos = 0; 3053 | } else if (end - pos < 4) { 3054 | v[off++] = din.readFloat(); 3055 | continue; 3056 | } else { 3057 | span = Math.min(endoff - off, ((end - pos) >> 2)); 3058 | } 3059 | 3060 | bytesToFloats(buf, pos, v, off, span); 3061 | off += span; 3062 | pos += span << 2; 3063 | } 3064 | } 3065 | 3066 | void readLongs(long[] v, int off, int len) throws IOException { 3067 | int stop, endoff = off + len; 3068 | while (off < endoff) { 3069 | if (!blkmode) { 3070 | int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 3); 3071 | in.readFully(buf, 0, span << 3); 3072 | stop = off + span; 3073 | pos = 0; 3074 | } else if (end - pos < 8) { 3075 | v[off++] = din.readLong(); 3076 | continue; 3077 | } else { 3078 | stop = Math.min(endoff, off + ((end - pos) >> 3)); 3079 | } 3080 | 3081 | while (off < stop) { 3082 | v[off++] = Bits.getLong(buf, pos); 3083 | pos += 8; 3084 | } 3085 | } 3086 | } 3087 | 3088 | void readDoubles(double[] v, int off, int len) throws IOException { 3089 | int span, endoff = off + len; 3090 | while (off < endoff) { 3091 | if (!blkmode) { 3092 | span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 3); 3093 | in.readFully(buf, 0, span << 3); 3094 | pos = 0; 3095 | } else if (end - pos < 8) { 3096 | v[off++] = din.readDouble(); 3097 | continue; 3098 | } else { 3099 | span = Math.min(endoff - off, ((end - pos) >> 3)); 3100 | } 3101 | 3102 | bytesToDoubles(buf, pos, v, off, span); 3103 | off += span; 3104 | pos += span << 3; 3105 | } 3106 | } 3107 | 3108 | /** 3109 | * Reads in string written in "long" UTF format. "Long" UTF format is 3110 | * identical to standard UTF, except that it uses an 8 byte header 3111 | * (instead of the standard 2 bytes) to convey the UTF encoding length. 3112 | */ 3113 | String readLongUTF() throws IOException { 3114 | return readUTFBody(readLong()); 3115 | } 3116 | 3117 | /** 3118 | * Reads in the "body" (i.e., the UTF representation minus the 2-byte 3119 | * or 8-byte length header) of a UTF encoding, which occupies the next 3120 | * utflen bytes. 3121 | */ 3122 | private String readUTFBody(long utflen) throws IOException { 3123 | StringBuilder sbuf = new StringBuilder(); 3124 | if (!blkmode) { 3125 | end = pos = 0; 3126 | } 3127 | 3128 | while (utflen > 0) { 3129 | int avail = end - pos; 3130 | if (avail >= 3 || (long) avail == utflen) { 3131 | utflen -= readUTFSpan(sbuf, utflen); 3132 | } else { 3133 | if (blkmode) { 3134 | // near block boundary, read one byte at a time 3135 | utflen -= readUTFChar(sbuf, utflen); 3136 | } else { 3137 | // shift and refill buffer manually 3138 | if (avail > 0) { 3139 | System.arraycopy(buf, pos, buf, 0, avail); 3140 | } 3141 | pos = 0; 3142 | end = (int) Math.min(MAX_BLOCK_SIZE, utflen); 3143 | in.readFully(buf, avail, end - avail); 3144 | } 3145 | } 3146 | } 3147 | 3148 | return sbuf.toString(); 3149 | } 3150 | 3151 | /** 3152 | * Reads span of UTF-encoded characters out of internal buffer 3153 | * (starting at offset pos and ending at or before offset end), 3154 | * consuming no more than utflen bytes. Appends read characters to 3155 | * sbuf. Returns the number of bytes consumed. 3156 | */ 3157 | private long readUTFSpan(StringBuilder sbuf, long utflen) 3158 | throws IOException 3159 | { 3160 | int cpos = 0; 3161 | int start = pos; 3162 | int avail = Math.min(end - pos, CHAR_BUF_SIZE); 3163 | // stop short of last char unless all of utf bytes in buffer 3164 | int stop = pos + ((utflen > avail) ? avail - 2 : (int) utflen); 3165 | boolean outOfBounds = false; 3166 | 3167 | try { 3168 | while (pos < stop) { 3169 | int b1, b2, b3; 3170 | b1 = buf[pos++] & 0xFF; 3171 | switch (b1 >> 4) { 3172 | case 0: 3173 | case 1: 3174 | case 2: 3175 | case 3: 3176 | case 4: 3177 | case 5: 3178 | case 6: 3179 | case 7: // 1 byte format: 0xxxxxxx 3180 | cbuf[cpos++] = (char) b1; 3181 | break; 3182 | 3183 | case 12: 3184 | case 13: // 2 byte format: 110xxxxx 10xxxxxx 3185 | b2 = buf[pos++]; 3186 | if ((b2 & 0xC0) != 0x80) { 3187 | throw new UTFDataFormatException(); 3188 | } 3189 | cbuf[cpos++] = (char) (((b1 & 0x1F) << 6) | 3190 | ((b2 & 0x3F) << 0)); 3191 | break; 3192 | 3193 | case 14: // 3 byte format: 1110xxxx 10xxxxxx 10xxxxxx 3194 | b3 = buf[pos + 1]; 3195 | b2 = buf[pos + 0]; 3196 | pos += 2; 3197 | if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) { 3198 | throw new UTFDataFormatException(); 3199 | } 3200 | cbuf[cpos++] = (char) (((b1 & 0x0F) << 12) | 3201 | ((b2 & 0x3F) << 6) | 3202 | ((b3 & 0x3F) << 0)); 3203 | break; 3204 | 3205 | default: // 10xx xxxx, 1111 xxxx 3206 | throw new UTFDataFormatException(); 3207 | } 3208 | } 3209 | } catch (ArrayIndexOutOfBoundsException ex) { 3210 | outOfBounds = true; 3211 | } finally { 3212 | if (outOfBounds || (pos - start) > utflen) { 3213 | /* 3214 | * Fix for 4450867: if a malformed utf char causes the 3215 | * conversion loop to scan past the expected end of the utf 3216 | * string, only consume the expected number of utf bytes. 3217 | */ 3218 | pos = start + (int) utflen; 3219 | throw new UTFDataFormatException(); 3220 | } 3221 | } 3222 | 3223 | sbuf.append(cbuf, 0, cpos); 3224 | return pos - start; 3225 | } 3226 | 3227 | /** 3228 | * Reads in single UTF-encoded character one byte at a time, appends 3229 | * the character to sbuf, and returns the number of bytes consumed. 3230 | * This method is used when reading in UTF strings written in block 3231 | * data mode to handle UTF-encoded characters which (potentially) 3232 | * straddle block-data boundaries. 3233 | */ 3234 | private int readUTFChar(StringBuilder sbuf, long utflen) 3235 | throws IOException 3236 | { 3237 | int b1, b2, b3; 3238 | b1 = readByte() & 0xFF; 3239 | switch (b1 >> 4) { 3240 | case 0: 3241 | case 1: 3242 | case 2: 3243 | case 3: 3244 | case 4: 3245 | case 5: 3246 | case 6: 3247 | case 7: // 1 byte format: 0xxxxxxx 3248 | sbuf.append((char) b1); 3249 | return 1; 3250 | 3251 | case 12: 3252 | case 13: // 2 byte format: 110xxxxx 10xxxxxx 3253 | if (utflen < 2) { 3254 | throw new UTFDataFormatException(); 3255 | } 3256 | b2 = readByte(); 3257 | if ((b2 & 0xC0) != 0x80) { 3258 | throw new UTFDataFormatException(); 3259 | } 3260 | sbuf.append((char) (((b1 & 0x1F) << 6) | 3261 | ((b2 & 0x3F) << 0))); 3262 | return 2; 3263 | 3264 | case 14: // 3 byte format: 1110xxxx 10xxxxxx 10xxxxxx 3265 | if (utflen < 3) { 3266 | if (utflen == 2) { 3267 | readByte(); // consume remaining byte 3268 | } 3269 | throw new UTFDataFormatException(); 3270 | } 3271 | b2 = readByte(); 3272 | b3 = readByte(); 3273 | if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) { 3274 | throw new UTFDataFormatException(); 3275 | } 3276 | sbuf.append((char) (((b1 & 0x0F) << 12) | 3277 | ((b2 & 0x3F) << 6) | 3278 | ((b3 & 0x3F) << 0))); 3279 | return 3; 3280 | 3281 | default: // 10xx xxxx, 1111 xxxx 3282 | throw new UTFDataFormatException(); 3283 | } 3284 | } 3285 | } 3286 | 3287 | /** 3288 | * Unsynchronized table which tracks wire handle to object mappings, as 3289 | * well as ClassNotFoundExceptions associated with deserialized objects. 3290 | * This class implements an exception-propagation algorithm for 3291 | * determining which objects should have ClassNotFoundExceptions associated 3292 | * with them, taking into account cycles and discontinuities (e.g., skipped 3293 | * fields) in the object graph. 3294 | * 3295 | *

General use of the table is as follows: during deserialization, a 3296 | * given object is first assigned a handle by calling the assign method. 3297 | * This method leaves the assigned handle in an "open" state, wherein 3298 | * dependencies on the exception status of other handles can be registered 3299 | * by calling the markDependency method, or an exception can be directly 3300 | * associated with the handle by calling markException. When a handle is 3301 | * tagged with an exception, the HandleTable assumes responsibility for 3302 | * propagating the exception to any other objects which depend 3303 | * (transitively) on the exception-tagged object. 3304 | * 3305 | *

Once all exception information/dependencies for the handle have been 3306 | * registered, the handle should be "closed" by calling the finish method 3307 | * on it. The act of finishing a handle allows the exception propagation 3308 | * algorithm to aggressively prune dependency links, lessening the 3309 | * performance/memory impact of exception tracking. 3310 | * 3311 | *

Note that the exception propagation algorithm used depends on handles 3312 | * being assigned/finished in LIFO order; however, for simplicity as well 3313 | * as memory conservation, it does not enforce this constraint. 3314 | */ 3315 | // REMIND: add full description of exception propagation algorithm? 3316 | private static class HandleTable { 3317 | 3318 | /* status codes indicating whether object has associated exception */ 3319 | private static final byte STATUS_OK = 1; 3320 | private static final byte STATUS_UNKNOWN = 2; 3321 | private static final byte STATUS_EXCEPTION = 3; 3322 | 3323 | /** array mapping handle -> object status */ 3324 | byte[] status; 3325 | /** array mapping handle -> object/exception (depending on status) */ 3326 | Object[] entries; 3327 | /** array mapping handle -> list of dependent handles (if any) */ 3328 | HandleList[] deps; 3329 | /** lowest unresolved dependency */ 3330 | int lowDep = -1; 3331 | /** number of handles in table */ 3332 | int size = 0; 3333 | 3334 | /** 3335 | * Creates handle table with the given initial capacity. 3336 | */ 3337 | HandleTable(int initialCapacity) { 3338 | status = new byte[initialCapacity]; 3339 | entries = new Object[initialCapacity]; 3340 | deps = new HandleList[initialCapacity]; 3341 | } 3342 | 3343 | /** 3344 | * Assigns next available handle to given object, and returns assigned 3345 | * handle. Once object has been completely deserialized (and all 3346 | * dependencies on other objects identified), the handle should be 3347 | * "closed" by passing it to finish(). 3348 | */ 3349 | int assign(Object obj) { 3350 | if (size >= entries.length) { 3351 | grow(); 3352 | } 3353 | status[size] = STATUS_UNKNOWN; 3354 | entries[size] = obj; 3355 | return size++; 3356 | } 3357 | 3358 | /** 3359 | * Registers a dependency (in exception status) of one handle on 3360 | * another. The dependent handle must be "open" (i.e., assigned, but 3361 | * not finished yet). No action is taken if either dependent or target 3362 | * handle is NULL_HANDLE. 3363 | */ 3364 | void markDependency(int dependent, int target) { 3365 | if (dependent == NULL_HANDLE || target == NULL_HANDLE) { 3366 | return; 3367 | } 3368 | switch (status[dependent]) { 3369 | 3370 | case STATUS_UNKNOWN: 3371 | switch (status[target]) { 3372 | case STATUS_OK: 3373 | // ignore dependencies on objs with no exception 3374 | break; 3375 | 3376 | case STATUS_EXCEPTION: 3377 | // eagerly propagate exception 3378 | markException(dependent, 3379 | (ClassNotFoundException) entries[target]); 3380 | break; 3381 | 3382 | case STATUS_UNKNOWN: 3383 | // add to dependency list of target 3384 | if (deps[target] == null) { 3385 | deps[target] = new HandleList(); 3386 | } 3387 | deps[target].add(dependent); 3388 | 3389 | // remember lowest unresolved target seen 3390 | if (lowDep < 0 || lowDep > target) { 3391 | lowDep = target; 3392 | } 3393 | break; 3394 | 3395 | default: 3396 | throw new InternalError(); 3397 | } 3398 | break; 3399 | 3400 | case STATUS_EXCEPTION: 3401 | break; 3402 | 3403 | default: 3404 | throw new InternalError(); 3405 | } 3406 | } 3407 | 3408 | /** 3409 | * Associates a ClassNotFoundException (if one not already associated) 3410 | * with the currently active handle and propagates it to other 3411 | * referencing objects as appropriate. The specified handle must be 3412 | * "open" (i.e., assigned, but not finished yet). 3413 | */ 3414 | void markException(int handle, ClassNotFoundException ex) { 3415 | switch (status[handle]) { 3416 | case STATUS_UNKNOWN: 3417 | status[handle] = STATUS_EXCEPTION; 3418 | entries[handle] = ex; 3419 | 3420 | // propagate exception to dependents 3421 | HandleList dlist = deps[handle]; 3422 | if (dlist != null) { 3423 | int ndeps = dlist.size(); 3424 | for (int i = 0; i < ndeps; i++) { 3425 | markException(dlist.get(i), ex); 3426 | } 3427 | deps[handle] = null; 3428 | } 3429 | break; 3430 | 3431 | case STATUS_EXCEPTION: 3432 | break; 3433 | 3434 | default: 3435 | throw new InternalError(); 3436 | } 3437 | } 3438 | 3439 | /** 3440 | * Marks given handle as finished, meaning that no new dependencies 3441 | * will be marked for handle. Calls to the assign and finish methods 3442 | * must occur in LIFO order. 3443 | */ 3444 | void finish(int handle) { 3445 | int end; 3446 | if (lowDep < 0) { 3447 | // no pending unknowns, only resolve current handle 3448 | end = handle + 1; 3449 | } else if (lowDep >= handle) { 3450 | // pending unknowns now clearable, resolve all upward handles 3451 | end = size; 3452 | lowDep = -1; 3453 | } else { 3454 | // unresolved backrefs present, can't resolve anything yet 3455 | return; 3456 | } 3457 | 3458 | // change STATUS_UNKNOWN -> STATUS_OK in selected span of handles 3459 | for (int i = handle; i < end; i++) { 3460 | switch (status[i]) { 3461 | case STATUS_UNKNOWN: 3462 | status[i] = STATUS_OK; 3463 | deps[i] = null; 3464 | break; 3465 | 3466 | case STATUS_OK: 3467 | case STATUS_EXCEPTION: 3468 | break; 3469 | 3470 | default: 3471 | throw new InternalError(); 3472 | } 3473 | } 3474 | } 3475 | 3476 | /** 3477 | * Assigns a new object to the given handle. The object previously 3478 | * associated with the handle is forgotten. This method has no effect 3479 | * if the given handle already has an exception associated with it. 3480 | * This method may be called at any time after the handle is assigned. 3481 | */ 3482 | void setObject(int handle, Object obj) { 3483 | switch (status[handle]) { 3484 | case STATUS_UNKNOWN: 3485 | case STATUS_OK: 3486 | entries[handle] = obj; 3487 | break; 3488 | 3489 | case STATUS_EXCEPTION: 3490 | break; 3491 | 3492 | default: 3493 | throw new InternalError(); 3494 | } 3495 | } 3496 | 3497 | /** 3498 | * Looks up and returns object associated with the given handle. 3499 | * Returns null if the given handle is NULL_HANDLE, or if it has an 3500 | * associated ClassNotFoundException. 3501 | */ 3502 | Object lookupObject(int handle) { 3503 | return (handle != NULL_HANDLE && 3504 | status[handle] != STATUS_EXCEPTION) ? 3505 | entries[handle] : null; 3506 | } 3507 | 3508 | /** 3509 | * Looks up and returns ClassNotFoundException associated with the 3510 | * given handle. Returns null if the given handle is NULL_HANDLE, or 3511 | * if there is no ClassNotFoundException associated with the handle. 3512 | */ 3513 | ClassNotFoundException lookupException(int handle) { 3514 | return (handle != NULL_HANDLE && 3515 | status[handle] == STATUS_EXCEPTION) ? 3516 | (ClassNotFoundException) entries[handle] : null; 3517 | } 3518 | 3519 | /** 3520 | * Resets table to its initial state. 3521 | */ 3522 | void clear() { 3523 | Arrays.fill(status, 0, size, (byte) 0); 3524 | Arrays.fill(entries, 0, size, null); 3525 | Arrays.fill(deps, 0, size, null); 3526 | lowDep = -1; 3527 | size = 0; 3528 | } 3529 | 3530 | /** 3531 | * Returns number of handles registered in table. 3532 | */ 3533 | int size() { 3534 | return size; 3535 | } 3536 | 3537 | /** 3538 | * Expands capacity of internal arrays. 3539 | */ 3540 | private void grow() { 3541 | int newCapacity = (entries.length << 1) + 1; 3542 | 3543 | byte[] newStatus = new byte[newCapacity]; 3544 | Object[] newEntries = new Object[newCapacity]; 3545 | HandleList[] newDeps = new HandleList[newCapacity]; 3546 | 3547 | System.arraycopy(status, 0, newStatus, 0, size); 3548 | System.arraycopy(entries, 0, newEntries, 0, size); 3549 | System.arraycopy(deps, 0, newDeps, 0, size); 3550 | 3551 | status = newStatus; 3552 | entries = newEntries; 3553 | deps = newDeps; 3554 | } 3555 | 3556 | /** 3557 | * Simple growable list of (integer) handles. 3558 | */ 3559 | private static class HandleList { 3560 | private int[] list = new int[4]; 3561 | private int size = 0; 3562 | 3563 | public HandleList() { 3564 | } 3565 | 3566 | public void add(int handle) { 3567 | if (size >= list.length) { 3568 | int[] newList = new int[list.length << 1]; 3569 | System.arraycopy(list, 0, newList, 0, list.length); 3570 | list = newList; 3571 | } 3572 | list[size++] = handle; 3573 | } 3574 | 3575 | public int get(int index) { 3576 | if (index >= size) { 3577 | throw new ArrayIndexOutOfBoundsException(); 3578 | } 3579 | return list[index]; 3580 | } 3581 | 3582 | public int size() { 3583 | return size; 3584 | } 3585 | } 3586 | } 3587 | 3588 | /** 3589 | * Method for cloning arrays in case of using unsharing reading 3590 | */ 3591 | private static Object cloneArray(Object array) { 3592 | if (array instanceof Object[]) { 3593 | return ((Object[]) array).clone(); 3594 | } else if (array instanceof boolean[]) { 3595 | return ((boolean[]) array).clone(); 3596 | } else if (array instanceof byte[]) { 3597 | return ((byte[]) array).clone(); 3598 | } else if (array instanceof char[]) { 3599 | return ((char[]) array).clone(); 3600 | } else if (array instanceof double[]) { 3601 | return ((double[]) array).clone(); 3602 | } else if (array instanceof float[]) { 3603 | return ((float[]) array).clone(); 3604 | } else if (array instanceof int[]) { 3605 | return ((int[]) array).clone(); 3606 | } else if (array instanceof long[]) { 3607 | return ((long[]) array).clone(); 3608 | } else if (array instanceof short[]) { 3609 | return ((short[]) array).clone(); 3610 | } else { 3611 | throw new AssertionError(); 3612 | } 3613 | } 3614 | 3615 | } 3616 | --------------------------------------------------------------------------------