From d7b4e104300b77c8bcda2fcd74064caa2590ce2b Mon Sep 17 00:00:00 2001 From: "Ray J. Greenwell" Date: Sat, 21 Mar 2026 14:46:35 -0700 Subject: [PATCH] Only check for the "L" and ";" if we're in an array. I can't help but over-optimize. We're parsing the result of class.getName(), it will be a bare class name if not in the array. Also: Stop going out of our way to not throw an exception if the input is malformed, as that's the desired result for bad input. --- .../com/threerings/io/ObjectInputStream.java | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/com/threerings/io/ObjectInputStream.java b/core/src/main/java/com/threerings/io/ObjectInputStream.java index c5887c48a..3f03535cf 100644 --- a/core/src/main/java/com/threerings/io/ObjectInputStream.java +++ b/core/src/main/java/com/threerings/io/ObjectInputStream.java @@ -278,35 +278,42 @@ public class ObjectInputStream extends DataInputStream protected void validateClassPrefix (short code, String cname) throws IOException { - for (int start = 0, end = cname.length(); start < end; ++start) { - char c = cname.charAt(start); - if (c == '[') continue; // continue the loop to step past array markers - - // if it's a single character do a quick check: - if (start == end - 1) { - switch (c) { + if (cname.charAt(0) == '[') { + // array handling. It's ok to throw if the cname is in an invalid format... + for (int start = 1, last = cname.length() - 1; true; ++start) { + switch (cname.charAt(start)) { + case '[': continue; // advance `start` case 'I': case 'Z': case 'B': case 'S': case 'C': case 'J': case 'F': case 'D': - return; // valid - } - } else { - // This is annoying: Something else is pre-stripping the L if not in an array? - if (c == 'L' && cname.endsWith(";")) { - ++start; - --end; - } - String className = cname.substring(start, end); - for (String prefix : _allowedPrefixes) { - if (className.startsWith(prefix)) return; // valid + if (start == last) return; // valid + break; + + case 'L': + if (cname.charAt(last) == ';' && + isValidClassPrefix(cname.substring(start + 1, last))) return; // valid + break; } + break; // once we see any non-[ character, we're done } - break; // if we didn't see a [, we're done processing - } + + } else if (isValidClassPrefix(cname)) return; log.warning("Blocked deserialization of non-whitelisted class", "code", code, "class", cname); throw new IOException("Deserialization of class not allowed: " + cname); } + /** + * Validate a class name against the prefix whitelist, after array information has been + * removed and primitive arrays handled elsewhere. + */ + protected boolean isValidClassPrefix (String className) + { + for (String prefix : _allowedPrefixes) { + if (className.startsWith(prefix)) return true; + } + return false; + } + /** * Reads an object from the input stream that was previously written with {@link * ObjectOutputStream#writeBareObject(Object)}.