2

I am experimenting mixing assembly(x86) and C++. I wrote a procedure in assembly and then called it from C++.

However, when writing the returned value to a local variable I get a write permission violation error.

#include <iostream>


// will return 1 if all ok and 0 if b is 0
extern "C" int integerMulDiv(int a, int b, int* prod, int* quo, int* rem);

int main() {
    int a = 13, b = 4;
    int p, q, r;

    int res = integerMulDiv(a, b, &p, &q, &r);
    std::cout << p << '\t' << q << '\t' << r << std::endl;
    std::cout << res << std::endl << std::endl;

    res = integerMulDiv(31, 0, &p, &q, &r);
    std::cout << p << '\t' << q << '\t' << r << std::endl;
    std::cout << res << std::endl << std::endl;

    return 0;
}

The assembly procedure returns a few values through pointers and an int through RAX.

; Returns : 0 Error (division by 0)
;         : 1 All ok

; *prod = a * b
; *quo  = a / b
; *rem  = a % b
integerMulDiv proc

    push ebp
    mov ebp, esp
    push ebx  ; save ebp and ebx

    xor eax, eax

    mov ecx, [ebp + 8]  ; get a
    mov edx, [ebp + 12] ; get b (the divisor)

    or edx, edx ; check divisor
    jz invalidDivizor

    imul edx, ecx
    mov ebx, [ebp + 16] ; get address of prod
    mov [ebx], edx      ; write prod

    mov eax, ecx
    cdq ; extend to edx
    idiv dword ptr[ebx + 12]

    mov ebx, [ebp + 20] ; get address of quo
    mov [ebp], eax      ; write quo
    mov ebx, [ebp + 24] ; get address of rem
    mov [ebp], edx      ; write rem

    mov eax, 1          ; set success
    jmp returnFromProc

invalidDivizor:
    mov eax, 0          ; set failed

returnFromProc:
    pop ebx
    pop ebp
    ret   ; restore and return

integerMulDiv endp

I get the error after the first call of integerMulDiv, when it tries to write the result in the res variable.

The disassembly looks like this:

    int res = integerMulDiv(a, b, &p, &q, &r);
002D24BD  lea         eax,[r]  
002D24C0  push        eax  
002D24C1  lea         ecx,[q]  
002D24C4  push        ecx  
002D24C5  lea         edx,[p]  
002D24C8  push        edx  
002D24C9  mov         eax,dword ptr [b]  
002D24CC  push        eax  
002D24CD  mov         ecx,dword ptr [a]  
002D24D0  push        ecx  
002D24D1  call        _integerMulDiv (02D133Eh)  
002D24D6  add         esp,14h  
002D24D9  mov         dword ptr [res],eax   <- The #PF happens here

Does anyone know what is happening and why?

  • BTW, `or edx, edx` would be more efficient as `test edx, edx`. [Test whether a register is zero with CMP reg,0 vs OR reg,reg?](https://stackoverflow.com/a/33724806) – Peter Cordes Aug 19 '22 at 06:00

1 Answers1

6

The following section of code stands out to me.

idiv dword ptr[ebx + 12]

mov ebx, [ebp + 20] ; get address of quo
mov [ebp], eax      ; write quo
mov ebx, [ebp + 24] ; get address of rem
mov [ebp], edx      ; write rem

I am not sure you are wanting to divide by the contents of memory 12 bytes after the address of the product. Perhaps you meant [ebp + 12].

After that, you are loading addresses into ebx and then writing values to ebp.

Mikel F
  • 3,567
  • 1
  • 21
  • 33
  • I guess I'm just dumb. I meant to write mov ebx, [ebp + 20] mov [ebx], edx thanks :) – Cristian Rotaru Aug 18 '22 at 19:13
  • @CristianRotaru Not dumb, just normal developer tunnel-vision. At first glance, `ebp` and `ebx` can be easily swapped and not notice down the way. – Mikel F Aug 18 '22 at 19:18
  • @CristianRotaru: Single-stepping with a debugger can often catch such mistakes, e.g. when your function crashes with a bad EBP addressing mode somewhere in the caller, you look for how EBP got messed up. You'll find `pop ebp` loading some small integer, which you'll recognize as the remainder from this test run. Then you go back and look again at the code that's supposed to be storing it, or set a watch point on the memory address of the saved EBP value. – Peter Cordes Aug 19 '22 at 05:59