<?php
namespace Demo;
class OutOfMemory extends \Exception {}
class InvalidFree extends \Exception {}
// This is the system heap, this system only has 128 bytes of memory available to applications
const HEAP_SIZE = 128;
$heap = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF";
// The operating system allocator keeps track of the memory block that have been allocated to each application
$blocks = [];
function malloc($size)
{
global $blocks;
$newPtr = 0;
foreach ($blocks as $ptr => $size) {
// If the gap between $newPtr and the start of this block is big enough to fit the request size,
// allocate the block in between
if ($newPtr + $size <= $ptr) {
$blocks[$newPtr] = $size;
return $newPtr;
}
$newPtr = $ptr + $size;
}
// Check if there's enough space at the end
// When allocating at the end, verify that the requested size won't overflow the heap
if ($newPtr + $size <= HEAP_SIZE) {
$blocks[$newPtr] = $size;
return $newPtr;
}
// There was not a large enough contiguous block on the heap to allocate the requested size
throw new OutOfMemory;
}
function free($ptr)
{
global $blocks;
// Can only free a complete block that has been allocated (can't free a partial one)
if (!isset($blocks[$ptr])) {
throw new InvalidFree;
}
unset($blocks[$ptr]);
}
// strcpy() is an example of a function that works with C strings and relies on the presence of a null terminator
function strcpy($dstPtr, $srcPtr)
{
global $heap;
for ($i = 0; $heap[$srcPtr + $i] !== "\0"; $i++) {
$heap[$dstPtr + 0] = $heap[$srcPtr + $i];
}
}
// Output an arbitrary block from the heap
// Notice that this requires a length argument so it knows when to stop
function output($ptr, $len)
{
global $heap;
for ($i = 0; $ptr + $i < HEAP_SIZE && $i < $len; $i++) {
echo $heap[$ptr + $i];
}
// This is just here to make the output readable
echo "\n\n";
}
// Output a string
// No length argument here, so it must be a valid string
function output_string($ptr)
{
global $heap;
for ($i = 0; $ptr + $i < HEAP_SIZE && $heap[$ptr + $i] !== "\0"; $i++) {
echo $heap[$ptr + $i];
}
// This is just here to make the output readable
echo "\n\n";
}
$myString = malloc(14);
$heap[$myString + 0] = 'h';
$heap[$myString + 1] = 'e';
$heap[$myString + 2] = 'l';
$heap[$myString + 3] = 'l';
$heap[$myString + 4] = 'o';
$heap[$myString + 5] = ',';
$heap[$myString + 6] = ' ';
$heap[$myString + 7] = 'w';
$heap[$myString + 8] = 'o';
$heap[$myString + 9] = 'r';
$heap[$myString + 10] = 'l';
$heap[$myString + 11] = 'd';
$heap[$myString + 12] = '!';
$heap[$myString + 13] = "\0";
// fine, this is a valid string, it has a null terminator
output_string($myString);
$arbitraryBlock = malloc(1);
$heap[$arbitraryBlock] = '#';
// fine, we gave it a sensible length
output($arbitraryBlock, 1);
// but if we do this, we get an overflow and it keeps going until it encounters the next null byte or the end of the heap
output_string($arbitraryBlock);
// things can get weirder when stuff like this happens
free($myString); // release the memory back to the OS
$newString = malloc(9); // this will end up reallocating the same block
$heap[$newString + 0] = 'H';
$heap[$newString + 1] = 'i';
$heap[$newString + 2] = ' ';
$heap[$newString + 3] = 't';
$heap[$newString + 4] = 'h';
$heap[$newString + 5] = 'e';
$heap[$newString + 6] = 'r';
$heap[$newString + 7] = 'e';
// oops! no null terminator
output_string($newString);