Lecture 15: Virtualization (cont.)

Mar ch 5th 2012

Alexander Chang



Page Faults:

When running into a page-fault we must use the page map (as covered last time) to map the virtual address to a real address in order to recover the meaningful data.


Each process gets its own page table in order to maintain isolation from other processes; however, threads in a process only get one page table.


When a page fault occurs a victim page from the RAM cache is picked and then we replace it with the one that we need, marking the old page with FAULT.


Mechanism

Code:


// assume we have a pointer to a function pmap that we can pass

// pmap(virtual address)

// maps a virtual address to a real address


// Each process has it's own pmap code which is why we pass it along as a function

// pointer so that we can change it how we want


// va = virtual address

// valim = virtual address limit

p->swapmap(va) {

return (va > p->valim ? FAULT : p->diskorigin + va);

}


// va = virtual address

// p = process

// ova = old virtual address

// op = old process

pfault(va, p, ova, op) {

if (p->swapmap(va) == FAULT)

kill(p);

else {

(op, ova) = removal_policy(); // removes the old process and virtual

pa = op->pmap(ova); // address via some removal policy

// reassign old pagemap to new process

// psuedo code

// write pa block to disk @ op->swapmap(ova);

// op->pmap(ova) = FAULT;

// read pa block to disk @ p->swapmap(va);

// p->pmap(va) = pa;

}

}


Policy

Removal Policy:

Q: Which page to choose as a victim?
A0: Pick a victim at random
(works if memory accesses truly are random)
But: LOCALITY OF REFERENCE common
A1: FIFO – change page that's been in RAM the longest


*Note:

Traces from real systems to test: list of va accesses from a real program → sequence of accessed virtual page numbers. Apply algorithm to traces.


Traces

FIFO page faults


Oracle and LRU page faults

Problem: How to take track?

LRU implementation

Approximately: Invalidate PT on clock interrupt.

If the page is there, then good. If not, then just choose one that is
still marked FAULT.


Improving Performance:

A: Demand Paging
On Startup: read just main page & then execute
+ idea: start faster (put up a splash screen)
– more page fault code executed
– more disk arm movement in a multi-process ap

Permissions with dirty bit

B: Don't write a victim page to disk if it's never been written to → dirty bit – often in hardware, if not mark page as invalid → kernel traps → records dirty bit
Mark each page without write access even if they have write access. Once
the program wants to execute a write, then the program will trap and
see that the program actually
does have write access and flips the dirty
bit and the write access bit.

To prevent buffer overflows we can simply make it so that everything in
the stack has read/write access but no execution access.


fork()

Copies all of process's virtual memory.

How to speed up?
1) don't need to copy read only areas.
2) don't need to copy writable pages either! → copy on write – on trap, re-allows write permissions and copies page later

1) Simply have the page table of the child process point to the same page as the parent process without write permissions.

2) Similar to 1. Have the page table of the child process point to the same page as the parent process, however, remove write permissions from both. This will continue to work fine until one of the processes want to write, then the kernel will trap and find out that the process actually does have write permissions and will then copy a new page and change the page table to reflect the change. Then both pages will then have write permissions enabled again.


vfork
Parent + Child share memory (and therefore PTEs)
parent is frozen until child
exits or execs

p = vfork();

if (!p) {

// fiddle a bit -- should not write to globally visible RAM

exec(--);

exit(--);

}


Networking:

Distributed Systems & RPC (Remote Procedure Call)
transfer(10, a b); // body of function runs on some other
// machine

RPCs differ from ordinary function calls & syscalls
– slower yet (speed of light problems)
+ have hard modularity (not sharing address space)
– no call by reference (always call by value)
± don't need to agree on their own architecture (SPARC – big endian vs. Intel – little endian)
(requires data format to be known)

Caller:
Marshalling: must marshal the data.
Re-arranges the data into a agreed upon format.

Callee:
Must unmarshal the data.
Re-arranges the marshalled data into a data that the callee can process and send back.

Data marshalling

Aside:

ROP (Return Oriented Program) – Buffer overflow attack
Assuming that application has a buffer overflow vulnerability that allows us to write whatever we want on the stack. Even if we don't have execution permissions we can still execute whatever code we want if target program is large enough and we can analyze the main program.

Idea: Use small snippets of code to do what we want
By editing the return addresses we can continually jump to and from the main application and use small executable instructions which edit the registers in order to execute what we want in small snippets at a time.

ROP -- Buffer overflow attack implementation