A more robust raw OpenBSD syscall demo
nullprogram.com
nullprogram.com
.global _start
.data
what: .string "hello\n"
.set len, .-what - 1
.text
_start:
mov w0, 1
adr x1, what
mov w2, len
mov w8, 4
99: svc 0
dsb nsh
isb
mov w8, 1
98: svc 0
.section .openbsd.syscalls, ""
.long 99b, 4
.long 98b, 1
.section .note.openbsd.ident, "a"
.long 8, 4, 1
.string "OpenBSD"
.long 0Mixing C is helpful because most code doesn't need to be written in raw assembly.
I can grasp the example, but back in my days, if we wanting something faster than interpreted BASIC, Assembly was the way.
Folks nowadays start with Python and JavaScript.
It may happen because without "dsb nsh" (Data Synchronization Barrier) and "isb" (Instruction Synchronization Barrier), QEMU may continue execution before the syscall fully completes, which causes a segfault or UB, but I am not entirely sure.
"dsb nsh" and "isb" ensures correct execution order which prevents speculative execution issues.
https://github.com/openbsd/src/commit/bbeaada4689520859307d5...
https://github.com/openbsd/src/commit/0c401ffc2a2550c32105ce...
https://github.com/openbsd/src/commit/5ecc9681133f1894e81c38...
If I'm reading the commits correctly, the OpenBSD kernel will skip two instructions after a "svc 0" when returning to userspace, on the assumption that any syscall comes from libc and therefore has "dsb nsh; isb" after it.
register long a0 asm("a0") = arg0;
register long syscall_id asm("a7") = n;
asm volatile ("ecall" : "+r"(a0) : "r"(syscall_id));
return a0;
This is an example where a0 is an in/out integer. For memory, change long a0 to a pointer to some struct and add a "m" input or "+m" in/out. It's even easier in other languages like Rust and Zig.asm ( assembler template : output operands (optional) : input operands (optional) : clobbered registers list (optional) );
clobbered register list will give same hints to compiler... register keyword is also a hint not a fixed thing so i cant imagine its really handled differently?
might be wrong, but it seem earier to me to use all the features of the asm call itself rather than outside of it pre-specifying these preferences.
Apparently that is a VC++ only thing.
Who's writing these idioms? I've always seen register variables as an alternative, not the default option, except for those registers which have no constraint code.
Step one would be to ensure that every syscall has a wrapper. Place a uprobe at the start of that wrapper which, when hit, sets a per-thread permission bit and a per-thread-per-syscall permission bit in an eBPF map. Place a corresponding uretprobe that clears the per-thread-per-syscall bit. For each syscall place a kprobe which checks the per-thread table to make sure the thread is one which has enabled the feature, and which then checks to make sure the per-thread-per-syscall bit is set for that syscall. If not, sigkill.
Performance would probably suck but it seems like it would protect the syscall entrypoints enough to do some potentially interesting attack surface reduction. The question is really why you would do that there instead of by attaching to, say, the LSM hooks where you have stronger guarantees vis a vis userspace.
Seems mildly useful if you have a really flexible syscall you can't forbid (ioctl, say) but which you only use for a specific narrow purpose.
It "feels" very low-level in an otherwise very high-level language, but provides enough power to implement an entire userland from initrd up, libc- and asm-free (check out Gokrazy and u-root).
OpenBSD and Go have always been at odds here. Go really wants to produce static executables whenever possible, and do syscalls directly; OpenBSD really wants to prevent user programs from accidentally creating gadgets. I guess they've settled on dynamically linking ld.so?
$ echo 'package main; func main() {}' > nop.go
$ go build ./nop.go
$ DYLD_PRINT_LIBRARIES=1 ./nop 2>&1 | wc -l
44
OpenBSD & macOS philosophies are often surprisingly aligned in certain ways, but OpenBSD is simple, macOS is comprehensive (and - TBF quite bloated).I like what OpenBSD did with pledge&unveil. It gets "the first 80%" of the work done, it's easy for the developer, and invisible to the user.
own linker file and manual linking step imo is key. (i use gcc/ld). if u let gcc spit linker file for modern platform, u can see its full of clutter... most of it unneeded. u can also strip that one down, but i am sure u know what elf sections to put and omit, and you found all the bsd specific ones.
in the linker step u can also add symbols / calculate offsets.
in gcc u can also in the c code use attribute section('bla'). not sure if its handy in this case but maybe it'll ease somewhat these things or bring it back more into C :).
cool example :) remebering struggle tirleesly tryin to find out how to run a raw syscall on openbsd. a lot of man pages, readelfs and headaches i was so happy to get my exit code hahah
So I ended up on the flak tag of this blog[1], but I still can't figure out what it is. I can find no links to any source code, or any service description. Even though the blogger mentions flak being their "signature service".
I'm guessing it's a blogging platform, with ActivityPub support, but I can't find any info about how it's used.