gal
gal

Reputation: 496

What does the usage of mprotect() as an ASM syscall look like with respect to its third argument?

In i386 architecture Linux I know that you can build a syscall by loading the identity of the syscall into EAX and the arguments into EBX, ECX, etc.

I am confused by what the third argument for mprotect would look like in this case; Assuming I wanted to make a memory segment within the binary's allocated memory executable, how does the encoding work for the PROT_EXEC argument (arg 3)? I'm aware that the first two arguments are (1) a pointer to the start of the allocated block, and (2) the length of the allocated block which are relatively easy to conceptualize in ASM (as they are hexadecimal addresses in memory).

How do you format the third argument for mprotect() as an interrupt-issued syscall in i386 assembly on Linux?

Thanks.

Upvotes: 2

Views: 1827

Answers (1)

qdii
qdii

Reputation: 12963

TL;DR: An integer is passed as the third parameter.

Now let's answer the question in the comments. If you open mman-common.h, which should sit in /usr/include/asm-generic, you will find these values.

#define PROT_READ       0x1             /* page can be read */
#define PROT_WRITE      0x2             /* page can be written */
#define PROT_EXEC       0x4             /* page can be executed */
#define PROT_SEM        0x8             /* page may be used for atomic ops */

Just before compiling, the preprocessor replaces your parameters with the numbers above. So if you had this call:

mprotect(myaddress, 256, PROT_READ | PROT_WRITE);

It will be replaced by this code:

mprotect(myaddress, 256, 0x1 | 0x2);

Now look at the values the different parameters can take: they haven’t been chosen randomly, they are powers of two, so that in binary notation they are represented by only one 1 digit and zeros.

PROT_READ  = 0x1 =   00000001
PROT_WRITE = 0x2 =   00000010
PROT_EXEC  = 0x4 =   00000100

Choosing powers of two is handy because when you use a binary OR, the number you obtain combine the two previous values, so both information is contained into the OR-ed number.

PROT_WRITE | PROT_EXEC = 
    00000010
  | 00000100
=   00000110

So now back to our call:

If you had called mprotect(myaddress, 256, PROT_READ | PROT_WRITE), what would have happened is that PROT_READ | PROT_WRITE would have been combined into 0x1 | 0x2, which is 0x3.

Now on the kernel side, suppose PROT_READ | PROT_WRITE was written by the user. The kernel receives the argument 0x3, and wants to check whether PROT_READ was originally written. A way to do that is to write this:

if (PROT_READ & userValue) { }

It works because userValue contains the combined version of PROT_READ and PROT_WRITE, in binary:

PROT_READ & userValue = 
    00000001
&&  00000011
 =  00000001

If the flag was set, this number is non-zero, so the kernel knows that the flagged was passed.

Hope this helps.

Upvotes: 6

Related Questions