#OBOS (not vibecoded)
1 messages · Page 36 of 1
dhcp clients are usually using PF_LINK sockets (which are for directly dealing with ethernet frames) due to their particular requirements
was readin the TCP spec
This document specifies the Transmission Control Protocol (TCP). TCP is an important transport-layer protocol in the Internet protocol stack, and it has continuously evolved over decades of use and growth of the Internet. Over this time, a number of changes have been made to TCP as it was specified in RFC 793, though these have only been documen...
why tf is it so long

nah it's easy
mhm...
I'm going to start TCP soon
later today maybe
just need to finish something
TCP is long because of all the stuff that was added i think
SOOOOO easyyy
what, it's just a sliding window and sending acks and naks when you feel like it
yes definitely
if (rand() % 2)
send_ack();
else
send_nak();```
It seems like obos' r8169 driver just works™️ on the r8139
Well the 8169 is just a 1000mbps version of the 8139, right?
Idk something like that
boo
linux has different drivers for them iirc
8139 being more generic and 8169 is a fork of it or something
i don't think OBOS does anything where that would matter tho
I haven't been able to figure out how to communicate with obos when it runs in qemu
So until then
what networking mode did you specify to qemu?
-nic user,model=r8139
iirc you have to port forward
or use a tap device
smth like ,id=net0,hostfwd=tcp::2222-:22
Ywah this is what I used to ssh into astral
You can even port forward on your router and have someone connect to obos
or just support ipv6 lol
you can probably do bridge networking to give qemu an entire ipv6 address
if you can't then add that to qemu
bah qemu networking stoobid
At that point just roll your own isa
ngl anything that would be considered normal traffic would crash obos
fun fact: virtualbox does not have _S3
pg->pagedCount--;
MmH_DerefPage(pg);```
I just found
the stupidest lines of code
in obos
congrats
I love it when bash segfaults:
[ LOG ] User thread 7 SIGSEGV (rip 0x0, cr2 0x0, error code 0x00000014)```


yes definitely all bash's fault
I implemented a mount syscall
(the mount command is some makeshift one I made myself in 5 minutes)
monkey
gnu coreutils when
actually some bsd's coreutils would be interesting
I have coreutils
Orangutan
yeah ikr-
[ LOG ] User thread 7 SIGSEGV (rip 0x0, cr2 0x0, error code 0x00000014)```
I've been working on adding better cross compilation support to obos-strap
as well as host and target packages
what happend to tcp?
you use autotools-inspired build/host/target platforms right?
e.g. build is the machine that builds, host is the mschine that runs and target is the machine that host compiles for
yes
soon™️
obostrap pictured

