Operating System - Linux
1753241 Members
3463 Online
108792 Solutions
New Discussion юеВ

executing an mmaped area fails

 
SOLVED
Go to solution
Larry Liimatainen
New Member

executing an mmaped area fails

Following program do:
1) mmaps an area that is set to executable.
2) copy some NOP instructions into it.
3) calls the executable area

At step three it will fail with SIGILL.
It wont even execute the NOPs. Why ?
I'm expecting an SIGILL after the NOPs where BREAK (zeros) is found, but not before.

Environment: hppa2.0w-hp-hpux11.11 gcc4.1.1


compile with gcc and run (optionally under gdb):

//this code will only work on hp-ux11.11 running on parisc2.0

#include
#include
#include
#include

int main (int argc, char **argv)
{
unsigned int base;
void (*func)();
void * addr = mmap(0,0x1000,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS,-1,0);

if(addr < 0){
fprintf(stderr, "mmap failed\n");
perror("mmap");
exit(-1);
}
base = (unsigned int) addr;
fprintf(stderr, "mmaped to %x\n", base);
*((unsigned int *) base + 0) = 0x08000240;
*((unsigned int *) base + 1) = 0x08000240;
*((unsigned int *) base + 2) = 0x08000240;
*((unsigned int *) base + 3) = 0x08000240;
*((unsigned int *) base + 4) = 0x08000240;
func = (void *) base;
fprintf(stderr, "calling %x\n", (unsigned int) func);
func();
fprintf(stderr, "done\n");
return 0;
}

7 REPLIES 7
Dennis Handly
Acclaimed Contributor
Solution

Re: executing an mmaped area fails

You're doing something tricky, which requires you to read all of the documentation. You have N things wrong with this. Why are you even trying to do this? Why not just use java?

>At step 3) it will fail with SIGILL. It wont even execute the NOPs. Why?

Because you can't call code like that. You can't just make up a plabel out of thin air.

>I'm expecting an SIGILL after the NOPs where BREAK (zeros) is found, but not before.

How did you know where it aborted?

>if (addr < 0){

The proper error check is comparing with MAP_FAILED.

>func = (void*)base;

This is not a valid plabel.

So basically what you have to do is:
0) Read documentation and maybe give up. :-)
1) mmap an area that is set to executable.
2) copy some NOP instructions into it. And have a valid return instruction.
3) Flush ICACHE for the region.
4) Create valid plabel.
5) call the executable area

Here are some documentation links:
http://docs.hp.com/en/B2355-90654/index.html
3-7 Assembly Language Source for flush_cache Function
http://docs.hp.com/en/B2355-90654/ch03s11.html#fig-flush-cache

Unfortunately I don't see anything that talks about 4). At the time it was written, this would magically work but the magic was thrown away for performance.
Larry Liimatainen
New Member

Re: executing an mmaped area fails

Thanks!
Flushing the caches sound reasonable.
Adding following routine and calling it before calling into the mmaped area works.
from c:
call_flush_cache(base);

#define LANGUAGE_ASSEMBLY

.level 2.0
.text

.export call_flush_cache
call_flush_cache:
.proc
.callinfo entry_gr=18,save_rp
.entry

fic %r0(%r26)
fdc %r0(%r26)
sync
bv %r0(%rp)
nop
.exit
.procend
Dennis Handly
Acclaimed Contributor

Re: executing an mmaped area fails

>Adding following routine and calling it before calling into the mmaped area works.
call_flush_cache(base);

This won't work in all cases. You need a start and length so you can flush the whole region. Why not use what was at that link?
Hein van den Heuvel
Honored Contributor

Re: executing an mmaped area fails

Larry,

The question feels like a 'proof of concept' for some tricky solution.

Maybe you want to explain what the real problem is that you are trying to solve?

Generating and activating code on the fly is a cool technique, but rarely needed for 'normal' programming. It may be the right tool to generate special case sort or parse routines, to create virusses, and so on.

There may be easier and more portable solutions (state table) than hard coded instruction stream for an end-of-life-imminent (2008, end of support 2013platform.

fwiw,
Hein.


http://h40059.www4.hp.com/campaigns/golddust/downloads/transition_to_integrity.pdf


Larry Liimatainen
New Member

Re: executing an mmaped area fails

Getting pass that sigill on first address was enough to convince/teach me of why the original code caused sigill when gdb showed me perfectly good code at that memory location.

Dennis, Hein,, thanks for your input. Yes it is an proof of concept. It is an boiled down version of an runtime-loader (like sbcl/cmucl).

My previously attempt of overlapping mmap fails, I cant understand why, each mmap works by itself:

#include
#include
#include
#include
#include

int main (int argc, char **argv)
{
int fd;

void * addr = mmap((void *) 0x60000000, 0x1000000,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,-1,0);
if(addr == MAP_FAILED){
fprintf(stderr, "mmap failed\n");
perror("mmap anon");
exit(-1);
}

fd = open("core", O_RDONLY);
if(fd < 0){
perror("open");
exit(-1);
}
if(mmap((void *) 0x60001000, 0x0001000,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_FILE | MAP_FIXED,fd,0)
== MAP_FAILED){
perror("mmap file");
exit(-1);
}

close(fd);

return 0;
}





Hein van den Heuvel
Honored Contributor

Re: executing an mmaped area fails

> sbcl/cmucl

As in Lisp?

And I guess you already figured out that this is not something you could coerce dlopen / dlsym to do for you?

Maybe with help of the opts argument and RTLD_EXT_TEXT_ADDR and such?

Good luck!
Hein.

Dennis Handly
Acclaimed Contributor

Re: executing an mmaped area fails

>when gdb showed me perfectly good code at that memory location.

Do NOT trust any debugger to tell you that it has "perfectly good code" there.

All debuggers seem to have this problem, xdb, dde and gdb. They don't realize that the hardware is either 64 bit or 96 bit and then they truncate the address to 32 or 64 bit and point you to a location, even if it isn't the right one. (I.e. you need to compare space registers too.) The only correct debugger here is NMDEBUG on MPE/iX.

>My previously attempt of overlapping mmap fails, I cant understand why, each mmap works by itself:

mmap(2) on PA-RISC is very picky. It only wants one mapping for every page range. IPF is more forgiving.

By the way, a "better" NOP is 0x20000000.