|
use NativeCall; |
|
use NativeCall::Types; |
|
|
|
use Terminal::ANSIColor; |
|
|
|
my constant __NR_userfaultfd = 323; |
|
|
|
sub syscall(int32 $omnomnom, int32 $foo --> int32) is native(Str) { }; |
|
sub ioctl(int32 $fd, int64 $request, OpaquePointer $thing --> int32) is native(Str) { }; |
|
|
|
class uffdio_register is repr<CStruct> is rw { |
|
has uint64 $.start; |
|
has uint64 $.len; |
|
has uint64 $.mode; |
|
has uint64 $.ioctls; |
|
} |
|
|
|
class uffd_zeropage is repr<CStruct> is rw { |
|
has uint64 $.start; |
|
has uint64 $.len; |
|
has uint64 $.mode; |
|
|
|
has int64 $.returnval; |
|
|
|
method gist { |
|
"UFFD_zeropg[dst: $.dst.fmt("0x%x"), len: $.len.fmt("0x%x")" |
|
~ ($.mode ?? ", mode: $.mode" !! "") |
|
~ ($.returnval ?? ", ret: $.returnval.fmt("0x%x")" !! "") |
|
~ "]"; |
|
} |
|
} |
|
|
|
class uffd_copy is repr<CStruct> is rw { |
|
has uint64 $.dst; |
|
has uint64 $.src; |
|
has uint64 $.len; |
|
has uint64 $.mode; |
|
|
|
has int64 $.returnval; |
|
|
|
method gist { |
|
"UFFD_copy[dst: $.dst.fmt("0x%x"), src: $.src.fmt("0x%x"), len: $.len.fmt("0x%x")" |
|
~ ($.mode ?? ", mode: $.mode" !! "") |
|
~ ($.returnval ?? ", ret: $.returnval.fmt("0x%x")" !! "") |
|
~ "]"; |
|
} |
|
} |
|
|
|
class uffd_continue is repr<CStruct> is rw { |
|
has uint64 $.start; |
|
has uint64 $.len; |
|
has uint64 $.mode; |
|
|
|
has int64 $.returnval; |
|
|
|
method gist { |
|
"UFFD_copy[dst: $.dst.fmt("0x%x"), src: $.src.fmt("0x%x")]" |
|
} |
|
} |
|
|
|
class uffd_writeprotect is repr<CStruct> is rw { |
|
has uint64 $.start; |
|
has uint64 $.len; |
|
has uint64 $.mode; |
|
|
|
method gist { |
|
"UFFD_wprot[start: $.start.fmt("0x%x"), len: $.len.fmt("0x%x")" |
|
~ ($.mode ?? ", mode: $.mode" !! "") |
|
~ "]"; |
|
} |
|
} |
|
|
|
class uffdio_api is repr<CStruct> is rw { |
|
has uint64 $.api; |
|
has uint64 $.features; |
|
has int64 $.ioctls; |
|
} |
|
|
|
class uffd_pagefault is repr<CStruct> is rw { |
|
has uint64 $.flags; |
|
has uint64 $.address; |
|
has uint32 $.ptid; |
|
|
|
has uint32 $.padding; |
|
} |
|
|
|
my constant UFFD_FEATURE_PAGEFAULT_FLAG_WP = (1 +< 0); |
|
my constant UFFD_FEATURE_EVENT_FORK = (1 +< 1); |
|
my constant UFFD_FEATURE_EVENT_REMAP = (1 +< 2); |
|
my constant UFFD_FEATURE_EVENT_REMOVE = (1 +< 3); |
|
my constant UFFD_FEATURE_MISSING_HUGETLBFS = (1 +< 4); |
|
my constant UFFD_FEATURE_MISSING_SHMEM = (1 +< 5); |
|
my constant UFFD_FEATURE_EVENT_UNMAP = (1 +< 6); |
|
my constant UFFD_FEATURE_SIGBUS = (1 +< 7); |
|
my constant UFFD_FEATURE_THREAD_ID = (1 +< 8); |
|
my constant UFFD_FEATURE_MINOR_HUGETLBFS = (1 +< 9); |
|
my constant UFFD_FEATURE_MINOR_SHMEM = (1 <+ 10); |
|
my constant UFFD_FEATURE_EXACT_ADDRESS = (1 <+ 11); |
|
my constant UFFD_FEATURE_WP_HUGETLBFS_SHMEM = (1 <+ 12); |
|
my constant UFFD_FEATURE_WP_UNPOPULATED = (1 <+ 13); |
|
my constant UFFD_FEATURE_POISON = (1 <+ 14); |
|
my constant UFFD_FEATURE_WP_ASYNC = (1 <+ 15); |
|
my constant UFFD_FEATURE_MOVE = (1 <+ 16); |
|
|
|
my constant UFFD_EVENT_PAGEFAULT = 0x12; |
|
my constant UFFD_EVENT_FORK = 0x13; |
|
my constant UFFD_EVENT_REMAP = 0x14; |
|
my constant UFFD_EVENT_REMOVE = 0x15; |
|
my constant UFFD_EVENT_UNMAP = 0x16; |
|
|
|
my constant _UFFDIO_REGISTER = 0x00; |
|
my constant _UFFDIO_UNREGISTER = 0x01; |
|
my constant _UFFDIO_WAKE = 0x02; |
|
my constant _UFFDIO_COPY = 0x03; |
|
my constant _UFFDIO_ZEROPAGE = 0x04; |
|
my constant _UFFDIO_MOVE = 0x05; |
|
my constant _UFFDIO_WRITEPROTECT = 0x06; |
|
my constant _UFFDIO_CONTINUE = 0x07; |
|
my constant _UFFDIO_POISON = 0x08; |
|
my constant _UFFDIO_API = 0x3F; |
|
|
|
my constant UFFDIO_REGISTER_MODE_MISSING = 1 +< 0; |
|
my constant UFFDIO_REGISTER_MODE_WP = 1 +< 1; |
|
my constant UFFDIO_REGISTER_MODE_MINOR = 1 +< 2; |
|
|
|
my constant UFFDIO_COPY_MODE_DONTWAKE = 1 +< 0; |
|
my constant UFFDIO_COPY_MODE_WP = 1 +< 1; |
|
|
|
my constant UFFDIO = 0xAA; |
|
|
|
my constant UFFDIO_API = 0xc018aa3f; |
|
my constant UFFDIO_ZEROPAGE = 0xc020aa04; |
|
my constant UFFDIO_CONTINUE = 0xc020aa07; |
|
my constant UFFDIO_COPY = 0xc028aa03; |
|
my constant UFFDIO_WAKE = 0x8010aa02; |
|
my constant UFFDIO_WRITEPROTECT = 0xc018aa06; |
|
my constant UFFDIO_REGISTER = 0xc020aa00; |
|
|
|
class uffd_msg is repr<CStruct> is rw { |
|
has uint8 $.event; |
|
|
|
has uint8 $.reserved1; |
|
has uint16 $.reserved2; |
|
has uint32 $.reserved3; |
|
|
|
# needs to be a union if we want more than just this kind |
|
HAS uffd_pagefault $.event_pagefault; |
|
} |
|
|
|
sub c_read(int32 $fd, buf8 $buf, uint64 $len --> int32) is native(Str) is symbol("read") { } |
|
|
|
sub mmap(int64 $addr, int64 $len, int32 $prot, int32 $flags, int32 $fd, int64 $offs --> int64) is native(Str) { } |
|
|
|
sub memset(int64 $addr, int32 $c, int64 $len --> int64) is native(Str) { } |
|
|
|
sub memcpy(buf8 $tgt, int64 $src_addr, int64 $len --> int64) is native(Str) { } |
|
sub rmemcpy(int64 $tgt_addr, buf8 $src, int64 $len --> int64) is native(Str) is symbol("memcpy") { } |
|
|
|
# my @colors = (^255).combinations(3).roll(*).map({ .join(",") }); |
|
my @colors = ((16, 32 ...^ 256) X (16, 32 ...^ 256) X (16, 32 ...^ 256)).grep({ .sum >= 400 && .minmax.elems > 216 }).map({ .join(",") }).pick(*); |
|
# $*OUT.put(colored($_, $_)) for @colors; |
|
|
|
sub say(*@text) { |
|
my $ts = ( |
|
colored(.hour.fmt("%02d"), "128,128,128"), |
|
colored(.minute.fmt("%02d"), "196,196,196"), |
|
colored(.whole-second.fmt("%02d"), "200,200,200") ~ "." |
|
~ colored((.second - .whole-second).fmt("%3.3f").substr(2), "255,255,255")).join(":") |
|
|
|
given DateTime.now; |
|
$*OUT.put($ts ~ " " ~ colored($*THREAD.Str.subst("Thread", "Thd") ~ ": " ~ @text>>.gist.join(""), @colors[$*THREAD.id])); |
|
} |
|
|
|
say "going to start now"; |
|
|
|
my $uffdt = Thread.start(:name<uffd>, sub { |
|
try { |
|
my int32 $uffd = syscall(__NR_userfaultfd, 0); |
|
|
|
my $mmapped_size = 0x100000; |
|
|
|
my buf8 $buf .= allocate(4096); |
|
|
|
my CArray[uint8] $copysrc .= new("/usr/share/dict/words".IO.slurp(:bin)); |
|
|
|
sub pump() { |
|
my int64 $rcnt = c_read($uffd, $buf, 32); |
|
# say $buf.head($rcnt); |
|
say "userfaultfd event:"; |
|
say " - event ", $buf.read-uint8(0).fmt("0x%02x"), |
|
" - flags ", (my uint64 $flags = $buf.read-uint64(8)).fmt("0b%b"), |
|
" - addr ", (my uint64 $addr = $buf.read-uint64(16)).fmt("0x%x"); |
|
# we're not getting thread IDs ;( |
|
#" - ptid ", $buf.read-uint32(24).fmt("0x%x"); |
|
|
|
if $flags == 0 { |
|
say "event was MISSING: do copy + write-protect"; |
|
my uffd_copy $cpy .= new(:dst($addr), :src(nativecast(OpaquePointer, $copysrc)), :len(4096), :mode(UFFDIO_COPY_MODE_WP)); |
|
say $cpy; |
|
say "ioctl result: $_" andthen say $cpy if ioctl($uffd, UFFDIO_COPY, nativecast(OpaquePointer, $cpy)); |
|
} |
|
elsif $flags == 0b11 { |
|
#say "reacting with zeropage"; |
|
#my uffd_zeropage $zp .= new(:start($addr), :len(4096), :mode(0)); |
|
#say $zp.raku; |
|
#say "ioctl result: ", ioctl($uffd, UFFDIO_ZEROPAGE, nativecast(OpaquePointer, $zp)); |
|
#say $zp.raku; |
|
#if $zp.returnval == -17 { |
|
# say "event was WP: remove writeprotect"; |
|
my uffd_writeprotect $wp .= new(:start($addr), :len(4096), :mode(0)); |
|
say $wp; |
|
say "ioctl result: $_" if ioctl($uffd, UFFDIO_WRITEPROTECT, nativecast(OpaquePointer, $wp)); |
|
#say $wp.raku; |
|
#} |
|
} |
|
else { |
|
say "don't know what to do with these flags?"; |
|
} |
|
} |
|
|
|
if $uffd == 0 { |
|
say "couldn't create userfaultfd (do you need to set /proc/sys/vm/unprivileged_userfaultfd to 1?)"; |
|
exit(1); |
|
} |
|
my uffdio_api $api .= new(:api(0xAA), :features(0)); |
|
say "the api i built looks like $api.raku()"; |
|
my $ioctlresult = ioctl($uffd, 3222841919, nativecast(OpaquePointer, $api)); |
|
say "api ioctl returned $ioctlresult, api is now $api.raku()"; |
|
say "features: ", $api.features.base(2); |
|
$api.features = UFFD_FEATURE_PAGEFAULT_FLAG_WP +| UFFD_FEATURE_WP_UNPOPULATED; |
|
say "setting my own features to $api.features() aka $api.features().base(2)"; |
|
$ioctlresult = ioctl($uffd, 3222841919, nativecast(OpaquePointer, $api)); |
|
say "api ioctl returned $ioctlresult, api is now $api.raku()"; |
|
|
|
say "mmap result: ", (my $baseaddr = mmap(0x30000, $mmapped_size, 0x1 +| 0x2, 0x02 +| 0x20, 0, 0)).base(16); |
|
|
|
#my @maps = "/proc/self/maps".IO.lines.>>.words>>.[0]>>.split("-")>>.parse-base(16); |
|
#.fmt("%10x", "-").say for @maps; |
|
#my uffdio_register @regs = uffdio_register.new(:mode(1)) xx +@maps; |
|
#for @maps Z @regs -> ($m, $r) { |
|
# $r.start = $m[0]; |
|
# $r.len = $m[1]; |
|
#} |
|
my @regs = uffdio_register.new(:start($baseaddr), :len($mmapped_size), :mode(UFFDIO_REGISTER_MODE_WP +| UFFDIO_REGISTER_MODE_MISSING)); |
|
say "regs prepared"; |
|
for @regs { |
|
$ioctlresult = ioctl($uffd, UFFDIO_REGISTER, nativecast(OpaquePointer, $_)); |
|
say "register ioctl returned $ioctlresult, reg is now ", $_; |
|
} |
|
for ^4 -> $dood { |
|
Thread.start(:name("Dood $dood"), sub { |
|
sleep $dood / 3; |
|
loop { |
|
my int64 $targetaddr = $baseaddr + ($mmapped_size - 0x1000).rand.Int; |
|
say "trying to read memory at that location: ", $targetaddr, " ", $targetaddr.base(16); |
|
my buf8 $tb .= allocate(0x10); |
|
memcpy($tb, $targetaddr, 0x0f); |
|
say "read: ", $tb.list.fmt("%02x", " "); |
|
@$tb = (^255).pick xx $tb.elems; |
|
say "writing buffer back to the same address ..."; |
|
rmemcpy($targetaddr, $tb, 0x10); |
|
say "done! sleeping now"; |
|
sleep 2; |
|
} |
|
}); |
|
} |
|
pump() while True; |
|
CATCH { |
|
.say; |
|
} |
|
} |
|
}); |
|
|
|
$uffdt.finish; |