Skip to content

The asm! macro incorrectly zeroes high byte register if both high and low byte registers are used for input, and the low byte input value is zero. #97646

Open

Description

@A1-Triard

STR:

Compile the following code for 32-bit target (e.g. i686-unknown-linux-gnu or i686-pc-windows-msvc) in release mode:

use std::arch::asm;

unsafe fn int_21h(al_mode: u8) -> Result<u16, u16> {
    let mut ax: u16;
    let mut flags: u8;
    asm!(
        "int 0x21",
        "mov {ax:x}, ax",
        "lahf",
        ax = out(reg) ax,
        in("ah") 0x3du8,
        in("al") al_mode,
        in("edx") 0u32,
        lateout("ah") flags,
        lateout("al") _,
    );
    if flags & 0x01 == 0 {
        Ok(ax)
    } else {
        Err(ax)
    }
}

fn main() {
    let x = unsafe { int_21h(0) }.unwrap();
    println!("{}", x);
}
$ cargo build --target i686-unknown-linux-gnu --release

Look at the final machine code:

$ objdump -d target/i686-unknown-linux-gnu/release/test_reg_bytes | grep -B 5 "int "

    514a:	5b                     	pop    %ebx
    514b:	b4 3d                	mov    $0x3d,%ah
    514d:	31 d2                	xor    %edx,%edx
    514f:	31 c0                	xor    %eax,%eax
    5151:	81 c3 62 fd 04 00   	add    $0x4fd62,%ebx
    5157:	cd 21                	int    $0x21

AR: As you can see, eax is xored after ah was set to input 0x3d value.

ER: Either the compiler should prevent such high and low byte register usage and refuse to compile such code, either it should not clear ah when it is trying to clear al.

Activity

  1. changed the title [-]The asm! macro incorrectly zeroes high byte register if both high and low byte registers are used for input, and low byte input value is zero.[/-] [+]The asm! macro incorrectly zeroes high byte register if both high and low byte registers are used for input, and the low byte input value is zero.[/+] on Jun 2, 2022
  2. asquared31415 commented on Jun 2, 2022

    @asquared31415
    Contributor

    Minimal reproduction:

    pub fn uwu() {
        unsafe {
            core::arch::asm!("", in("ah") 1_u8, in("al") 0_u8);
        }
    }

    godbolt with target

    Requires opt-level 1 or greater to reproduce otherwise the zeroing of eax gets done first, which is correct. This is possibly a buggy optimization?

    In 1.60.0 this code started being accepted by #93868

    Reproduces in all stable versions since 1.60.0, up to nightly 2022-06-02

  3. talchas commented on Jun 2, 2022

    @talchas

    This is an LLVM breakage, and the equivalent C syntax is

        register char al asm("al") = 0;
        register char ah asm("ah") = 1;
        asm volatile("": : "r"(ah), "r"(al));

    which is broken in both clang and gcc, though in different ways. In clang which write wins depends on the ordering of the constraints like in rust (unsurprising), in gcc it depends on the order of the writes to the register variables, since those are somewhat an actual thing (despite the docs saying no guarantees are made for anything other than their use with inline asm).

  4. added
    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.
    A-inline-assemblyArea: Inline assembly (`asm!(…)`)
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    O-x86_64Target: x86-64 processors (like x86_64-*) (also known as amd64 and x64)
    and removed
    O-x86_64Target: x86-64 processors (like x86_64-*) (also known as amd64 and x64)
    on Jun 6, 2022
  5. added
    O-x86_64Target: x86-64 processors (like x86_64-*) (also known as amd64 and x64)
    O-x86_32Target: x86 processors, 32 bit (like i686-*) (also known as IA-32, i386, i586, i686)
    and removed on Oct 25, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.A-inline-assemblyArea: Inline assembly (`asm!(…)`)C-bugCategory: This is a bug.O-x86_32Target: x86 processors, 32 bit (like i686-*) (also known as IA-32, i386, i586, i686)O-x86_64Target: x86-64 processors (like x86_64-*) (also known as amd64 and x64)T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions