Sindbad~EG File Manager
// uconversions tapset -- BPF version
// Copyright (C) 2018-2022 Red Hat Inc.
//
// This file is part of systemtap, and is free software. You can
// redistribute it and/or modify it under the terms of the GNU General
// Public License (GPL); either version 2, or (at your option) any
// later version.
// TODO: Really need a solution for redundant docstrings.
// TODO: Use jump_to_catch/register_error for user_long{,_warn} type functions.
function user_string_n:string (addr:long, n:long)
{ /* pure */ /* bpf */
// TODO: Does not provide address in error message.
return user_string_n(addr, n, "user string copy fault")
}
function user_string_n:string (addr:long, n:long, err_msg:string)
%( 1 == 0 %?
{ /* pure */ /* bpf */
/* !!! ACHTUNG !!!
* bpf uses the same bpf_probe_read() helpers for kernel and user
* addresses, on the assumption that the address spaces coincide.
* Which only really works on x86_64 in Current Day.
*
* If the address space is changed, it may return the wrong data.
* TODO PR25168: Fix this as soon as BPF ships proper, separate
* bpf_probe_read_{user,kernel}() helpers.
*/
%( arch == "x86_64" %?
// TODO: Does not use the provided err_msg.
return kernel_string_n(addr, n) // calls probe_read_str()
%:
// TODO: Use error() function.
print("ERROR(unsupported): %s", err_msg)
exit()
%)
}
%:
%{ /* bpf */ /* pure */
/* if (n > BPF_MAXSTRINGLEN) n = BPF_MAXSTRINGLEN; */
jle $n, BPF_MAXSTRINGLEN, skip
mov $n, BPF_MAXSTRINGLEN
/* buf = bpf_stk_alloc(BPF_MAXSTRINGLEN);
buf[0] = 0x0; // guarantee NUL byte
rc = bpf_probe_read_user_str(buf, n, addr); */
skip: alloc, $buf, BPF_MAXSTRINGLEN
stw [$buf+0], 0 /* guarantee NUL byte */
call $rc, probe_read_user_str, $buf, $n, $addr
call -, printf, "hello from the new bpf assembler"
/* TODO <- should work if the helper is named bpf_probe_read_user_str() too */
/* TODO <- substitute probe_read_str automatically for older bpf? */
/* if (rc < 0) error("...", addr); */
jsge $rc, 0, done
call -, printf, "ERROR: string copy fault at %p [man error::fault]\n", $addr
/* TODO <- document stapbpf version of error::fault */
/* TODO <- return value reg should be optional */
/* TODO <- use jump_to_catch error handling? */
call -, printf, "%s", $err_msg
call -, exit
/* return buf; */
done: mov $$, $buf
%}
%)
function user_string_n_warn:string (addr:long, n:long, warn_msg:string)
%( 1 == 1 %?
{ /* pure */ /* bpf */
/* !!! ACHTUNG !!!
* bpf uses the same bpf_probe_read() helpers for kernel and user
* addresses, on the assumption that the address spaces coincide.
* Which only really works on x86_64 in Current Day.
*
* If the address space is changed, it may return the wrong data.
* TODO PR25168: Fix this as soon as BPF ships proper, separate
* bpf_probe_read_{user,kernel}() helpers.
*/
%( arch == "x86_64" %?
return kernel_string_n(addr, n, warn_msg) // calls probe_read_str()
%:
return warn_msg // don't even bother
%)
}
%:
%{ /* bpf */ /* pure */
/* buf = bpf_stk_alloc(BPF_MAXSTRINGLEN);
buf[0] = 0x0; // guarantee NUL byte
rc = bpf_probe_read_user_str(buf, BPF_MAXSTRINGLEN, addr); */
alloc, $buf, BPF_MAXSTRINGLEN;
0x62, $buf, -, -, 0x0; /* stw [$buf+0], 0x0 -- guarantee NUL byte */
call, $rc, probe_read_user_str, $buf, BPF_MAXSTRINGLEN, $addr;
/* if (rc < 0) return err_msg;
return buf; */
0xc5, $rc, -, _err, 0; /* jslt $rc, 0, _err */
0xbf, $$, $buf, -, -; /* mov $$, $buf */
0x05, -, -, _done, -; /* ja _done; */
label, _err;
0xbf, $$, $warn_msg, -, -; /* mov $$, $warn_msg */
label, _done;
%}
%)
function __bpf_probe_read_user_error:long (addr:long, size:long)
%{ /* bpf */ /* pure */
/* if (size > 8) error("...", addr, size); */
0xb5, $size, -, _skip, 8; /* jle $n, 8, _skip */
call, -, printf, "ERROR: attempted to read %ul (>8) bytes from %p into unsigned long\n", $size, $addr;
call, -, exit;
label, _skip;
/* buf = bpf_stk_alloc(8); // size <= 8
*buf = (unsigned long)0x0; // guarantee leading zeroes
rc = bpf_probe_read(buf, size, addr);
*/
alloc, $buf, 8;
0x7a, $buf, -, -, 0x0; /* stdw [buf+0], 0 -- guarantee leading zeroes */
/* TODO PR25168: probe_read is used for both kernel and user memory.
BPF will soon deprecate these in favour of separate functions. */
call, $rc, probe_read, $buf, $size, $addr;
/* call, $rc, probe_read_user, $buf, $size, $addr; */
/* XXX code for testing LITTLE ENDIAN / BIG ENDIAN */
/* 0xb7, $rc, -, -, 0x0;
0x62, $buf, -, -, 0xdeadbeef;
0x62, $buf, -, 4, 0xea7b3375; /* end test */
/* if (rc < 0) error("...", addr); */
0x75, $rc, -, _done, 0; /* jsge $rc, 0, _done */
call, -, printf, "ERROR: user copy fault at %p [man error::fault]\n", $addr; /* TODO document stapbpf version of error::fault */
call, -, exit;
label, _done;
/* return *(unsigned long *)buf; */
0x79, $$, $buf, 0x0, -; /* ldxdw $$, buf */
%}
// XXX I assumed I would need to return buf >> 8*(8-size) here,
// but buf is already correctly aligned for smaller sizes. Hmm.
function __signext:long (src:long, size:long) { /* pure */ /* bpf */
// XXX 2'S COMPLEMENT; assumes upper bits of size are zero
// as provided by __bpf_probe_read_user_error()
// thanks to https://graphics.stanford.edu/~seander/bithacks
bits = 8*size
mask = 1 << (bits-1)
return (src ^ mask) - mask
}
function __bpf_probe_read_user_error_signed (addr:long, size:long) { /* pure */ /* bpf */
return __signext(__bpf_probe_read_user_error(addr, size), size)
}
// TODO We can't be sure of exact lengths of {char,short,int,long}
// so the best guess for now is 1,2,4,8 to match the BPF definitions.
// However, I'm pretty sure sizeof.stp uses some @cast() trickery to
// get the size of a data type. Should we capture it as a macro?
function user_char_error:long (addr:long) { /* pure */ /* bpf */
// XXX: Note that the lkm version is also reading a signed char,
// which gets sign extended to a long just like this. I was surprised.
return __bpf_probe_read_user_error_signed(addr, 1)
}
function user_short_error:long (addr:long) { /* pure */ /* bpf */
return __bpf_probe_read_user_error_signed(addr, 2)
}
function user_ushort_error:long (addr:long) { /* pure */ /* bpf */
return __bpf_probe_read_user_error(addr, 2)
}
function user_int_error:long (addr:long) { /* pure */ /* bpf */
return __bpf_probe_read_user_error_signed(addr, 4)
}
function user_long_error:long (addr:long) { /* pure */ /* bpf */
/* XXX code for testing LITTLE ENDIAN / BIG ENDIAN */
/* printf("1: %lx\n2: %x\n4: %lx\n8: %lx\n",
__bpf_probe_read_user_error(addr, 1),
__bpf_probe_read_user_error(addr, 2),
__bpf_probe_read_user_error(addr, 4),
__bpf_probe_read_user_error(addr, 8)) /* end test */
return __bpf_probe_read_user_error(addr, 8) // XXX no sign extension needed
}
function user_ulong_error:long (addr:long) { /* pure */ /* bpf */
return __bpf_probe_read_user_error(addr, 8)
}
function user_int8_error:long (addr:long) { /* pure */ /* bpf */
return __bpf_probe_read_user_error_signed(addr, 1)
}
function user_uint8_error:long (addr:long) { /* pure */ /* bpf */
return __bpf_probe_read_user_error(addr, 1)
}
function user_int16_error:long (addr:long) { /* pure */ /* bpf */
return __bpf_probe_read_user_error_signed(addr, 2)
}
function user_uint16_error:long (addr:long) { /* pure */ /* bpf */
return __bpf_probe_read_user_error(addr, 2)
}
function user_int32_error:long (addr:long) { /* pure */ /* bpf */
return __bpf_probe_read_user_error_signed(addr, 4)
}
function user_uint32_error:long (addr:long) { /* pure */ /* bpf */
return __bpf_probe_read_user_error(addr, 4)
}
function user_int64_error:long (addr:long) { /* pure */ /* bpf */
return __bpf_probe_read_user_error(addr, 8) // XXX no sign extension needed
}
function user_uint64_error:long (addr:long) { /* pure */ /* bpf */
return __bpf_probe_read_user_error_(addr, 8)
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists