]> xenbits.xensource.com Git - people/royger/xen.git/commitdiff
x86emul: avoid UB shifts in FLDENV/FRSTOR handling
authorJan Beulich <jbeulich@suse.com>
Wed, 30 Apr 2025 06:46:21 +0000 (08:46 +0200)
committerJan Beulich <jbeulich@suse.com>
Wed, 30 Apr 2025 06:46:21 +0000 (08:46 +0200)
16-bit quantities, no matter whether expressed as uint16_t or as
bitfield, will be promoted to plain int before doing any arithmetic on
them. Shifting such values by 16 will therefore shift into the sign bit,
which is UB if that bit becomes set. To account for all reads and all
writes accessing opposite members of the same union, introduce yet more
local variables to reduce the shift counts to 12.

Fixes: be55ed744ed8 ("x86emul: support FLDENV and FRSTOR")
Reported-by: Fabian Specht <f.specht@tum.de>
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Jason Andryuk <jason.andryuk@amd.com>
xen/arch/x86/x86_emulate/blk.c

index 494e6c1e3b5f23c6dfbe2e368de0e5d4b56db8a7..9fddf649ee0d361c9d65c45f40c0f30a7d737ee3 100644 (file)
@@ -81,17 +81,19 @@ int x86_emul_blk(
             if ( !s->rex_prefix )
             {
                 /* Convert 32-bit real/vm86 to 32-bit prot format. */
-                unsigned int fip = fpstate.env.mode.real.fip_lo +
-                                   (fpstate.env.mode.real.fip_hi << 16);
-                unsigned int fdp = fpstate.env.mode.real.fdp_lo +
-                                   (fpstate.env.mode.real.fdp_hi << 16);
+                unsigned int fip = fpstate.env.mode.real.fip_lo & 0xf;
+                unsigned int fcs = (fpstate.env.mode.real.fip_lo >> 4) |
+                                   (fpstate.env.mode.real.fip_hi << 12);
+                unsigned int fdp = fpstate.env.mode.real.fdp_lo & 0xf;
+                unsigned int fds = (fpstate.env.mode.real.fdp_lo >> 4) |
+                                   (fpstate.env.mode.real.fdp_hi << 12);
                 unsigned int fop = fpstate.env.mode.real.fop;
 
-                fpstate.env.mode.prot.fip = fip & 0xf;
-                fpstate.env.mode.prot.fcs = fip >> 4;
+                fpstate.env.mode.prot.fip = fip;
+                fpstate.env.mode.prot.fcs = fcs;
                 fpstate.env.mode.prot.fop = fop;
-                fpstate.env.mode.prot.fdp = fdp & 0xf;
-                fpstate.env.mode.prot.fds = fdp >> 4;
+                fpstate.env.mode.prot.fdp = fdp;
+                fpstate.env.mode.prot.fds = fds;
             }
 
             if ( bytes == sizeof(fpstate.env) )
@@ -121,17 +123,19 @@ int x86_emul_blk(
             else
             {
                 /* Convert 16-bit real/vm86 to 32-bit prot format. */
-                unsigned int fip = env->mode.real.fip_lo +
-                                   (env->mode.real.fip_hi << 16);
-                unsigned int fdp = env->mode.real.fdp_lo +
-                                   (env->mode.real.fdp_hi << 16);
+                unsigned int fip = env->mode.real.fip_lo & 0xf;
+                unsigned int fcs = (env->mode.real.fip_lo >> 4) |
+                                   (env->mode.real.fip_hi << 12);
+                unsigned int fdp = env->mode.real.fdp_lo & 0xf;
+                unsigned int fds = (env->mode.real.fdp_lo >> 4) |
+                                   (env->mode.real.fdp_hi << 12);
                 unsigned int fop = env->mode.real.fop;
 
-                fpstate.env.mode.prot.fip = fip & 0xf;
-                fpstate.env.mode.prot.fcs = fip >> 4;
+                fpstate.env.mode.prot.fip = fip;
+                fpstate.env.mode.prot.fcs = fcs;
                 fpstate.env.mode.prot.fop = fop;
-                fpstate.env.mode.prot.fdp = fdp & 0xf;
-                fpstate.env.mode.prot.fds = fdp >> 4;
+                fpstate.env.mode.prot.fdp = fdp;
+                fpstate.env.mode.prot.fds = fds;
             }
 
             if ( bytes == sizeof(*env) )