// disclosure & hardening

Security.

Two things live on this page. First, what MayteraOS actually enforces against exploitation today: address-space randomization, stack canaries, position-independent executables, a hardened syscall boundary, and TLS certificate validation, each stated as enforced, partial or planned rather than claimed wholesale. Second, the advisory log below: every reachable memory-safety defect found by the project's own fuzzing and AddressSanitizer harnesses, with the builds it affected and the build that fixed it.

Hardening & exploit mitigations

Enforced today: position-independent executables and a whole-image writable-plus-executable (W^X) gate across every shipped app and game binary; per-process load-address randomization drawn from the kernel's CSPRNG; stack canaries in the kernel and across the userland app fleet, also seeded from the kernel CSPRNG rather than a raw timestamp; SMEP where the CPU supports it; all 132 pointer-taking syscalls validated at a single dispatcher chokepoint, enforced by a build-failing lint; and real TLS certificate chain and signature verification (including CertificateVerify), not just parsing.

Partial or planned, stated plainly rather than left implied: SMAP is fully wired but deliberately left disabled until a remaining set of kernel-side user-memory accesses are moved onto the safe copy primitives; the filesystem permission layer is implemented and self-tested but inert as shipped, because every account, including the one first-boot setup creates, is an administrator (uid 0) account: there is no non-root user tier yet, so a permission check between users has nothing to enforce. Autologin itself ships disabled and first boot requires creating an account. Kernel base randomization (KASLR) is assessed but not built, blocked on the kernel's current identity-mapped page tables; and the security event log is not yet append-only or tamper-evident.

Every claim above has the evidence behind it, plus the gaps, on the Hardening & Mitigations page: what was measured, how, and the commands to re-check it yourself against a downloaded image.

Advisories

16advisories closed
7out-of-bounds writes
7reads / null-deref
5remote
2285current fixed build

Product line: MayteraOS v2.0.x. The meaningful version identifier is the build number, shown on the desktop as “v2.0.2 (build NNN)”. Every advisory below is witnessed against the reference implementation (which over-reads or over-writes on a crafted input) and confirmed fixed (the identical input is confined) in an offline differential harness. AddressSanitizer is the usual witness, but it is not sufficient everywhere and we do not claim it is: one read in 0006 lands past ASan’s redzone in valid heap and needs a guard page, 0008’s write claim needed a page-fault error code, and 0001 cannot be witnessed at all. The evidence basis is stated per advisory rather than claimed across the board. Severity uses an informal High / Medium scale, weighted by write-vs-read impact and reachability (remote exposure ranks above network/LAN, which ranks above a local file or disk).

