Skip to content

Commit 3d6eb91

Browse files
committed
Fixes for simple-array fixnum
1 parent 3a29f46 commit 3d6eb91

File tree

3 files changed

+65
-11
lines changed

3 files changed

+65
-11
lines changed

benchmarks.lisp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
(with-open-file (str "blarg.bin" :if-exists :supersede :if-does-not-exist :create
6262
:direction :output :element-type '(unsigned-byte 8))
6363
(cl-binary-store:store str data))))))
64+
;;(assert (equalp (cl-binary-store:restore store) data))
6465
(when read
6566
(timed (" READ :" repeats output-size-MB)
6667
(dotimes (x repeats) (cl-binary-store:restore store)))

src/simple-array.lisp

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,23 @@
416416
,@body
417417
(setf (read-storage-offset ,storage) (+ ,original-offset ,reserve-bytes)))))))
418418

419+
(defmacro sap-ref-fixnum (sap offset)
420+
"This is hideous. On SBCL we have stored the data fixnum encoded, which means shifted up
421+
by one so we shift it back to generate an 'unencoded' fixnum. We would normally just have
422+
blitted things back into the array without telling sbcl what we are doing, but in the
423+
presence of potentially malicious data, we have to make sure that our numbers are fixnums,
424+
so we shift them back down which makes it impossible for malicious actor to put bogus dat
425+
in the array sap. On non sbcl we just check to make sure things are fixnums because we
426+
did not blit the data out"
427+
#+sbcl
428+
`(ash (signed-sap-ref-64 ,sap ,offset) -1)
429+
#-sbcl
430+
(let ((a (gensym)))
431+
`(let ((,a (signed-sap-ref-64 ,sap ,offset)))
432+
(if (typep ,a 'fixnum)
433+
,a
434+
(unexpected-data "non fixnum in fixnum array")))))
435+
419436
(defun restore-simple-specialized-vector (storage)
420437
(declare (optimize (speed 3) (safety 1)))
421438
(let ((num-elts (restore-tagged-unsigned-fixnum/interior storage)))
@@ -452,15 +469,28 @@
452469
(bit (reader 1 nil))
453470
(single-float (reader 32 nil sap-ref-single (simple-array single-float (*))))
454471
(double-float (reader 64 nil sap-ref-double (simple-array double-float (*))))
455-
(fixnum (reader 64 t signed-sap-ref-64 (simple-array fixnum (*))))
472+
(fixnum (reader 64 t sap-ref-fixnum (simple-array fixnum (*))))
456473
(otherwise (unexpected-data "array of unexpected type"))))))
457474
#+sbcl
458475
(with-pinned-objects (sv)
459-
(let ((target-sap (vector-sap sv)))
460-
(chunked/read
461-
storage num-bytes
462-
(lambda (source-sap source-sap-offset data-start/bytes data-end-bytes)
463-
(copy-sap target-sap data-start/bytes source-sap source-sap-offset (the fixnum (- data-end-bytes data-start/bytes)))))))
476+
(multiple-value-bind (type bits-per-elt)
477+
(encoded-element-type-to-type/packing encoded-element-info)
478+
(declare (ignorable bits-per-elt))
479+
(cond
480+
((eq type 'fixnum)
481+
;; We have to be careful here, as just blitting potentially malicious 64
482+
;; bit data into a simple-array fixnum (*) can cause issues.
483+
;; Now the problem is that we already "fixnum encoded" the data if it is
484+
;; correct, which means that it needs to be un-fixnum-ized
485+
(reader 64 t sap-ref-fixnum (simple-array fixnum (*))))
486+
(t
487+
(let ((target-sap (vector-sap sv)))
488+
;; OK, so we have a problem here --- if the fixnums aren't actually fixnums
489+
;; then we end up screwing things up.
490+
(chunked/read
491+
storage num-bytes
492+
(lambda (source-sap source-sap-offset data-start/bytes data-end-bytes)
493+
(copy-sap target-sap data-start/bytes source-sap source-sap-offset (the fixnum (- data-end-bytes data-start/bytes))))))))))
464494
sv))))
465495

466496
;; for ccl ccl::array-data-and-offset would be fine... it's been a stable interface
@@ -511,8 +541,19 @@
511541
(type-of sa) num-bytes array-dimensions encoded-element-info)
512542
(with-pinned-objects (sa)
513543
(let ((target-sap (array-sap sa)))
514-
(chunked/read
515-
storage num-bytes
516-
(lambda (source-sap source-sap-offset data-start/bytes data-end-bytes)
517-
(copy-sap target-sap data-start/bytes source-sap source-sap-offset (- data-end-bytes data-start/bytes))))))
544+
(multiple-value-bind (type bits-per-elt)
545+
(encoded-element-type-to-type/packing encoded-element-info)
546+
(declare (ignorable bits-per-elt))
547+
(cond
548+
((eq type 'fixnum)
549+
;; We have to be careful here, as just blitting potentially malicious 64
550+
;; bit data into a simple-array fixnum (*) can cause issues.
551+
(sb-kernel:with-array-data ((sv sa) (start) (end))
552+
(declare (ignore start end))
553+
(reader 64 t sap-ref-fixnum (simple-array fixnum (*)))))
554+
(t
555+
(chunked/read
556+
storage num-bytes
557+
(lambda (source-sap source-sap-offset data-start/bytes data-end-bytes)
558+
(copy-sap target-sap data-start/bytes source-sap source-sap-offset (- data-end-bytes data-start/bytes)))))))))
518559
sa)))

test/cl-binary-store-tests.lisp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -679,11 +679,23 @@
679679
(let ((a (make-array (file-length str))))
680680
(read-sequence a str)
681681
a))))
682-
(loop repeat 10000
682+
(loop repeat 100 ;;10000
683683
with input = (make-array (random 100) :element-type '(unsigned-byte 8))
684684
do (loop for i fixnum below (length input) do (setf (aref input i) (random 256)))
685685
do (try input))
686686
(loop repeat 10
687687
with input = (make-array (random 1000000) :element-type '(unsigned-byte 8))
688688
do (loop for i fixnum below (length input) do (setf (aref input i) (random 256)))
689689
do (try input))))
690+
691+
(define-test simple-array-fixnum-malicious
692+
;; The below is a non-fixnum claiming to be in a fixnum array
693+
(finish
694+
(handler-case
695+
(cl-binary-store::restore #(21 5 3 127 127 127 127 127 127 127 127))
696+
(invalid-input-data ())))
697+
(finish
698+
(handler-case
699+
(cl-binary-store::restore #(21 5 3 127 127 127 127 127 127 127 127))
700+
(invalid-input-data ()))))
701+

0 commit comments

Comments
 (0)