224 lines
10 KiB
Diff
224 lines
10 KiB
Diff
From c8d42afb5ec4fb5e2267aab3fb2d3af4a40df5d9 Mon Sep 17 00:00:00 2001
|
|
From: Chouser <chouser@n01se.net>
|
|
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 "<init>" (. 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
|
|
|