master: x86-64, struct-by-value: don't read past input struct

stassats via Sbcl-commits <[email protected]>
Newsgroups gmane.lisp.steel-bank.cvs
Message-ID <[email protected]>
The branch "master" has been updated in SBCL:
       via  e3d6e48da20817522949c41ffc44ea5ff805d9b4 (commit)
      from  fa6a16b5ecbe2e91c8450be4273a1cdebc8d21f7 (commit)

- Log -----------------------------------------------------------------
commit e3d6e48da20817522949c41ffc44ea5ff805d9b4
Author: Jesse Bouwman <[email protected]>
Date:   Sat Apr 25 08:11:11 2026 -0700

    x86-64, struct-by-value: don't read past input struct
---
 src/compiler/x86-64/c-call.lisp | 65 ++++++++++++++++++++++++++++++++---------
 1 file changed, 51 insertions(+), 14 deletions(-)

diff --git a/src/compiler/x86-64/c-call.lisp b/src/compiler/x86-64/c-call.lisp
index a728bf225..e5af514fe 100644
--- a/src/compiler/x86-64/c-call.lisp
+++ b/src/compiler/x86-64/c-call.lisp
@@ -286,19 +286,52 @@ Floats are passed in integer registers."
 ;;; VOPs for struct argument passing
 ;;; These VOPs load eightbytes from a struct SAP into target registers
 
+(defun emit-load-partial-int (target sap offset size temp)
+  (ecase size
+    (8 (inst mov :qword target (ea offset sap)))
+    (4 (inst mov :dword target (ea offset sap))) ; zero-extends
+    (2 (inst movzx '(:word :qword) target (ea offset sap)))
+    (1 (inst movzx '(:byte :qword) target (ea offset sap)))
+    (3
+     (inst movzx '(:word :qword) target (ea offset sap))
+     (inst movzx '(:byte :qword) temp   (ea (+ offset 2) sap))
+     (inst shl temp 16)
+     (inst or target temp))
+    (5
+     (inst mov :dword target (ea offset sap))
+     (inst movzx '(:byte :qword) temp (ea (+ offset 4) sap))
+     (inst shl temp 32)
+     (inst or target temp))
+    (6
+     (inst mov :dword target (ea offset sap))
+     (inst movzx '(:word :qword) temp (ea (+ offset 4) sap))
+     (inst shl temp 32)
+     (inst or target temp))
+    (7
+     (inst mov :dword target (ea offset sap))
+     (inst movzx '(:word :qword) temp (ea (+ offset 4) sap))
+     (inst shl temp 32)
+     (inst or target temp)
+     (inst movzx '(:byte :qword) temp (ea (+ offset 6) sap))
+     (inst shl temp 48)
+     (inst or target temp))))
+
 (define-vop (load-struct-int-arg)
-  (:args (sap :scs (sap-reg)))
-  (:info offset)
+  (:args (sap :scs (sap-reg) :to :save))
+  (:info offset size)
   (:results (target :scs (unsigned-reg signed-reg)))
+  (:temporary (:sc unsigned-reg) temp)
   (:generator 5
-    (inst mov :qword target (ea offset sap))))
+    (emit-load-partial-int target sap offset size temp)))
 
 (define-vop (load-struct-sse-arg)
   (:args (sap :scs (sap-reg)))
-  (:info offset)
+  (:info offset size)
   (:results (target :scs (double-reg single-reg)))
   (:generator 5
-    (inst movsd target (ea offset sap))))
+    (ecase size
+      (4 (inst movss target (ea offset sap)))
+      (8 (inst movsd target (ea offset sap))))))
 
 ;;; VOPs for storing struct result registers to memory
 ;;; These VOPs store result register values back to memory for struct-by-value returns
@@ -317,15 +350,17 @@ Floats are passed in integer registers."
   (:generator 5
     (inst movsd (ea offset sap) value)))
 
-;;; VOP to copy a qword from struct SAP to the C argument stack
-;;; Used for passing large structs (>16 bytes) by value
+;;; VOP to copy a full or partial qword from struct SAP to the C
+;;; argument stack Used for passing large structs (>16 bytes) by value
+
 (define-vop (copy-struct-arg-to-stack)
-  (:args (sap :scs (sap-reg))
+  (:args (sap :scs (sap-reg) :to :save)
          (nsp :scs (any-reg)))
-  (:info src-offset dst-offset)
+  (:info src-offset dst-offset size)
   (:temporary (:sc unsigned-reg :from (:argument 0)) temp)
+  (:temporary (:sc unsigned-reg) temp2)
   (:generator 5
-    (inst mov :qword temp (ea src-offset sap))
+    (emit-load-partial-int temp sap src-offset size temp2)
     (inst mov :qword (ea dst-offset nsp) temp)))
 
 ;;; Arg TN generation for record types
@@ -365,6 +400,7 @@ Floats are passed in integer registers."
    For large structs (>16 bytes), copies to stack per System V AMD64 ABI.
    For small structs, returns a function that emits load VOPs into registers."
   (let* ((classification (classify-struct type))
+         (struct-size (sb-alien::struct-classification-size classification))
          (slots (sb-alien::struct-classification-register-slots classification)))
     (if (or (sb-alien::struct-classification-memory-p classification)
             (let (stack
@@ -380,8 +416,7 @@ Floats are passed in integer registers."
               stack))
         ;; Large struct: copy to stack (System V AMD64 ABI)
         ;; The struct is passed by value on the stack, not by pointer
-        (let* ((size (sb-alien::struct-classification-size classification))
-               (words (ceiling size 8))
+        (let* ((words (ceiling struct-size 8))
                (stack-base (arg-state-stack-frame-size state)))
           ;; Reserve stack slots for the struct
           (incf (arg-state-stack-frame-size state) words)
@@ -391,13 +426,14 @@ Floats are passed in integer registers."
               (loop for i from 0 below words
                     for src-offset = (* i 8)
                     for dst-offset = (* (+ stack-base i) n-word-bytes)
+                    for load-size = (min 8 (- struct-size src-offset))
                     do (sb-c::emit-and-insert-vop
                         call block
                         (sb-c::template-or-lose 'copy-struct-arg-to-stack)
                         (sb-c::reference-tn-list (list sap-tn nsp) nil)
                         nil  ; no results
                         nil  ; insert at end
-                        (list src-offset dst-offset))))))
+                        (list src-offset dst-offset load-size))))))
         ;; Small struct: allocate target TNs and return a function to load into them
         (let ((arg-tns nil)
               (offsets nil)
@@ -427,6 +463,7 @@ Floats are passed in integer registers."
              (let ((sap-tn (sb-c::lvar-tn call block arg)))
                (loop for target-tn in arg-tns
                      for (off . class) in offsets
+                     for load-size = (min 8 (- struct-size off))
                      do (let ((vop (ecase class
                                      (:integer 'load-struct-int-arg)
                                      (:double 'load-struct-sse-arg))))
@@ -436,7 +473,7 @@ Floats are passed in integer registers."
                            (sb-c::reference-tn sap-tn nil)
                            (sb-c::reference-tn target-tn t)
                            nil
-                           (list off)))))))))))
+                           (list off load-size)))))))))))
 
 ;;; VOP to set up hidden struct return pointer in first arg register.
 #+win32

-----------------------------------------------------------------------


hooks/post-receive
-- 
SBCL
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.