these new changes to obos-strap have caused me to rebuild gcc thrice today
anyway so now host packages (like the bootstrap gcc binaries) are all built under build_root/host_pkgs whilst target packages are built under build_root/pkgs
In file included from /home/oberrow/Code/obos-pkgs/host_pkgs/lib/gcc/x86_64-obos/14.2.0/include/syslimits.h:7,
from /home/oberrow/Code/obos-pkgs/host_pkgs/lib/gcc/x86_64-obos/14.2.0/include/limits.h:34,
from /home/oberrow/Code/obos-master/src/oboskrnl/external/nanoprintf.h:65,
from /home/oberrow/Code/obos-master/src/oboskrnl/klog.c:38:
/home/oberrow/Code/obos-pkgs/host_pkgs/lib/gcc/x86_64-obos/14.2.0/include/limits.h:210:15: fatal error: limits.h: No such file or directory
210 | #include_next <limits.h> /* recurse down to the real one */
bruh what the hell
what why would it want mlibc's limits header
that's nothing
i'm currently on my 12th compile
only thrice? I rebuild gcc every day
it's part of my routine now
i wake up
change stuff in mlibc
rebuild gcc (twice, bootstrapping it from the compiler that compiles the compiler)
then breakfast as it compiles
then repeat
I've been debugging the sleep command in coreutils
because it just like doesn't work in obos
(it hangs indefinitely)
memzero(Core_GetCurrentThread()->context.extended_ctx_ptr, Arch_GetXSaveRegionSize());
if (obos_expect(Arch_HasXSAVE, true))
__builtin_ia32_xrstor(Core_GetCurrentThread()->context.extended_ctx_ptr, __builtin_ia32_xgetbv(0));
else
__builtin_ia32_fxrstor(Core_GetCurrentThread()->context.extended_ctx_ptr);```
idk why but I feel like doing this to reset extended context state is wrong
well I tried looking at astral
it only uses fxsave
The XSAVE header of a newly allocated XSAVE area should be initialized to all zeroes before saving context. An
operating system may choose to establish beginning state-component values for a task by executing XRSTOR from
an XSAVE area that the OS has configured. If it is desired to begin state component i in its initial configuration, the
OS should clear bit i in the XSTATE_BV field in the XSAVE header; otherwise, it should set that bit and place the
desired beginning value in the appropriate location in the XSAVE area
from intel sdm
volume 3 chapter 15
section 5.4
do you set up the fcw and mxcsr in the area?
don't think so
this is before an execve call passes control to userspace btw
I also don't even touch fcw
I see
static __attribute__((target("xsave"))) __attribute__((target("avx"))) void reset_extended_state()
{
asm volatile("fninit");
memzero(Core_GetCurrentThread()->context.extended_ctx_ptr, Arch_GetXSaveRegionSize());
static const uint32_t mxcsr = 0x1f80;
*((uint32_t*)(Core_GetCurrentThread()->context.extended_ctx_ptr + 24)) = mxcsr;
*((uint16_t*)(Core_GetCurrentThread()->context.extended_ctx_ptr)) = 0x33f;
if (obos_expect(Arch_HasXSAVE, true))
__builtin_ia32_xrstor(Core_GetCurrentThread()->context.extended_ctx_ptr, __builtin_ia32_xgetbv(0));
else
__builtin_ia32_fxrstor(Core_GetCurrentThread()->context.extended_ctx_ptr);
}```
this is in an effort to get bash to stop segfaulting
and I think it worked
jinxed it
[ LOG ] User thread 7 SIGSEGV (rip 0x0, cr2 0x0, error code 0x00000014)
yeah bash likes to segfault
I think it's a bug with fork
hey ive come to check on progress with obos
it seems your encountering a bug with
fork()?
yep
understandable
well maybe i could help like look through the code myself
i wont write anything cause yk cast
but
ill try to look for it
can i see ur fork code
if thats okay omar
@flint idol
thanks omar
from what im looking at
the only thing im suspecting is this line
hold on
wait
no
it looks valid
💀 those are the worst kinds of bugs tbh
if it is a memory manager bug, it's probably a use after free of a physical page
maybe it could be that cause the code all looks fine
I guess an easy way to figure that out is by examining each use of struct page
and making sure each one references it and dereferences it at the appropriate times
which I am doing now
static bool sym_cow_cpy(context* ctx, page_range* rng, uintptr_t addr, uint32_t ec, page* pg, page_info* info)
{
info->prot.present = true;
if (pg->refcount == 1 /* we're the only one left */)
{
// Steal the page.
info->prot.rw = true;
info->prot.ro = false;
MmS_SetPageMapping(ctx->pt, info, pg->phys, false);
pg->cow_type = COW_DISABLED;
return true;
}
page* new = MmH_PgAllocatePhysical(rng->phys32, info->prot.huge_page);
new->pagedCount++;
memcpy(MmS_MapVirtFromPhys(new->phys), MmS_MapVirtFromPhys(pg->phys), info->prot.huge_page ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE);
info->prot.rw = true;
info->prot.ro = false;
info->phys = new->phys;
MmS_SetPageMapping(ctx->pt, info, new->phys, false);
pg->pagedCount--;
MmH_DerefPage(pg);
return true;
}
this is the actual CoW handler
it handle cow?
which at a glance doesn't seem to do anything stupid
yes it moos
I think
no
i do not believe that will necessarily cause a lock prefix on x86 or for an ll/sc sequence to be generated on riscs or whatever
you should check and see what its emitting
I don't support risc-v
but I could try m68k
void MmH_RefPage(page* buf)
{
buf->refcount++;
printf("refed page %p (now at %d)\n", buf->phys, buf->refcount);
}
void MmH_DerefPage(page* buf)
{
printf("derefed page %p (now at %d)\n", buf->phys, buf->refcount - 1);
if (!(--buf->refcount))
{
if (~buf->flags & PHYS_PAGE_MMIO)
Mm_FreePhysicalPages(buf->phys, ((buf->flags & PHYS_PAGE_HUGE_PAGE) ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE) / OBOS_PAGE_SIZE);
// printf("removed page from tree (refcount %d)\n", buf->refcount);
RB_REMOVE(phys_page_tree, &Mm_PhysicalPages, buf);
Mm_PhysicalMemoryUsage -= ((buf->flags & PHYS_PAGE_HUGE_PAGE) ? OBOS_HUGE_PAGE_SIZE : OBOS_PAGE_SIZE);
Mm_Allocator->Free(Mm_Allocator, buf, sizeof(*buf));
}
}```
this is probably correct...
mov eax, Sys_ProcessStart
mov rsi, rbx
mov rdx, 1
syscall
mov r14, rax
// Get the PID
// TODO: Can this be a race condition if the process ends fast enough?
mov rdi, r14
mov eax, Sys_ProcessGetPid
syscall
this is my fork code in userspace
maybe through some miracle that race condition happens
and somehow breaks all
Built-in increment and decrement operators and compound assignment are read-modify-write atomic operations with total sequentially consistent ordering (as if using memory_order_seq_cst).
epic
this is broken though
you have been fooled by atomic into thinking it will save you from all of your sins
even if those incs and decs are atomic im p sure this is broken
do you ever call DerefPage at a time where the refcount may be 1 and some other guy may still have a pointer to the page?
but has not yet called RefPage himself
how do you deal with putting pages on like standby/modified/whatever list
this is a pinnable message
[ LOG ] User thread 7 SIGSEGV (rip 0x0, cr2 0x0, error code 0x00000014)```
hmm
what if
it's calling a weak function
in mlibc
a weak sysdep specifically
I would expect mlibc to check for that as it usually does
and return ENOSYS
the error code passed by the cpu
so 0x14 means executing a non present page in user mode
it's executing nullptr 
no
since the execute bit is set in the error code
and cr2=0
that means the program tried to execute 0x0
no
wrong
yeah no stack corruption
is the cause of the bug
but if that were the case
that would also mean rbp is broke
4b8381: 5d pop %rbp
4b8382: c3 ret
that's what I get looking into the function bash calls before dying
https://github.com/managarm/mlibc/blob/71ddf2d365b56de6d7dff40dd35c2de572ff38be/options/posix/generic/posix_stdlib.cpp#L243 this is the only place in mlibc that I can find that lacks proper sysdep checking
yeah this PF is happening likely right after a ret statement
wait if the stack is corrupted then so is the popped rbp
(likely)
well I guess it depends
it could be that only stuff starting at the ret addr would be corrupted
possibly
just in case I will stub out sys_mkdir
yeah no it persists
but the thing is it doesn't always happen
how much stack space do you give to the thread?
4M
isn't that a bit much
yeah I deleted it because I realized that this probably doesn't happen when just running bash and not doing anything in there?
No it only happens in bash afaik
does it happen if you just run plain bash
like when you don't type anything to there
It only happens after typing a command
And it only happens when fork has been called at least once
Occasionally a SIGSEGV happens before that
And that segfault is because of a GPF
293 text files.
293 unique files.
0 files ignored.
github.com/AlDanial/cloc v 2.02 T=0.24 s (1200.9 files/s, 205999.5 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
C 136 2591 3004 28549
C/C++ Header 143 1799 2360 9889
Assembly 9 226 124 1241
CMake 5 72 35 372
-------------------------------------------------------------------------------
SUM: 293 4688 5523 40051
-------------------------------------------------------------------------------
OBOS LoC update
I need to get rid of 51 un needed LoC
to get it to exactly 40k 
I want to port obos to i686 /srs
I will start in a bit
anywhere from 1-2 buisness days
it will be my 3rd port
it will use the same drivers as x86_64
and interrupt controller stuff
stuff like that
not much will be different except for paging, gdt, and idt code
and syscall abi
and probably some other stuff that I can't think of off the top of my head
thread context info stuff
I will also probably shift away from using the HHDM
execve handoff code might need to be changed according to the sysv abi for i386
and signal code
I would also be adding PIC and PIT support
which actually might be tricky considering how my abstract IRQ interface works, but I think I'll be able to find it out
it should be fun, but until then...
actually I should poll what arch I should port to next
#polls message
pls vote
1 vote risc-v 64 😭
strace it
time to start itanium port
uh so this will be fun
I guess I'll start with compiling an ia64-gcc toolchain
on gcc 15
or gcc 13.3
since I want a tarball release I will go with 13.3
I stole @ornate ginkgo's limine stub
hmm could I just use debian's packaged ia64 binutils and gcc
add support for ia64-be
I'll do that one day
itanium surely has ACPI, right?
I know it has EFI, iirc it was made for itanium (or with itanium in mind)
It has acpi
GL with the port, itanium looks interesting
yeah
(famous last words)
currently compiling ia64-elf binutils
and ia64-elf-gcc
compiled ia64-elf binutils
it was able to assemble and link some random ass program I found online
wait I haven't linked it yet
was able to link it
I think gcc just finished compiling
this took way too little time
it's compiling libgcc now
checking for suffix of object files... configure: error: in `/home/oberrow/Code/obos-pkgs-ia64/bootstrap/bootstrap-gcc/ia64-elf/libgcc':
configure: error: cannot compute suffix of object files: cannot compile

libgcc isn't compiling
it's compiling about some libc headers
couldn't care less about libc headers
so I'm ignoring it
@dim bolt know of any itanium emulators?
ski
if you hate yourself
I'm making an itanium port, so of course I hate myself /j
ia64 (Itanium) instruction set simulator. Fork of http://ski.sourceforge.net - trofi/ski
this?
ya
kthx
../configure: line 5959: syntax error near unexpected token `-Werror=implicit-int,'
../configure: line 5959: `AX_CHECK_COMPILE_FLAG(-Werror=implicit-int, CFLAGS="$CFLAGS -Werror=implicit-int", , -Werror)'
bruh
ok whatever
I got it to configure
yay it compiles
Ok I was able to get a basic itanium while loop running
Literally no firmware
No bootloader
Just while loop
oberrow on his way to write the entire boot stack, including the firmware, up to OBOS just to flex 
check the ski-bootloader
or just the ski boot functions
I'm using simics now
not possible.
never been done.
my condolences
yeah this makes no sense
itanium would be a lot more fun
if there were good emulators
so I might not do it
port OBOS to R3000 Jazz
No.
why
don't know what it is
32 bit mips dev board that hasnt existed since 1990
@upper tulip poke!
it now re-exists but as basically a fantasy computer that only boots an incredibly ancient windows nt build
wonderful.
it could also boot OBOS.
I need the science to do this to myself 
port it to the WRT54G 
it would unironically be easier to port OBOS to r3k jazz than to port it to itanium
by like 2x
but if u did mips youd probably want to do it on something that exists physically outside of a museum
since Itanium is proving to be difficult
I will port to something else
stay tuned™️
SPARC
SH3
fall back to the 2nd closest option in the polls, PPC :)
why
Not a popular target
No one really knows about it besides a few devotees
Routers 
emulation isn't great
I already suggested WRT54G 
You can run PPC port on game consoles and old Macs 
But that thing has 2MB of ram
There are newer ones
(I have many)
*16MB
Plenty of room 
I believe there are some powerpc routers out there
There are a lot of weird arches in routers
Or...
now that would be an interesting port
But PS3 is not multiprocessor unfortunately
But Wii U is 
And very cursed
(also, there is a normal Wii)
or ps2 with mips64 
@upper tulip how is your bootloader doing?
Progressing 
indeed, and you happen to have one of the best resources for it available: me (literally the developer of the port of Linux for it
)
I'm also in close contact with the dev of linux-wiiu
porting to either one would be hilarious, and you have the ultimate advantage in doing so
pmOS should be portable, but I don't want to figure out how to boot on it
(sorry for wrong thread)
d
I'm going to keep the platform I'm porting to a secret
Only posting screenshots occasionally
Won't push anything to GitHub either
I will have a local git server just in case™️

I should also port my kernel to something, though I am still in the middle of deciding what I want to do about things like gpu drivers (I could possibly manage to nih intel fully including accel render stuff but some other gpus like nvidia are very much likely even bigger pain because there are no proper specs and only nouveau)
its why I have been mostly working on the separate nt thing for now
imagine oberrow ports to the Xbox 360 (the 5 people with modded consoles can run it)
ps4 port
X86 that's not a PC
Good idea
port it to the lego ev3
so ppc, just as I though
You'll see what it is.
Maybe I'll do two ports
(this will be a hell to maintain NGL)
Idk
I would then have 4 ports
I want to be the one with the most ports on the server, that would be cool
then I need aarch64 urgently
and armv6
(since I have physical hardware where I can test it)
pmOS already supports device trees so it shouldn't be too difficult... (famous last words)
I have a 32-bit arm tablet
but the thing is that I think 32-bit arm is pretty much annoying, especially considering that its 32-bit so using hhdm is not a good idea
After these ports, I want to get mlibc running on m68k
For obos
And just a userspace in general for m68k
Should be fun
Does obos support i686
I already (almost) have i686 without HHDM, so it's not gonna be an issue for me
I mean not almost, I just want to add PAE before I merge it to main branch
well actually maybe its not that annoying if you abstract hhdm away into some kind of phys mem range accessor that just uses the hhdm if its available and else temporary mapping
that's exactly what I have
yeah I were thinking that if you'd have to just get rid of every use of the hhdm completely and unconditionally replace them it would be annoying and would cause unnecessary mapping on arches where you can have a hhdm
maybe I should try to do a 32-bit port too
Wouldn't compilers optimize it to the same code if you have HHDM anyway?
with the phys mem range accessor using hhdm if its available yeah, that was an unrelated though that I had before I realized that
like if you'd get rid of the hhdm on all arches
I didn't have it in the first place
No
does obos support lego ev3
unbased
i am in need of a hybrid kernel with advanced driver loading for my lego ev3
Lol I should change that description
Does it have MMU?
lego ev3? it's a armv5tej CPU, and it runs linux
and there's a debian based distro ev3dev that runs a closer-to-mainline kernel
NXT is sadly a microcontroller
spike is also a microcontroller
lego ev3 is so funn
like the dpad lights are controllable with sysfs
and you can play doom on it. with sound
it has audio, monochrome LCD, a dpad, a host USB port (with an official wifi dongle but in ev3dev you can use any wifi dongle or ethernet adapter or flash drive or keyboard or mouse), bluetooth, a micro usb port that you can use for usb tethering to get internet through a laptop
and most importantly it has lego mindstorms connectors for sensors and motors
so you can make a robot that passes you butter
Found OBOS while watching football clips
i686, I see
powerpc instructions are 4 bytes long...
it's a mystery platform
anyway I need to get an IRQ controller working on this
mystery powerpc platform
is it armv5tej
Is that an x86 SSE mnemonic?
no it's arm version 5 with thumb and jazelle
GF2P8AFFINEINVQB
based
which version(s) of PPC do you plan to target
ppc64le? ppc64be? ppc32be?
why not all?
All 
I really wonder what platform this could be
I can't tell if you're being sarcastic or not
so many options
there's tons of 32-bit architectures lmfao
you could literally be doing anything
ARM, MIPS, SPARC, RISC-V, etc
since it's not i686, or 32-bit ppc
but there's still tons
I'll tell you guys soon
I got thread context switching done
turns out I overflow the goddamned stack
I'm going to get MMU code in now
I fucking hate 32-bit address spaces
I will identity map 0x1000-0xc0000000
then any physical pages > 0xc0000000 will be mapped at a fixed address
this fixed address will be protected by a spinlock, and should only be accessed at IRQL_MASKED
you might be like "but but identity map bad
"
I don't care.
are you not planning on having userspace?
KPTI

how do you copy things from user? Onto stack?

Isn't it kinda very slow?
for large IO, I use a separate function
// Maps user pages into the kernel address space. This can be used in syscalls to avoid copying large amounts of memory.
// returns OBOS_STATUS_PAGE_FAULT when a portion of the user memory requested is not mapped.
// NOTE: The returned address is not aligned down to the page size.
OBOS_EXPORT void* Mm_MapViewOfUserMemory(context* const user_context, void* ubase, void* kbase, size_t nBytes, prot_flags protection, bool respectUserProtection, obos_status* status);
oh god what is this?
I refuse to elaborate.
this is how I do it:
.globl __memcpy_from_userspace
.type __memcpy_from_userspace, @function
__memcpy_from_userspace:
.cfi_startproc
jmp usercopy_common
.cfi_endproc
.size __memcpy_from_userspace, . - __memcpy_from_userspace
.globl __memcpy_to_userspace
.type __memcpy_to_userspace, @function
__memcpy_to_userspace:
.cfi_startproc
jmp usercopy_common
.cfi_endproc
.size __memcpy_to_userspace, . - __memcpy_to_userspace
.globl __usercopy_pagefault_address
.type usercopy_common, @function
usercopy_common:
.cfi_startproc
xorq %rax, %rax
movq %rdx, %rcx
__usercopy_pagefault_address:
rep movsb
ret
.cfi_endproc
.size usercopy_common, . - usercopy_common
.globl __usercopy_fixup_address
.type __usercopy_fixup_address, @function
__usercopy_fixup_address:
.cfi_startproc
movl $EFAULT, %eax
ret
.cfi_endproc
.size __usercopy_fixup_address, . - __usercopy_fixup_address
.globl __userstrcpy_pagefault_address
.globl __strncpy_from_userspace
.type __strncpy_from_userspace, @function
__strncpy_from_userspace:
.cfi_startproc
1:
__userstrcpy_pagefault_address:
lodsb (%rsi), %al
stosb %al, (%rdi)
testb %al, %al
jz 1f
decq %rdx
jnz 1b
1: xorq %rax, %rax
ret
.cfi_endproc
.size __strncpy_from_userspace, . - __strncpy_from_userspace
holy shit what
that's cool and all
basically it's just memcpy but with special handling for page faults
but I'm playing minecraft
this is probably like 3 lines of asm total
yes. 
and 50 lines of GCC black magic
also
wtf
free cve spawner
theres literally no reason to have it
just get a size
and copy that amount of bytes
counter argument: filesystem syscalls
but yeah
I agree, somewhat
you have userspace strlen the file path
easy
explicitly sized buffers are better
all you need is copyin and copyout
and the only thing they differ in is the direction
and they can share an extable entry
or whatever you use as an equivalent
they can share a page fault recovery location thingy
oh... you haven't seen my pagefault handler
static pagefault_status_t
handle_pagefault_in_vm (unsigned long addr, unsigned int error_code)
{
struct pagefault_info fault;
fault.addr = addr;
fault.access_write = (error_code & PF_WR) ? 1 : 0;
fault.access_exec = (error_code & PF_ID) ? 1 : 0;
irq_enable ();
pagefault_status_t status = vm_handle_pagefault (&fault);
irq_disable ();
return status;
}
extern char __usercopy_pagefault_address[];
extern char __userstrcpy_pagefault_address[];
extern char __usercopy_fixup_address[];
static inline bool
is_usercopy_addr (void *rip)
{
return rip == __usercopy_pagefault_address
|| rip == __userstrcpy_pagefault_address;
}
static bool
handle_usercopy_fault (unsigned long addr, struct entry_regs *regs)
{
if (usercopy_ok ((void *) addr, 1) != ESUCCESS)
return false;
if (regs->error_code & PF_ID)
return false;
if (handle_pagefault_in_vm (addr, regs->error_code) != FAULT_HANDLED)
regs->rip = (unsigned long) __usercopy_fixup_address;
return true;
}
void
handle_PF_exception_k (struct entry_regs *regs)
{
unsigned long addr = read_cr2 ();
note_pagefault (addr, regs->error_code);
if (is_usercopy_addr ((void *) regs->rip)) {
if (handle_usercopy_fault (addr, regs))
return;
}
panic ("x86: Page Fault in kernelspace! (rip=0x%lx ss:rsp=%lu:0x%lx)",
regs->rip, regs->ss, regs->rsp);
}
that's the entire thing?
no. but that's the part that dispatches faults to vm
and changes the return rip to the fixup addr
lol linux is like 10x more
I'll get there eventually.
I've stopped commenting on obos bc I'm afraid of being responsible for causing you to quit
Even just in that function there are huge issues beyond just the fact its slow
It is broken in numerous ways
btw
is that not guaranteed to be read-modify-write?
first, no
and gcc WILL hunt you down
also it will emit a single or instruction
which may or may not work
OBOS kernel pwn as a CTF challenge when?
Not a challenge, even bash crashes it relatively quickly
also a valid way to lower that loop is c if (!(HBA->ghc & BIT(31))) { while (1) { HBA->ghc; OBOSS_SpinlockHint(); } }
that's why it's a challenge. we need to keep it alive for long enough to extract the flag
I had thought about rewriting this kernel a few times, but never said anything about it
no length limit on the strcpy from user?
obviously lol.
iirc linux limits it to PATH_MAX for fs stuff
Mainly because I did stupid stuff and don't know how to fix it
this is what I do:
static errno_t
get_path_from_userspace (char *kbuf, const char *user)
{
kbuf[PATH_MAX - 1] = 0;
errno_t e = strncpy_from_userspace (kbuf, user, PATH_MAX + 1);
if (e == ESUCCESS && kbuf[PATH_MAX - 1])
e = ENAMETOOLONG;
return e;
}
and callers of this function:
SYSCALL6 (void, stat, struct stat *, buf, size_t, buf_size, const char *, path,
int, dirfd, struct pathwalk_info *, info, size_t, info_size)
{
char kpath[PATH_MAX];
struct stat kstat;
struct pathwalk_info kpathwalk_info;
if (info_size > sizeof (kpathwalk_info))
syscall_return_error (E2BIG);
memset (&kpathwalk_info, 0, sizeof (kpathwalk_info));
errno_t e = ESUCCESS;
if (info_size)
e = memcpy_from_userspace (&kpathwalk_info, info, info_size);
if (e != ESUCCESS)
syscall_return_error (e);
// ...

and also your code contains a data race
I do the same
ah yeah thats fine, i missed the dec %rdx : jnz 1b part
because of the same "volatile is stupid and bad" rpoblem
no centralised bus?
(aka static struct pci_driver driver = { ... }; register_pci_driver (&driver);)
no(t yet)
I don't have any limits for userspace buffers in my kernel
you can literally cause an oom just by using some syscall like syslog that takes in a pointer + length and if you give a bogus large length and a nullptr the kernel is going to die (or more like the specific cpu core is going to die, the others might still function depending on how the locking works, idr if I keep the alloc lock in the fail case)
(except that it gets back to the same context, and handles pagefaults from there
)
where
I can recommend watching (and rewatching) the many cppcon talks about atomics recorded and posted on youtube.
but the semantics are stupid and bad
thanks for the text block
it should really be a property of the read/write and not the pointer
thats not even the beginning of the problems lol
the real fun problem is that a volatile store doesnt place any ordering constraints
on anything
so like. the compiler can just remove stores you make
tbh _Atomic is also stupid and bad (albeit in less ways than volatile)
I'd assume it to not do that tho
because like that would very likely break linux
it cant remove volatile ones
but yeah it can reorder nonvolatile stores past volatile ones
volatile stores dont impose ANY ordering constraints
(except against other volatile loads and stores afaik)
(but that doesn't help much)
hmm thats an interesting question
do they
yeah probably
hopefully
but the value a load loads is not an input to the AM
only that a load occured
btw the rust people have come up with a """"""""fix""""""""" for this problem
get ready
volatile atomics!
please dont think about it
atomics on hw regs 💀 (that's not even really allowed)
yeah but consider this
sanity is optional in rust land
what does this do exactly?
wdym?
idk i think its for like some handoff stuff
it doesn't really matter, the pointer is volatile its going to be just fine
it is technically speaking UB
you have to accept the ub
#pragma GCC optimize("-O0")```
lol

it'd be very bad if the compiler could transform something like:
*(volatile uint32_t *) (base + IOREGSEL) = 0x22;
*(volatile uint32_t *) (base + IOWIN) = 0x00000040;
into
*(volatile uint32_t *) (base + IOWIN) = 0x00000040;
*(volatile uint32_t *) (base + IOREGSEL) = 0x22;
suddenly you're writing bogus into a bogus register in the IOAPIC
which solves this issue
I mean yeah but like still, at some point you just accept the ub
nahhh you dont you can write UB-free os code
its not really necessary tho
lets maybe start with not including data races :^)
there's no UB in asm 
exactly
on one hand yes, but on the other hand consider this:
compiler go brrr
i will make an assembler that outputs LLVM IR and optimizes the assembly
please dont think about how this sort of reordering is insanely cringe and stupid
and also these semantics make no sense
so having macros like:
#define MMIO_WRITE1(addr, x)
#define MMIO_READ1(addr)```
time for UB in your asm
yeah pretty much
it would be interesting to see this.
iirc linux does something like that
langdev project!!
i have ```c
#if defined (aarch64)
#define arm64
#define arch_uses_fdt
#define arch_little_endian
#define arch_asm_read8 "ldrb %w[value], [%[address]]"
#define arch_asm_read16 "ldrh %w[value], [%[address]]"
#define arch_asm_read32 "ldr %w[value], [%[address]]"
#define arch_asm_read64 "ldr %[value], [%[address]]"
#define arch_asm_write8 "strb %w[value], [%[address]]"
#define arch_asm_write16 "strh %w[value], [%[address]]"
#define arch_asm_write32 "str %w[value], [%[address]]"
#define arch_asm_write64 "str %[value], [%[address]]"
#elif defined (riscv64)
#define rv64
#define arch_uses_fdt
#define arch_little_endian
#error TODO: riscv arch_asm_*
#else
#error unknown platform!
#endif
#define rmw_func(size, typ)
static inline typ read##size(usize address) {
typ value;
asm volatile(arch_asm_read##size : [value] "=r" (value) : [address] "r"(address) : "memory");
return value;
}
static inline void write##size(usize address, typ value) {
asm volatile(arch_asm_write##size :: [value] "r" (value), [address] "r"(address) : "memory");
}
static inline void set##size(usize address, typ value) {
write##size(address, read##size(address) | value);
}
static inline void clear##size(usize address, typ value) {
write##size(address, read##size(address) & ~value);
}
static inline void mask##size(usize address, typ clear, typ set) {
write##size(address, (read##size(address) & ~clear) | set);
}
static inline typ writeread##size(usize address, typ value) {
write##size(address, value);
return read##size(address);
} \
rmw_func(8, u8)
rmw_func(16, u16)
rmw_func(32, u32)
rmw_func(64, u64)
how
but thats the same thing basically
the type is redundant
I have this 
which type?
can be derived from just u##size
imo the only sane way to deal with volatile/atomics is to have
- a library of things used commonly, eg. nmi-safe atomic list, spinlock, mutex, rwmutex, semaphore, atomic refcount
- some helper functions for hardware register bitbashing
and then only use those.
Toctou on the page access checks, and the accesses to the cpu local data are racy
I don't like arch_uses_fdt
the only sane way to deal with volatile is to not

i use it to compile out FDT logic when not needed, also its a shit ass prekernel its allowed to be bad
what if I boot it with ACPI on riscv or aarch64
doesnt matter, its not a "requires FDT", its a "supports FDT"
ah
it's ok then
tbh that shouldn't be an arch thing, I could boot x86 with FDT if I really wanted to
it's more like, default to compiling FDT support for this arch right?
yeah its more like that
no
its more like
"compile in fdt shit"
right now i have one platform (arm64 with linux boot) and that has FDT always anyway
ill fix it later tho
maybe
or maybe not
opinion: keep build configuration out of headers, even a Kconfig like linux or uboot would be fine
I think arm64 with ACPI is a thing in linux boot
still includes an FDT though
at least the raspberry pi 5 edk2 port mentions it
I think I will pause porting for a bit
its a shitty prekernel
oh?
yeah
to fix this MMIO read/write "problem" thing
or is it anything useful
I actually like this (credit for idea goes to managarm), you have register/bitfield types ```cpp
namespace regs {
constexpr BitRegister<u64> GCIDR {0};
constexpr BitRegister<u64> GCR {0x10};
constexpr BasicRegister<u64> MCVR {0xF0};
}
namespace gcidr {
// bitfield of u32 type in bits 32..64 on an u64
constexpr BitField<u64, u32> COUNTER_CLK_PERIOD {32, 32};
}
void hpet_init() {
auto fs_in_tick = SPACE.load(regs::GCIDR) & gcidr::COUNTER_CLK_PERIOD;
auto ns_in_tick = fs_in_tick / 1000000;
auto ticks_in_s = NS_IN_US * US_IN_S / ns_in_tick;
}
does it like
you can copy my meme
the bitfield thing is kinda silly tbh
did you copy it from managarm?
i see i see
or well not copy but the idea is from there
clearly copied :^)
uhda is C++?
yes
hmm actually this is an interesting question
can i complain to infy about UB
ah no
i cant
its implementer ub
actually the uhda impl does volatile loads/stores with an added launder
not the arch specific asm that I have in that other copy of it
oh cool so its UB!
as long as it works 🤷
though ig I might consider adding the asm impl but that would be the only arch-specific part in there
#if defined(__x86_64__) || defined(__i686__)
# define MMIO_WRITE1(addr, x) do { asm volatile("movb %1, (%0);" : :"r"(addr), "r"(x) :"memory"); } while(0)
# define MMIO_WRITE2(addr, x) do { asm volatile("movw %1, (%0);" : :"r"(addr), "r"(x) :"memory"); } while(0)
# define MMIO_WRITE4(addr, x) do { asm volatile("movd %1, (%0);" : :"r"(addr), "r"(x) :"memory"); } while(0)
# define MMIO_READ1(addr) ({ uint8_t x = 0; asm volatile("movb (%0), %1;" :"=r"(x) :"r"(addr) :"memory"); (x); })
# define MMIO_READ2(addr) ({ uint16_t x = 0; asm volatile("movw (%0), %1;" :"=r"(x) :"r"(addr) :"memory"); (x); })
# define MMIO_READ4(addr) ({ uint32_t x = 0; asm volatile("movd (%0), %1;" :"=r"(x) :"r"(addr) :"memory"); (x); })
#elif defined(__m68k__)
# define MMIO_WRITE1(addr, x) do { asm volatile("move.b %1, (%0);" : :"r"(addr), "r"(x) :"memory"); } while(0)
# define MMIO_WRITE2(addr, x) do { asm volatile("move.w %1, (%0);" : :"r"(addr), "r"(x) :"memory"); } while(0)
# define MMIO_WRITE4(addr, x) do { asm volatile("move.d %1, (%0);" : :"r"(addr), "r"(x) :"memory"); } while(0)
# define MMIO_READ1(addr) ({ uint8_t x = 0; asm volatile("move.b (%0), %1;" :"=r"(x) :"r"(addr) :"memory"); (x); })
# define MMIO_READ2(addr) ({ uint16_t x = 0; asm volatile("move.w (%0), %1;" :"=r"(x) :"r"(addr) :"memory"); (x); })
# define MMIO_READ4(addr) ({ uint32_t x = 0; asm volatile("move.d (%0), %1;" :"=r"(x) :"r"(addr) :"memory"); (x); })
#else
# error Define for your architecture!
#endif```
ok just did this
time to use them
for MMIO
ig
before that, I will add:
// thanks a lot for this, pitust.```
wait what
somethign specific?
i use volatile in like one place, just to prevent reorders iirc
probably
definitely
its followed by an atomic cmpxchg on the same memory location
is it to prevent cross-thread reorders?
so its enough
nah
its the firmware global lock
i load it and then cmpxchg
its a data race
cmpxchg is atomic tho
bruh
uacpi_status uacpi_system_memory_read(void *ptr, uacpi_u8 width, uacpi_u64 *out)
{
switch (width) {
case 1:
*out = *(volatile uacpi_u8*)ptr;
break;
case 2:
*out = *(volatile uacpi_u16*)ptr;
break;
case 4:
*out = *(volatile uacpi_u32*)ptr;
break;
case 8:
*out = *(volatile uacpi_u64*)ptr;
break;
default:
return UACPI_STATUS_INVALID_ARGUMENT;
}
return UACPI_STATUS_OK;
}
``` this is UB too
or at least
on paper yes probably
yeah that's what I said too
using a relaxed atomic load would compile to the same thing and not be ub
what
the obos one is stretching semantics
this is straight UB that can blow your arm off
doesnt that function not even exist anymore
idk
the compiler doesnt know anything
probably not
oh wait thats the internal one my bad
im so used to seeing that pattern in the old kernel raw one lol
wouldn't that depend on what ptr points to?
like a month ago
yeah true tbh
yeah that was a while ago lmao

it points to stuff returned via kernel_memory_map
it's only as UB as the firmware devs are braindead
it just always does map -> normal vmem access -> unmap now
which is to say very
:p
use SystemIO exclusively to be safe 
lmao
(a) doesnt matter, it can and i suspect sometimes will produce bad code (b) you need atomics anyway lol
a relaxed atomic load is fine here
id need a relaxed atomic load
which on x86 is the same
here being?
the global lock one
those loads and stores are UB too though
I don't think this is true?
An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects. Therefore, any expression referring to such an object shall be evaluated strictly according to the rules of the abstract machine, as described in 5.1.2.4
I interpret this as saying that the compiler cannot assume that the value hasn't changed between the two condition checks
not really
oh hmm
hmmmmmmmmmm
interestingggg
they refer to stuff mapped via kernel_map, which, as long as AML is correct stores this exact type it puns into
it's more so that the compiler is allowed to use any instruction it wants to effect the reads/writes, as well as being allowed to split said reads and writes
that's problematic because some instructions are not allowed to be used for MMIO
for example, a while ago I got bitten by clang trying to use some bit manipulation extension for setting the ioapic registers
lol
what im gonna do for mmio is have optional read_memory/write_memory kernel hooks that operate on virtual addresses
to combat that
sounds like a good idea
wait really?
can you reproduce?
or was it a normal read/write and not a volatile one?
this was like >1 month ago, i don't remember the commit it was on
ah
it was def volatile though
damn i would love to see how to do that lol
i thought only GCC engaged in this flavor of brain damage
znver3
ah
oh yeah!
damn
ah
clang enjoys this flavor of brain damage too TIL
ah maybe its GCC that didnt? idk
either way good to know
managed to repro it on the last commit before i switched to inline asm mmio
doesn't happen with gcc
the offending instruction is bextrl %eax, 0x10(%r12, %rbx), %edx
ah yeah
i can get it to produce a normal or lmao
basically the TLDR is
you cannot, ever, use volatile for MMIO
because it straight up produces broken code
yep
r12 here maps to 0xfec00000
and rbx is 0
thats only broken on qemu right?
unless the "mmio" is really just a different kind of ram, e.g. when writing to a framebuffer
probably, i'm not gonna bother testing on real hw
but i would assume that on actual cpus the instruction doing the mmio doesn't matter
then again on amd cpus you're only allowed to interface with ecam if you use mov to/from eax so what do i know
it's not actually enforced, doing it with some other register works fine on modern cpus
but it's in the bios and kernel developer's guide
lmfao
so it presumably was necessary on some previous generation
what's even the point of volatile then?
pretty much tbh
preventing compiler from assuming the value, preventing reordering
the former, yeah
the latter, no
you arent preventing reordering anyway
with other volatile writes
which you cant observe anyway
yes
and also it doesnt prevent it i think
hm?
idk maybe it does?
yeah but linux relies on intense ub
/*
* Prevent the compiler from merging or refetching reads or writes. The
* compiler is also forbidden from reordering successive instances of
* READ_ONCE and WRITE_ONCE, but only when the compiler is aware of some
* particular ordering. One way to make the compiler aware of ordering is to
* put the two invocations of READ_ONCE or WRITE_ONCE in different C
* statements.
*
* These two macros will also work on aggregate data types like structs or
* unions.
*
* Their two major use cases are: (1) Mediating communication between
* process-level code and irq/NMI handlers, all running on the same CPU,
* and (2) Ensuring that the compiler does not fold, spindle, or otherwise
* mutilate accesses that either do not require ordering or that interact
* with an explicit memory barrier or atomic instruction that provides the
* required ordering.
*/
- Yes, this permits 64-bit accesses on 32-bit architectures. These will
- actually be atomic in some cases (namely Armv7 + LPAE), but for others we
- rely on the access being split into 2x32-bit accesses for a 32-bit quantity
- (e.g. a virtual address) and a strong prevailing wind.
the "strong prevailing wind" comment does NOT inspire confidence
i also would call it blatant UB
how is it ub lol
it isnt preventing refetching
afaik
it can also produce torn writes without causing UB
actually it can produce a lot of things
yeah idk
its blatant UB reliance
language lawyering stuff can be counter productive
true
in this case the correct thing is an atomic
or an assembly load
but anyway the point is you cant use it to stop reordering cross-cpu
because that requires actual barriers
i mean yeah
and it doesnt stop reordering vs literally any other memory op you make
thats not its purpose tho
to do THAT you need a c11 signal fence
like yeah i know its stupid and rather lawyery but unfortunately that is the spec
and in practice the compiler can do many very bad things with volatile that preclude it being used for mmio anyway
was investigating a crash in obos-strap
only for it to be in cJSON
at first I thought I passed something invalid to the function
like nullptr or smth
No.
i like those managarm bit field things
it shows off C++'s merit as a systems language
Ngl I feel like making a new obos rewrite along this one
Like I work on both
Like some sort of OBOS2
why not just make this current version OBOS 1.x.x and make the rewrite OBOS 2.x.x
then you can also update OBOS 1 with new version numbers without it getting confused with 2
Because I would want to make a separate repo for the rewrite
This rewrite got a bit far in userspace, but the kernel is kind of not amazing
The memory manager is my biggest concern really
Also when I designed the driver interface, for example, I designed it not amazingly
Not to mention all IO is done synchronously
there can be a time for rewrites and a time not for
Which seeped into the VFS
i started a sort of keyronex rewrite but now i'm folding in as much of what i worked on there back into keyronex
whats wrong with the current obos
Also I kinda hate the naming convention lol
as while current keyronex is messy in many areas and there are new ways of doing things i want to introduce, there is no call for a blanket throwing out of everything
idk I'm just thinking about it
It might not happen
what would u change
lol
doesnt help that i started the project just when classes started
And when did you start it lol
like a month ago at this point lol
and it's still basically a limine hello world + kernel debugger
GDT init.. unimplemented
at least I'm doing somewhat productive stuff in my free time so
