From c8d42afb5ec4fb5e2267aab3fb2d3af4a40df5d9 Mon Sep 17 00:00:00 2001 From: Chouser Date: Mon, 17 Jul 2017 23:04:41 -0400 Subject: [PATCH] CLJ-2204 Disable serialization of proxy classes --- src/clj/clojure/core_proxy.clj | 20 ++++++++++++++++++- test/clojure/test_clojure/java_interop.clj | 31 +++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/clj/clojure/core_proxy.clj b/src/clj/clojure/core_proxy.clj index 813c8bb..70d0528 100644 --- a/src/clj/clojure/core_proxy.clj +++ b/src/clj/clojure/core_proxy.clj @@ -1,30 +1,31 @@ ; Copyright (c) Rich Hickey. All rights reserved. ; The use and distribution terms for this software are covered by the ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) ; which can be found in the file epl-v10.html at the root of this distribution. ; By using this software in any fashion, you are agreeing to be bound by ; the terms of this license. ; You must not remove this notice, or any other, from this software. (in-ns 'clojure.core) ;;;;;;;;;;;;;;;;;;;;;;;;;;;; proxy ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (import '(clojure.asm ClassWriter ClassVisitor Opcodes Type) '(java.lang.reflect Modifier Constructor) + '(java.io Serializable NotSerializableException) '(clojure.asm.commons Method GeneratorAdapter) '(clojure.lang IProxy Reflector DynamicClassLoader IPersistentMap PersistentHashMap RT)) (defn method-sig [^java.lang.reflect.Method meth] [(. meth (getName)) (seq (. meth (getParameterTypes))) (. meth getReturnType)]) (defn- most-specific [rtypes] (or (some (fn [t] (when (every? #(isa? t %) rtypes) t)) rtypes) (throw (Exception. "Incompatible return types")))) (defn- group-by-sig [coll] "takes a collection of [msig meth] and returns a seq of maps from return-types to meths." (vals (reduce1 (fn [m [msig meth]] (let [rtype (peek msig) argsig (pop msig)] @@ -32,31 +33,32 @@ {} coll))) (defn proxy-name {:tag String} [^Class super interfaces] (let [inames (into1 (sorted-set) (map #(.getName ^Class %) interfaces))] (apply str (.replace (str *ns*) \- \_) ".proxy" (interleave (repeat "$") (concat [(.getName super)] (map #(subs % (inc (.lastIndexOf ^String % "."))) inames) [(Integer/toHexString (hash inames))]))))) (defn- generate-proxy [^Class super interfaces] (let [cv (new ClassWriter (. ClassWriter COMPUTE_MAXS)) - cname (.replace (proxy-name super interfaces) \. \/) ;(str "clojure/lang/" (gensym "Proxy__")) + pname (proxy-name super interfaces) + cname (.replace pname \. \/) ;(str "clojure/lang/" (gensym "Proxy__")) ctype (. Type (getObjectType cname)) iname (fn [^Class c] (.. Type (getType c) (getInternalName))) fmap "__clojureFnMap" totype (fn [^Class c] (. Type (getType c))) to-types (fn [cs] (if (pos? (count cs)) (into-array (map totype cs)) (make-array Type 0))) super-type ^Type (totype super) imap-type ^Type (totype IPersistentMap) ifn-type (totype clojure.lang.IFn) obj-type (totype Object) sym-type (totype clojure.lang.Symbol) rt-type (totype clojure.lang.RT) ex-type (totype java.lang.UnsupportedOperationException) gen-bridge @@ -136,30 +138,46 @@ ;add ctors matching/calling super's (doseq [^Constructor ctor (. super (getDeclaredConstructors))] (when-not (. Modifier (isPrivate (. ctor (getModifiers)))) (let [ptypes (to-types (. ctor (getParameterTypes))) m (new Method "" (. Type VOID_TYPE) ptypes) gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)] (. gen (visitCode)) ;call super ctor (. gen (loadThis)) (. gen (dup)) (. gen (loadArgs)) (. gen (invokeConstructor super-type m)) (. gen (returnValue)) (. gen (endMethod))))) + ;disable serialization + (when (some #(isa? % Serializable) (cons super interfaces)) + (let [m (. Method (getMethod "void writeObject(java.io.ObjectOutputStream)")) + gen (new GeneratorAdapter (. Opcodes ACC_PRIVATE) m nil nil cv)] + (. gen (visitCode)) + (. gen (loadThis)) + (. gen (loadArgs)) + (. gen (throwException (totype NotSerializableException) pname)) + (. gen (endMethod))) + (let [m (. Method (getMethod "void readObject(java.io.ObjectInputStream)")) + gen (new GeneratorAdapter (. Opcodes ACC_PRIVATE) m nil nil cv)] + (. gen (visitCode)) + (. gen (loadThis)) + (. gen (loadArgs)) + (. gen (throwException (totype NotSerializableException) pname)) + (. gen (endMethod)))) ;add IProxy methods (let [m (. Method (getMethod "void __initClojureFnMappings(clojure.lang.IPersistentMap)")) gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)] (. gen (visitCode)) (. gen (loadThis)) (. gen (loadArgs)) (. gen (putField ctype fmap imap-type)) (. gen (returnValue)) (. gen (endMethod))) (let [m (. Method (getMethod "void __updateClojureFnMappings(clojure.lang.IPersistentMap)")) gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)] (. gen (visitCode)) (. gen (loadThis)) (. gen (dup)) diff --git a/test/clojure/test_clojure/java_interop.clj b/test/clojure/test_clojure/java_interop.clj index 86ba5ca..44b5c70 100644 --- a/test/clojure/test_clojure/java_interop.clj +++ b/test/clojure/test_clojure/java_interop.clj @@ -1,28 +1,29 @@ ; Copyright (c) Rich Hickey. All rights reserved. ; The use and distribution terms for this software are covered by the ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) ; which can be found in the file epl-v10.html at the root of this distribution. ; By using this software in any fashion, you are agreeing to be bound by ; the terms of this license. ; You must not remove this notice, or any other, from this software. ; Author: Frantisek Sodomka (ns clojure.test-clojure.java-interop - (:use clojure.test)) + (:use clojure.test) + (:require [clojure.inspector])) ; http://clojure.org/java_interop ; http://clojure.org/compilation (deftest test-dot ; (.instanceMember instance args*) (are [x] (= x "FRED") (.toUpperCase "fred") (. "fred" toUpperCase) (. "fred" (toUpperCase)) ) (are [x] (= x true) (.startsWith "abcde" "ab") (. "abcde" startsWith "ab") @@ -159,30 +160,58 @@ (testing "That the proxy functions can chain" (are [x y] (= x y) (-> (get-proxy-class Object) construct-proxy (init-proxy {}) (update-proxy {"toString" (fn [_] "chain chain chain")}) str) "chain chain chain" (-> (proxy [Object] [] (toString [] "superfuzz bigmuff")) (update-proxy {"toString" (fn [_] "chain chain chain")}) str) "chain chain chain"))) +;; serialized-proxy can be regenerated using a modified version of +;; Clojure with the proxy serialization prohibition disabled and the +;; following code: +#_(let [baos (java.io.ByteArrayOutputStream.) ] + (with-open [baos baos] + (.writeObject (java.io.ObjectOutputStream. baos) (clojure.inspector/list-model nil))) + (println (apply str (for [c (String. (.toByteArray baos) "ISO-8859-1")] + (if (<= 32 (int c) (int \z)) c (format "\\%03o" (int c))))))) +(def serialized-proxy "\254\355\000\005sr\000Eclojure.inspector.proxy$javax.swing.table.AbstractTableModel$ff19274art\330\266_\010ME\002\000\001L\000\016__clojureFnMapt\000\035Lclojure/lang/IPersistentMap;xr\000$javax.swing.table.AbstractTableModelr\313\3538\256\001\377\276\002\000\001L\000\014listenerListt\000%Ljavax/swing/event/EventListenerList;xpsr\000#javax.swing.event.EventListenerList\2616\306\175\204\352\326D\003\000\000xppxsr\000\037clojure.lang.PersistentArrayMap\3437p\017\230\305\364\337\002\000\002L\000\005_metaq\000\176\000\001[\000\005arrayt\000\023[Ljava/lang/Object;xr\000\033clojure.lang.APersistentMap]\174/\003t r\173\002\000\002I\000\005_hashI\000\007_hasheqxp\000\000\000\000\000\000\000\000pur\000\023[Ljava.lang.Object;\220\316X\237\020s)l\002\000\000xp\000\000\000\006t\000\016getColumnCountsr\000%clojure.inspector$list_model$fn__8816H\252\320\325b\371!+\002\000\000xr\000\026clojure.lang.AFunction>\006p\234\236F\375\313\002\000\001L\000\021__methodImplCachet\000\036Lclojure/lang/MethodImplCache;xppt\000\013getRowCountsr\000%clojure.inspector$list_model$fn__8818-\037I\247\234/U\226\002\000\001L\000\005nrowst\000\022Ljava/lang/Object;xq\000\176\000\017ppt\000\012getValueAtsr\000%clojure.inspector$list_model$fn__8820\323\331\174ke\233\370\034\002\000\002L\000\011get_labelq\000\176\000\024L\000\011get_valueq\000\176\000\024xq\000\176\000\017ppp") + +(deftest test-proxy-non-serializable + (testing "That proxy classes refuse serialization and deserialization" + ;; Serializable listed directly in interface list: + (is (thrown? java.io.NotSerializableException + (-> (java.io.ByteArrayOutputStream.) + (java.io.ObjectOutputStream.) + (.writeObject (proxy [Object java.io.Serializable] []))))) + ;; Serializable included via inheritence: + (is (thrown? java.io.NotSerializableException + (-> (java.io.ByteArrayOutputStream.) + (java.io.ObjectOutputStream.) + (.writeObject (clojure.inspector/list-model nil))))) + ;; Deserialization also prohibited: + (is (thrown? java.io.NotSerializableException + (-> serialized-proxy (.getBytes "ISO-8859-1") + java.io.ByteArrayInputStream. java.io.ObjectInputStream. + .readObject))))) + (deftest test-bases (are [x y] (= x y) (bases java.lang.Math) (list java.lang.Object) (bases java.util.Collection) (list java.lang.Iterable) (bases java.lang.Object) nil (bases java.lang.Comparable) nil (bases java.lang.Integer) (list java.lang.Number java.lang.Comparable) )) (deftest test-supers (are [x y] (= x y) -- 2.9.3