ID Component Type (CWE) Severity Attack vector / reachability Affected builds Patched in
MAYTERA-SEC-2026-0001 ext2 directory parse (fs/ext2.c: ext2_lookup / ext2_dirblock_find) CWE-125
out-of-bounds read
Medium Local: reading a crafted ext2 image with malformed directory rec_len / name_len heap over-reads on any lookup. Fixed by the #476 bounds guards on all four ext2 directory walkers. This is the one advisory whose vulnerable code is no longer recoverable (the fix predates every snapshot we retained), so it cannot be re-witnessed; it rests on the kernel’s own version log, and we label the evidence as such rather than overstate it. ≤ build 793 794
MAYTERA-SEC-2026-0002 DHCP reply parse (net/dhcp.c: dhcp_parse) CWE-125
out-of-bounds read
Medium Network (LAN): a spoofed runt DHCP OFFER drives the option TLV walk past the packet buffer. ≤ build 805 806
MAYTERA-SEC-2026-0003 ELF loader validate (exec/elf.c: elf_validate / calculate_load_bounds) CWE-190 → CWE-787 (+CWE-125)
integer overflow leading to OOB write
High Local file: a crafted ELF (oversized p_filesz underflow plus undersized e_phentsize) yields an out-of-bounds heap write on load. ≤ build 807 808
MAYTERA-SEC-2026-0004 FAT / VFAT LFN reassembly (fs/fat.c: fat_dir_step) CWE-787
out-of-bounds write
High Local, reachable from Ring 3 via SYS_READDIR: a crafted FAT long-file-name overruns the name buffer (260 vs. 256), an out-of-bounds heap write. ≤ build 809 810
MAYTERA-SEC-2026-0005 PNG decoder (gui/png.c: IHDR size math + BGRA convert loop) CWE-190 → CWE-787 (+CWE-125)
integer overflow leading to OOB write
High Untrusted image: a crafted PNG whose IHDR width/height wrap the 32-bit size math into an undersized allocation, after which the BGRA convert loop (plain C, not the defilter step) writes out of bounds. Working proof-of-concept: width=0x40000004, RGBA, height 1. What confines it is the Rust IHDR parser’s checked arithmetic, which rejects the image before the write is reached. Reachable from the browser’s <img> rendering, Files previews and downloads. Not wallpapers, which are all BMP. ≤ build 812 813
MAYTERA-SEC-2026-0006 JPEG decoder (gui/jpeg.c: SOF0 / SOS / DHT header parse) CWE-787 (intra-object) + CWE-125
buffer overflow inside the object, and out-of-bounds reads
High Untrusted image: a crafted JPEG with three unvalidated header fields. An unvalidated quantization-table index reads 12,800 bytes past a 256-byte object. An unvalidated DC/AC Huffman-table index reads 22,528 bytes past an 8,192-byte object; that read lands in valid heap past AddressSanitizer’s redzone, so ASan reports it clean and it is witnessed with a guard page instead. An unclamped DHT count sum then writes 4,080 bytes past its table: in the shipping code that write stays inside the enclosing 9,760-byte allocation, so it is intra-object corruption (it smashes the Huffman validity flags that drive decoding) rather than an out-of-allocation write. Reachable from album art, the browser’s <img> rendering and Files previews. ≤ build 813 814
MAYTERA-SEC-2026-0007 TLS handshake parse (net/tls/tls.c: TLS 1.2 handshake-message loop) CWE-125
out-of-bounds read
HighRemote, pre-auth Remote, pre-authentication: a malicious or on-path server’s first flight declares an oversized handshake length; the plaintext ServerHello loop reads out of bounds (measured: 4094 bytes past a 5-byte body). Runs on every outbound HTTPS handshake. ≤ build 815 816
MAYTERA-SEC-2026-0008 HTTP chunked decode (net/https.c: https_dechunk) CWE-190 → CWE-787 (+CWE-125)
integer overflow leading to OOB write
HighRemote Remote: a malicious or compromised HTTPS origin the browser visits (or a compromised API or update endpoint) sends a chunked response whose hex chunk-size is near 232; a 32-bit length clamp wraps, skipping the bounds check, and the following memory copy writes far outside the response body buffer. Reachable on any chunked HTTPS response, including LLM API replies, browser page loads, the updater, and widget feeds. ≤ build 816 817
MAYTERA-SEC-2026-0009 AAC / M4A parser (media/aac.c: mp4_parse) CWE-125
out-of-bounds read
Medium Local, reachable from Ring 3 via SYS_PLAY_WAV: a crafted .m4a / .mp4 file’s sample-table walk (stsz / stco / co64 / stsc chunk-offset entries) reads past the file buffer on inflated entry counts, with no per-entry bound. ≤ build 818 819
MAYTERA-SEC-2026-0010 HTTP/2 frame parser (net/http2.c: http2_get padding branch / frame framing) CWE-125 → CWE-476
out-of-bounds read leading to NULL pointer dereference
HighRemote, pre-auth Remote, pre-authentication: a malicious or compromised HTTPS site the browser visits negotiates HTTP/2 (h2 via ALPN) and sends a single DATA or HEADERS frame with length 0 and the PADDED flag set; the client reads the pad-length byte off a payload buffer that was never allocated for a zero-length frame, causing a NULL/out-of-bounds dereference, a page fault, and a whole-OS denial of service. One frame, no authentication, low complexity. ≤ build 821 822
MAYTERA-SEC-2026-0011 Extended-attribute parser (fs/xattr.c: on-disk entry-walk shared by getxattr / listxattr / setxattr / remove) CWE-125
out-of-bounds read
Medium Local: a crafted or corrupt FAT filesystem image whose /.xattr/*.xat block declares a name_len / value_len / attr_count that runs past the kmalloc’d file buffer; any getxattr / listxattr (sys_getxattr) on the associated file walks the entries trusting those on-disk lengths and over-reads the heap, causing a kernel info-leak or denial of service. The walk validated only the block magic, not the per-entry lengths. ≤ build 822 823
MAYTERA-SEC-2026-0012 NFS3 client read (net/nfs.c: nfs_read reply data length) CWE-787
out-of-bounds write
HighRemote Remote: a malicious or compromised NFS server that the client mounts and reads from (nfs://host/export) returns a READ reply whose declared data length is larger than the amount the client requested. The client copies that server-controlled length into the caller’s read buffer without clamping it to the buffer size, overflowing the destination on the heap. The transfer was bounded only against the receive buffer (source), never against the destination, so a single crafted reply corrupts kernel memory. Fixed by clamping the delivered length to the requested count. ≤ build 823 824
MAYTERA-SEC-2026-0013 JPEG decoder (gui/jpeg.c: build_huffman Huffman table build) CWE-787
out-of-bounds write
High A crafted JPEG whose Huffman table (DHT) declares a non-canonical set of code lengths, for example 255 one-bit codes where the code space allows at most two. The decoder built the lookup table without checking that the declared code lengths can actually exist, writing roughly 259 KB past the table with attacker-chosen bytes. Reachable from an ordinary Ring 3 application through image decoding, so any picture opened by the browser, the Files previews, the image viewer or printing could trigger it. Fixed in plain C by enforcing that the declared code lengths satisfy the Kraft inequality (used as an oracle independent of the implementation: the C, the Rust and the mathematics agree across 300,000 generated tables) and bounding the symbol index. The guard is independent of which implementation is selected at build time. This defect was original to the decoder and predates the Rust port; the port narrowed how it could be reached but did not create it. ≤ build 825 826
MAYTERA-SEC-2026-0014 IPv4 receive path (net/ip.c: ip_handle payload length) CWE-191 → CWE-125
integer underflow to out-of-bounds read
HighRemote Remote and unauthenticated, from any host on the same network: no IP address is required, because a system still awaiting DHCP accepts any frame and a broadcast destination is accepted regardless. The receive path checked the header length and the declared total length against the frame, but never that the total length was at least the header length, so the subtraction underflowed. A crafted 60-byte frame yields a payload length of 65,496, which was then handed to every protocol handler. Measured rather than inferred: ICMP, UDP and TCP were each observed being called with the bogus length, and the resulting over-reads were witnessed directly in the real handlers. Fixed in plain C with a one-line check that repairs all three protocols at the root, independent of build-time flags. ≤ build 825 826
MAYTERA-SEC-2026-0015 DHCP client transaction ID (net/dhcp.c: dhcp_init) CWE-330
use of insufficiently random values
Medium Network (LAN): every box computed its DHCP transaction ID as timer_ticks ˆ 0xDEADBEEF before interrupts were enabled, when timer_ticks is always zero at that point, so every machine sent its first DISCOVER with the same constant transaction ID (0xdeadbef0). A predictable ID makes off-path DHCP OFFER/ACK spoofing easier and contributed to a cross-box IP collision class. The fix (drawing a fresh, per-transaction ID from the kernel’s random number generator on every DISCOVER) was already present by the time this was re-audited, so, like 0001, its exact affected-build boundary cannot be recovered; we state that rather than invent a number. Confirmed on the wire: two independent boots produced two different, non-constant transaction IDs, neither the old constant. not recoverable
(fix predates this repository’s history)
confirmed fixed by 924
MAYTERA-SEC-2026-0016 System call argument validation (proc/syscall.c dispatcher; 20 previously-undeclared pointer-taking calls) CWE-284
improper access control (unvalidated pointer reaching Ring 0)
High Local: the single dispatcher chokepoint that validates every pointer a Ring 3 program passes to the kernel was rolled out with 96 of the syscall table’s entries declared, but a cross-audit against the real dispatcher found 20 pointer-taking calls with no declaration at all, so nothing validated them at that checkpoint. Four were unvalidated writes: a Ring 3 caller naming an address it does not own would have the kernel write to it. The rest were unvalidated reads, including the app-signature and package-signature verification inputs and several blocking network buffers. A separate terminal ioctl path checked only for a null pointer. All 20 are now declared and validated at the same chokepoint, plus a dedicated boundary check for the two-level pointer array used by process spawn and the command-specific ioctl path, which a flat descriptor cannot express. Fixed 2026-07-28 and verified on a dedicated test kernel (build 918, never shipped); present in every golden build since. builds before 2026-07-28 918
MAYTERA-SEC-2026-0017 is not a table entry. The number is reserved for record-keeping continuity, not for a defect. It documents a later live adversarial test that proved the TLS 1.3 CertificateVerify signature check, already correctly implemented in the tree, is actually load-bearing: an on-path attacker replaying a valid public certificate chain without possessing the matching private key is rejected, where a build with the check merely parsed and discarded would have accepted it. No vulnerable code shipped and no fix was needed. We record it here rather than skip the number silently, since a missing number in a public sequence invites the wrong guess about why.

How to update

If your MayteraOS desktop reports a build number below the “Patched in” column for any advisory above, update. All sixteen advisories are fixed by build 918 (0001–0007 in build 816, 0008 in build 817, 0009 in build 819, 0010 in build 822, 0011 in build 823, 0012 in build 824, 0013 and 0014 in build 826, 0015 confirmed fixed by build 924, 0016 in build 918), and every later build, including the three Ring-3 privilege-boundary fixes that landed in build 2246 and the current published release, build 2285. Grab the latest image from the downloads page, or pull the latest release from GitHub. The desktop shows its running build as “v2.0.2 (build NNN)”.

Notes

  • An advisory is only filed for a defect proven reachable: the C reference implementation demonstrably over-reads or over-writes on a crafted input, and the fix is proven to confine the identical input. Ports of already-correctly-bounded code (for example inflate/DEFLATE, ARP, DNS, URL parsing, ExFAT, BMP) are defense-in-depth only and are not listed here, since no reachable defect was found.
  • Every advisory is fixed at the source, in the shipping code path, so the fix holds regardless of which implementation a given build selects. Where a defect had more than one possible confinement, the fix that actually protects the shipping build is the one credited in the table.
  • On the counts above: 0006’s write is out of bounds of its Huffman table but stays inside the enclosing allocation in the shipping code (intra-object corruption); the other five out-of-bounds writes leave their allocation entirely. We count it as a write and say so here rather than adjust the number quietly.
  • By reachability: five remote issues (0007 TLS pre-authentication, 0008 HTTP chunked-transfer decode and 0010 HTTP/2 frame parsing, all pre-auth; 0012 a malicious NFS server the client mounts and reads; and 0014, reachable from any host on the same network with no IP address of its own), two network/LAN issues (0002 and 0015), three untrusted-image issues (0005, 0006 and 0013, all JPEG or PNG decode paths), five local file/disk issues (0001, 0003, 0004, 0009, 0011, the last two reachable from Ring-3 via the play-audio and get/list-xattr syscalls respectively), and one local syscall-boundary issue spanning 20 individual pointer-taking calls (0016). This corrects an earlier version of this list that left 0013 and 0014 out of the count entirely.
How these were found. All sixteen advisories were discovered internally, by the project’s own differential fuzzing, AddressSanitizer test harnesses, and targeted cross-audits of the syscall dispatcher. There was no external reporter and no coordinated disclosure process; each defect was found, reproduced, fixed and published here as part of the same work.