🐛 Handle corrupted PathData segments gracefully instead of crashing

Add nil defaults to all case expressions that match binary segment
type codes so that corrupted/unknown values are skipped instead of
throwing 'No matching clause'. This prevents a React render crash
(triggered via shape-with-open-path? -> get-subpaths -> reduce)
when a PathData buffer contains invalid bytes, e.g. from a WASM
data transfer or deserialization of damaged stored data.

Affected functions: read-segment, impl-walk, impl-reduce,
impl-lookup, to-string-segment*, and the seq/reduce protocol
implementations on both JVM and CLJS PathData types.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
This commit is contained in:
Andrey Antukh 2026-04-08 12:07:07 +00:00 committed by Belén Albeza
parent 0dfa450cc8
commit 2eaa2dc807

View File

@ -131,8 +131,10 @@
1 :move-to 1 :move-to
2 :line-to 2 :line-to
3 :curve-to 3 :curve-to
4 :close-path) 4 :close-path
res (f type c1x c1y c2x c2y x y)] nil)
res (when (some? type)
(f type c1x c1y c2x c2y x y))]
(recur (inc index) (recur (inc index)
(if (some? res) (if (some? res)
(conj! result res) (conj! result res)
@ -156,8 +158,11 @@
1 :move-to 1 :move-to
2 :line-to 2 :line-to
3 :curve-to 3 :curve-to
4 :close-path) 4 :close-path
result (f result index type c1x c1y c2x c2y x y)] nil)
result (if (some? type)
(f result index type c1x c1y c2x c2y x y)
result)]
(if (reduced? result) (if (reduced? result)
result result
(recur (inc index) result))) (recur (inc index) result)))
@ -177,9 +182,11 @@
1 :move-to 1 :move-to
2 :line-to 2 :line-to
3 :curve-to 3 :curve-to
4 :close-path)] 4 :close-path
#?(:clj (f type c1x c1y c2x c2y x y) nil)]
:cljs (^function f type c1x c1y c2x c2y x y)))) (when (some? type)
#?(:clj (f type c1x c1y c2x c2y x y)
:cljs (^function f type c1x c1y c2x c2y x y)))))
(defn- to-string-segment* (defn- to-string-segment*
[buffer offset type ^StringBuilder builder] [buffer offset type ^StringBuilder builder]
@ -219,7 +226,10 @@
(.append ",") (.append ",")
(.append y))) (.append y)))
4 (doto builder 4 (doto builder
(.append "Z")))) (.append "Z"))
;; Skip corrupted/unknown segment types
nil))
(defn- to-string (defn- to-string
"Format the path data structure to string" "Format the path data structure to string"
@ -236,7 +246,8 @@
(.toString builder))) (.toString builder)))
(defn- read-segment (defn- read-segment
"Read segment from binary buffer at specified index" "Read segment from binary buffer at specified index. Returns nil for
corrupted/invalid segment types."
[buffer index] [buffer index]
(let [offset (* index SEGMENT-U8-SIZE) (let [offset (* index SEGMENT-U8-SIZE)
type (buf/read-short buffer offset)] type (buf/read-short buffer offset)]
@ -268,7 +279,10 @@
:c2y (double c2y)}}) :c2y (double c2y)}})
4 {:command :close-path 4 {:command :close-path
:params {}}))) :params {}}
;; Return nil for corrupted/unknown segment types
nil)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TYPE: PATH-DATA ;; TYPE: PATH-DATA
@ -320,8 +334,10 @@
(when (pos? size) (when (pos? size)
((fn next-seq [i] ((fn next-seq [i]
(when (< i size) (when (< i size)
(cons (read-segment buffer i) (let [segment (read-segment buffer i)]
(lazy-seq (next-seq (inc i)))))) (if (some? segment)
(cons segment (lazy-seq (next-seq (inc i))))
(next-seq (inc i))))))
0))) 0)))
clojure.lang.IReduceInit clojure.lang.IReduceInit
@ -329,7 +345,10 @@
(loop [index 0 (loop [index 0
result start] result start]
(if (< index size) (if (< index size)
(let [result (f result (read-segment buffer index))] (let [segment (read-segment buffer index)
result (if (some? segment)
(f result segment)
result)]
(if (reduced? result) (if (reduced? result)
@result @result
(recur (inc index) result))) (recur (inc index) result)))
@ -407,7 +426,10 @@
(read-segment buffer 0) (read-segment buffer 0)
nil)] nil)]
(if (< index size) (if (< index size)
(let [result (f result (read-segment buffer index))] (let [segment (read-segment buffer index)
result (if (some? segment)
(f result segment)
result)]
(if (reduced? result) (if (reduced? result)
@result @result
(recur (inc index) result))) (recur (inc index) result)))
@ -417,7 +439,10 @@
(loop [index 0 (loop [index 0
result start] result start]
(if (< index size) (if (< index size)
(let [result (f result (read-segment buffer index))] (let [segment (read-segment buffer index)
result (if (some? segment)
(f result segment)
result)]
(if (reduced? result) (if (reduced? result)
@result @result
(recur (inc index) result))) (recur (inc index) result)))
@ -446,8 +471,10 @@
(when (pos? size) (when (pos? size)
((fn next-seq [i] ((fn next-seq [i]
(when (< i size) (when (< i size)
(cons (read-segment buffer i) (let [segment (read-segment buffer i)]
(lazy-seq (next-seq (inc i)))))) (if (some? segment)
(cons segment (lazy-seq (next-seq (inc i))))
(next-seq (inc i))))))
0))) 0)))
cljs.core/IPrintWithWriter cljs.core/IPrintWithWriter