Unexpected behavior in simple pointer arithmetic in kernel space C code

I am trying to follow this and this tutorial for training. I came across strange behavior that I still do not understand.

I have the following C code snippet:

void print(const char* str, char* vidptr, TerminalColor color){
    for (unsigned i = 0; str[i] != '\0'; ++i){
        unsigned int j = i*2;
        vidptr[j] = str[i];
        vidptr[j+1] = color;
    }
    return;
}

When I call it with the following parameters:

const char* str = "My string";
char* vidptr = (char*) 0xb8000;
print(str, vidptr, GREY); // GREY is equal to 7

the memory looks like this at the end of the first execution for the body:

0xb8000: 0x07  0x00  0x00  0x00  0x00  0x00  0x00  0x00

when i was expecting:

0xb8000: 0x4D  0x07  0x00  0x00  0x00  0x00  0x00  0x00

Running the program with connecting gdb to gemberver qemu-system-x86_64, I noticed that vidptr[j] = str[i];everything works fine before , then the execution vidptr[j+1] = color;overwrites the first one, as if vidptr [j] is equal to vidptr [j +1]. The code breakdown (as shown by gdb in a split layout) is as follows:

; vidptr[j] = str[i]
0x100144 <print+35>     mov    edx,DWORD PTR [rbp-0x8]
0x100147 <print+38>     mov    rax,QWORD PTR [rbp-0x18]
0x10014b <print+42>     add    rax,rdx
0x10014e <print+45>     mov    ecx,DWORD PTR [rbp-0x4]
0x100151 <print+48>     mov    rdx,QWORD PTR [rbp-0x20]
0x100155 <print+52>     add    rdx,rcx
0x100158 <print+55>     movzx  eax,BYTE PTR [rax]
0x10015b <print+58>     mov    BYTE PTR [rdx],al
; vidptr[j+1] = color
0x10015d <print+60>     mov    eax,DWORD PTR [rbp-0x4]
0x100160 <print+63>     add    eax,0x1
0x100163 <print+66>     mov    edx,eax  ;eax = 0x1
0x100165 <print+68>     mov    rax,QWORD PTR [rbp-0x20] ;rax = 0xb8000
0x100169 <print+72>     add    rax,rdx ;here I observe the following: after I give the ni command to gdb the pc goes to the 
                                       ;equivalent instruction `add eax,edx` but I oddly see the value of rax going to 0xb7fff. 
                                       ;Then I send another ni command to gdb which shows again the instruction pointer to the 
                                       ;next instruction (print+75).
0x10016c <print+75>     mov    edx,DWORD PTR [rbp-0x24] ; here the value of rax is again 0xb8000 instead of 0xb8001
0x10016f <print+78>     mov    BYTE PTR [rax],dl ; here the value written in the previous instruction is overwritten
0x100171 <print+80>     add    DWORD PTR [rbp-0x8],0x1
0x100175 <print+84>     mov    edx,DWORD PTR [rbp-0x8]
0x100178 <print+87>     mov    rax,QWORD PTR [rbp-0x18]
0x10017c <print+91>     add    rax,rdx
0x10017f <print+94>     movzx  eax,BYTE PTR [rax]
0x100182 <print+97>     test   al,al
0x100184 <print+99>     jne    0x100139 <print+24>  

Is there any clue on why this is happening?

gdb:

1: x/i $pc
=> 0x100166 <print+69>: add    rax,rdx
2:x/8xb 0xb8000
0xb8000:        0x71    0x00    0x20    0x00    0x20    0x00    0x20    0x00
4: i = 0
5: j = 0
6: /x $rax = 0xb8000
(gdb) ni
1: x/i $pc
=> 0x100167 <print+70>: add    eax,edx
2:x/8xb 0xb8000
0xb8000:        0x71    0x00    0x20    0x00    0x20    0x00    0x20    0x00
6: /x $rax = 0xb7fff
(gdb) p/x $eax
$3 = 0xb7fff
(gdb) p/x $edx
$4 = 0x1
(gdb) ni
1: x/i $pc
=> 0x100169 <print+72>: mov    edx,DWORD PTR [rbp-0x24]
2:x/8xb 0xb8000
0xb8000:        0x71    0x00    0x20    0x00    0x20    0x00    0x20    0x00
6: /x $rax = 0xb8000
(gdb) ni
1: x/i $pc
=> 0x10016c <print+75>: mov    BYTE PTR [rax],dl
2:x/8xb 0xb8000
0xb8000:        0x71    0x00    0x20    0x00    0x20    0x00    0x20    0x00
6: /x $rax = 0xb8000
(gdb) ni
1: x/i $pc
=> 0x10016e <print+77>: add    DWORD PTR [rbp-0x8],0x1
2:x/8xb 0xb8000
0xb8000:        0x07    0x00    0x20    0x00    0x20    0x00    0x20    0x00
6: /x $rax = 0xb8000
+4

Source: https://habr.com/ru/post/1695291/


All Articles