Repository navigation
Rak v8.0.0
Rak v8.0.0
What's new in 8.0.0
Security primitives
unsafe "reason" { ... }blocks. Anything touching raw memory, FFI, or a
wire format can be wrapped in a block carrying a written justification. The
reason is enforced by the parser, so an unjustified exemption is a parse
error rather than a warning, andgrep unsafereturns every exemption with the
author's own words attached.rakc lint --auditprints the same list as a
review artifact, and the interpreter records which blocks were actually
entered during a run. This is a review boundary, not a permission: the block
executes exactly as written.requires/ensurescontracts onfn. Preconditions are checked with
the parameters bound and before a single statement of the body, so a violated
precondition blames the caller and a function that would corrupt state before
validating never gets the chance. Postconditions run after deferred cleanup,
withresultand the parameters in scope. Contracts survive intoasync fn,
on both the sequential and the concurrent drive path.rakc verify. Runs a script under finite step, loop-iteration and
recursion-depth budgets. It reports three outcomes and they are deliberately
distinct, because conflating them would be misleading:PASS(completed, all
contracts held),FAIL(a contract broke, an assert tripped, or a runtime
error), andSKIP(a budget was hit, so nothing was proved). Exit code 2 means
inconclusive, never "looks fine".- Constant-time comparison.
ct_eq,ct_eq_hex, andct_select. Rak's==
is a data-dependent branch that leaks the length of a shared prefix through its
exit timing, which is enough to recover a MAC or a session token one byte at a
time. - Zeroization.
zeroize(bytes)overwrites a buffer through a volatile path
the optimizer cannot elide.secret_deleteand the newsecret_delete_all
wipe values from memory and overwrite the store file with zeroes before
rewriting it. The serialized buffer in the secrets store is wiped too, and
file permissions are now set before the data lands rather than after.
Crypto
- RSA —
rsa_keypair,rsa_sign,rsa_verify,rsa_encrypt,rsa_decrypt
(PKCS#1 v1.5 over SHA-256, OAEP with SHA-256). Keys are DER. - ECDSA over NIST P-256 —
ecdsa_keypair,ecdsa_sign,ecdsa_verify, with
64-byter||ssignatures. DER keys.
The suite previously had only Ed25519 and X25519, both non-standard curves, so
nothing interoperated with ordinary OpenSSL tooling. Everything remains pure Rust
with no OpenSSL linkage.
Static analysis
- Six new security lint rules:
hardcoded-secret,plaintext-url,
weak-crypto,secret-compare,ffi-raw-pointer,insecure-transport, plus
unsafe-thin-reason. - This required fixing a real bug: the linter had no
Expr::Stringarm, so
string literals fell through to_ => {}and no rule could ever inspect a
byte of user text. A hardcodedsk-live-...was completely invisible. There is
a regression test for exactly that. rakc fmtandrakc lintare now listed inrakc --help.
Packet crafting
- ICMP —
net_raw_icmp,net_raw_icmp_ping,net_raw_icmp_echo_reply, with
matchingid/seqso a reply can be correlated to its request. - ARP —
net_raw_arp_request,net_raw_arp_reply, andnet_raw_arp_parse,
which accepts a frame with or without the Ethernet header and returnsnilon
anything that is not ARP, so raw capture can be fed straight in.
caps.rs previously gated a net_raw_icmp builtin that did not exist; the gate
now matches real builtins, and packet builders are documented as reachable
inside a sandbox because they only construct a bytes value.
Fuzzing on the stable toolchain
rakc fuzz <target>— a deterministic mutation loop in the compiler
itself, covering the lexer, parser, interpreter and the stdlib parsers. 12
targets, reproducible via--seed, with a crashing input written to disk and
the seed printed for replay.- The 13
cargo-fuzztargets infuzz/are still there for long
coverage-guided campaigns, but they need nightly and-fsanitize=fuzzer,
whichstable-x86_64-pc-windows-msvccannot provide. Nothing was running them
automatically before this.
Build hardening
- Release profile gains
lto = "fat",codegen-units = 1,panic = "abort"
andstrip = "symbols". The one that matters most isoverflow-checks = true:
Cargo's default in release isfalse, which silently wraps and turns a length
calculation into a heap overflow. .cargo/config.tomladds/guard:cf,/CETCOMPAT,/DYNAMICBASE,
/HIGHENTROPYVA,/NXCOMPATon MSVC, and full RELRO, a non-executable stack,
a stack canary and forced frame pointers on Linux.- Stated precisely, because these are easy to oversell.
/guard:cfmarks the
image CFG-compatible and guards the dispatch table, but rustc does not
instrument Rust's own indirect calls, so there is no__guard_check_icallin
the binary. This is not equivalent to Clang's-fsanitize=cfi. It closes a
real class of bugs; it does not close ROP or JOP. dist/verify_hardening.pyreads the produced binary back and asserts the bits
are really set, and both release jobs run it. It has a--self-testcovering
both the PE and ELF parsers on synthetic headers, positive and negative. This
already caught a real mistake: an early flag set passed both/NXCOMPATand
/NXCOMPAT:NO, silently disabling DEP.
Validating the hardening against real binaries
Both platforms were checked before tagging, by building the actual rakc and
reading the result back rather than trusting the flag list.
- Linux:
rakc(7.8 MB) built in WSL with the repo's exact
.cargo/config.toml. PIE, full RELRO, non-executable stack andBIND_NOW
all land correctly. The stack canary does not, and cannot be made to:
rustc'sx86_64-unknown-linux-gnutarget does not enable-fstack-protector,
and-C target-feature=+stack-protectoris rejected outright with "not a
recognized feature for this target". Getting a canary into Rust code needs
nightly-Z stack-protector, or a C object built with GCC's
-fstack-protectorand linked in. - So the verifier reports the canary as an advisory finding rather than a
required one. A check the stable toolchain cannot satisfy is either a
permanently red build or something everyone learns to ignore, and neither is
useful. The four checks that are satisfiable remain hard failures. - The same exercise turned up that
rustflagsis silently ignored when placed in
Cargo.toml("unused manifest key"). It only takes effect in
.cargo/config.toml, which is where it lives. A virtual manifest also rejects
a[target]section outright.
Deep recursion no longer kills the process
The tree walker burned several native frames per Rak call, and a Rak call costs
several KB of native stack in a debug build. fib(15) needed roughly a megabyte,
which is more than the default thread stack allows, so a moderately recursive
program died with a bare "has overflowed its stack" and no Rak-level line number.
The interpreter now runs on a thread with an explicit 64 MB stack. rakc verify
additionally bounds recursion depth, loop iterations and total steps, so runaway
recursion is a reportable outcome rather than a crash.
Tooling
- The VS Code TextMate grammar's builtin list was a single 2,900-character line,
close to TextMate's practical limits and unmaintainable. It is now 17 grouped
per-family patterns, the longest 371 characters. - The builtin lists in the LSP, the IDE editor, and the grammar were all stale
and disagreed with each other. All three are updated, and the LSP list had been
missing the entire batteries, OSINT, iterator, FFI, mmap andnet_rawfamilies. - New
docs/content/safety.md, covering what is enforced and — just as
importantly — what is not.
Correctness
docs/content/vm.mdclaimed binary pattern matching, method-call dispatch and
user enum patterns were unsupported on the VM. All three shipped; the table was
wrong.Pattern::Binddoes not exist: the@binding-pattern syntax the spec
advertised does not parse. Marked[SPEC]rather than[SHIPPED], with the
implementation steps recorded. Spec markers in both directions were wrong and
are now corrected against the code.expr_strfell back to the AST debug form for calls, indexing, field access
and ranges. Contract clauses and assertion failures are mostly calls, so the
most important diagnostics were the least readable. Now rendered as source.
Housekeeping
- All version strings moved to 8.0.0, including the REPL banner (still said
v0.3.0) andrakpkg(still said 0.7.0). raklib/was an empty untracked directory. Removed.adblocker/added to.gitignore: it is a nested git repository kept
deliberately local, and without thisgit add -Afails outright.scripts/run-tests-safe.ps1runs the test binary under a memory and time
watchdog. A stack overflow becomes a Windows Error Reporting event, and with
the system default of automatic memory dumps that writes a multi-gigabyte file.
Known limitations
Stated plainly rather than discovered later.
- The GUI is incomplete and Windows-only.
gui_update,gui_titleand
gui_closeare declared but do nothing:gui.rsstoresHashMap<i64, ()>and
discards theWindowandWebViewhandles. JavaScript cannot call into Rak —
the IPC handler receives the message and drops it. Closing a window ends the
process, because tao'sruncallsprocess::exit. Linux does not work at all,
becausegui_openbuilds the event loop off the main thread, which tao
rejects. The VM has no GUI natives, sorakc vmreports
Undefined: gui_open. The README and docs now say this instead of implying
otherwise. - No ownership or borrowing. There is no
&reference type;&is
bitwise-and. Values are shared rather than moved and there is no lifetime
tracking. This is a language redesign, not a version bump. - No instrumented control-flow integrity. See the hardening section above for
exactly what is and is not provided. - No inline assembly.
extern "C"FFI is the escape hatch, and it is
capability-gated and lint-flagged. - No formal verification. Contracts are checked at runtime; there is no
prover, no loop invariants, no pre/postconditions in the refinement-type sense. - No enclaves. The capability sandbox is name-based and in-process. It does
not confine the process from the kernel. For genuinely untrusted code, run the
script in a container or a VM. - Sets are not implemented (7A.11), and the backend parity items in
docs/rak-features-spec.mdPart 7A.4–7A.6 (VM streams, VMtunnel/udp_*, VM
import pkg.sub) remain open.
docs/V8-ROADMAP.md records the reasoning and the cost for each gap.