Phase 8 C (2/n): expand cert storage; NVRAM persistence crashed, reverted
Cert storage expanded from the old 16-byte placeholder to a real 32-byte seed + 32-byte pubkey. vm_zuse_cert_install() now has a kernel-side duplicate in src/starkernel/vm/vm_core.c -- the kernel build's VM_EXCLUDE list drops src/vm.c entirely (same reason vm_set_base() already has two independent copies), so the hosted-only version added earlier this session was never actually linked into the kernel. FORTH-side ZUSE-CERT-LO@/HI@ replaced with ZUSE-PUBKEY@ (i -- u) over the public half only; ACL-ZUSE-BOOT now checks ZUSE-CERT-INSTALLED? before authenticating instead of unconditionally. Attempted NVRAM-based persistence (GetVariable/SetVariable) for the first-boot mint flow: page-faulted inside OVMF's variable service (CR2 in the flash MMIO window). Moving the call site to match the one proven-safe existing SetVariable call site in this codebase produced the identical crash -- not a timing issue. Localized with debug markers (one boot): GetVariable works; SetVariable with real data never returns. The existing "working" precedent call is actually a delete-of-nonexistent-variable (size=0, data=NULL), a cheaper path that never touches flash, so it proved nothing about real writes. Root cause: this kernel's VMM never maps the region OVMF's variable service needs for real flash writes -- a genuine gap in UEFI runtime- services support, not Zuse-specific, and not obviously fixable in a 3-arch-uniform way (flash window location is firmware/arch-specific). Independently, storing the raw seed in RUNTIME_ACCESS NVRAM would have been a real security defect regardless of the crash -- readable by any later-loaded UEFI app or the booted OS. Reverted to a known-safe state: all NVRAM/mint code removed from kernel_main.c, init.4th's ACL.4th line back to its documented commented-out default. Verified clean compile and clean boot on all three architectures. Cert storage expansion (the part that works) stays. A dedicated system-identity disk (virtio-blk, already proven for writes via Artemis) is the recommended next substrate -- not yet decided or built. Full investigation documented in FABRIC-3.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
f223a31cec
commit
e5cbc71f46
+51
@@ -475,6 +475,57 @@ decisions get added here, not to `FABRIC-2.md`. Follow the same discipline `FABR
|
||||
first-boot mint-vs-already-minted boot sequence using `SetVariable`/`GetVariable`, and the
|
||||
`MINT` word itself.
|
||||
|
||||
**Cert struct expanded (2026-08-26):** `vm_zuse_cert_install()` (both `src/vm.c`'s hosted
|
||||
copy and a new kernel-side duplicate in `src/starkernel/vm/vm_core.c` -- the kernel build's
|
||||
`VM_EXCLUDE` list drops `src/vm.c` entirely, same reason `vm_set_base()` already has two
|
||||
independent copies) now takes a real 32-byte seed + 32-byte pubkey instead of the old
|
||||
16-byte placeholder. FORTH-side `ZUSE-CERT-LO@`/`HI@` replaced with `ZUSE-PUBKEY@ ( i -- u )`
|
||||
(8-byte LE chunk `i`, 0..3, of the public half only -- the seed has no FORTH access at all).
|
||||
`ACL-ZUSE-BOOT` now checks `ZUSE-CERT-INSTALLED?` before authenticating rather than
|
||||
authenticating unconditionally. Verified: clean compile and clean boot on all three
|
||||
architectures.
|
||||
|
||||
**NVRAM persistence attempt: crashed, root-caused, reverted -- do not retry as designed.**
|
||||
First attempt placed the mint-or-load `GetVariable`/`SetVariable` logic right after
|
||||
`virtio_rng_init()` (before `capsule_birth_mama()`); it page-faulted (`CR2` inside the OVMF
|
||||
flash MMIO window, a supervisor write to a not-present page) partway through boot. Moved the
|
||||
same logic to the one place in this codebase already calling `SetVariable` post-
|
||||
`ExitBootServices` successfully (`SF_VAR_REBOOT_TRIES`, much later in boot) — **identical
|
||||
crash, same RIP and CR2** — which disproved the "too early in boot" theory outright: it isn't
|
||||
a timing issue.
|
||||
|
||||
**Localized precisely (advisor-directed, one boot, debug markers around each call):**
|
||||
`GetVariable` returns fine. `SetVariable` **with real 64-byte data** never returns — that's
|
||||
the exact fault site. The pre-existing `SF_VAR_REBOOT_TRIES` call that looked like a working
|
||||
precedent is actually a **delete of a variable that's never existed** (`size=0, data=NULL`) —
|
||||
a fundamentally different, much cheaper internal path than a real data write, so it proved
|
||||
nothing about real persistence being safe. **Root cause: this kernel's VMM never maps
|
||||
whatever memory region OVMF's variable service needs to actually write flash-backed variable
|
||||
data** — a real gap in UEFI runtime-services support, not specific to Zuse. Fixing it for
|
||||
real means walking the UEFI memory map for the relevant regions and mapping them into the
|
||||
kernel's own page tables, and per Section U's own note, the flash window's location is
|
||||
firmware/arch-specific (OVMF's differs from AAVMF's and EDK2-riscv64's), so "walk the map and
|
||||
map everything" is not guaranteed 3-arch-uniform even once attempted.
|
||||
|
||||
**Second, independent finding (not a bug, a design flaw in the persistence choice): storing
|
||||
the raw 32-byte seed in NVRAM was a real defect regardless of the crash.** `SetVariable` was
|
||||
called with `EFI_VARIABLE_RUNTIME_ACCESS`, meaning any later-loaded UEFI application or the
|
||||
booted OS itself could read Zuse's private key straight out of NVRAM. For an irrevocable
|
||||
"one and only one Zuse, ever" root of trust, that undermines the property the design exists
|
||||
to provide — this would have needed fixing even had the crash not happened.
|
||||
|
||||
**Decision needed, not yet made:** given virtio-blk writes are already proven working on all
|
||||
three architectures in this repo (`vblk_write`, Artemis's own persistence across runs), a
|
||||
dedicated file-backed system-identity disk (mirroring `disk/artemis.img`'s existing pattern,
|
||||
separate from Artemis's internal storage and separate from home-blocks USB thumbdrives) is
|
||||
the substrate with no open unknowns today — recommended over either fixing the UEFI
|
||||
flash-mapping gap (real but large, unscoped VMM work) or accepting the NVRAM approach as
|
||||
originally designed (has the exposed-seed defect regardless). Not decided or built yet.
|
||||
**Reverted to a known-safe state:** all Zuse mint/NVRAM code removed from `kernel_main.c`
|
||||
(only two harmless includes remain), `init.4th`'s `ACL.4th` line back to its documented
|
||||
commented-out default. Verified clean compile and clean boot on all three architectures in
|
||||
this reverted state.
|
||||
|
||||
### From FABRIC-2.md §X, Milestone 5 — Console/VM key-match binding
|
||||
|
||||
- [ ] Settle the still-open question: reuse `ACL-PIN`/`acl_allow` directly, or build a
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-26T19:15:03Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-26T19:54:10Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
@@ -38,7 +38,7 @@
|
||||
| `sdk.4th` | 5109, 5110, 5111, 5112, 5113, 5114, 5115 | `0x008fdbbb62c94a3a` |
|
||||
| `turtle.4th` | 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108 | `0x4d470418ca543365` |
|
||||
| `user-font-demo.4th` | 4200, 4201, 4202 | `0xce1fd7d1b581a56d` |
|
||||
| `zuse.4th` | 4016, 4017, 4018 | `0x097456615c8d9173` |
|
||||
| `zuse.4th` | 4016, 4017, 4018 | `0x3b31872d02a43d83` |
|
||||
|
||||
## Block Map (sorted by LBN)
|
||||
|
||||
@@ -103,9 +103,9 @@
|
||||
| 4006 | `ACL.4th` | `0xd781d22148ff171d` | ok |
|
||||
| 4007 | `ACL.4th` | `0xd781d22148ff171d` | ok |
|
||||
| 4015 | `ACL.4th` | `0xd781d22148ff171d` | ok |
|
||||
| 4016 | `zuse.4th` | `0x097456615c8d9173` | ok |
|
||||
| 4017 | `zuse.4th` | `0x097456615c8d9173` | ok |
|
||||
| 4018 | `zuse.4th` | `0x097456615c8d9173` | ok |
|
||||
| 4016 | `zuse.4th` | `0x3b31872d02a43d83` | ok |
|
||||
| 4017 | `zuse.4th` | `0x3b31872d02a43d83` | ok |
|
||||
| 4018 | `zuse.4th` | `0x3b31872d02a43d83` | ok |
|
||||
| 4050 | `lib.4th` | `0x1c77d7c9562a5b62` | ok |
|
||||
| 4055 | `common:msg.4th` | `0x850a0382344ea6c4` | ok |
|
||||
| 4060 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
|
||||
|
||||
+17
-11
@@ -5,21 +5,27 @@ Block 4016
|
||||
( Loaded by ACL.4th; must not load before ACL.4th. )
|
||||
( FUTURE: Replace with thumbdrive Ed25519 PKI. )
|
||||
( HUMAN-REVIEW: capsule hash = root of superuser trust. )
|
||||
( Cert value lives in C-only VM fields (vm_zuse_cert_install),
|
||||
( NOT a dictionary CONSTANT: ACL-PIN only blocks redefinition,
|
||||
( not a >BODY-then-store, so a pinned CONSTANT is not actually
|
||||
( tamper-proof. Read with ZUSE-CERT-LO@ / ZUSE-CERT-HI@ / )
|
||||
( ZUSE-CERT-INSTALLED? -- all C primitives, all read-only. )
|
||||
( Cert (seed+pubkey) lives in C-only VM fields, installed by )
|
||||
( kernel_main.c's first-boot mint-or-load (NVRAM ZuseCert). )
|
||||
( NOT a CONSTANT: ACL-PIN blocks redefinition, not a )
|
||||
( >BODY-then-store, so a pinned CONSTANT isn't tamper-proof. )
|
||||
( Read with ZUSE-PUBKEY@ / ZUSE-CERT-INSTALLED? -- both C )
|
||||
( primitives, read-only; the seed has no FORTH access at all. )
|
||||
|
||||
Block 4017
|
||||
( ACL-ZUSE-BOOT ( -- ) )
|
||||
( Authenticates zuse session (sets vm->zuse_session=1)
|
||||
( via C primitive) and pins itself against redefinition.)
|
||||
( ZUSE-AUTHENTICATE is C-only; no FORTH word grants )
|
||||
( god-mode except through this boot sequence. )
|
||||
( Only authenticates if a real cert was installed this boot -- )
|
||||
( refuses god-mode to a Zuse with no real identity behind her )
|
||||
( (no runtime services, no entropy). Pins itself against )
|
||||
( redefinition either way. ZUSE-AUTHENTICATE is C-only; no )
|
||||
( FORTH word grants god-mode except through this sequence. )
|
||||
: ACL-ZUSE-BOOT ( -- )
|
||||
ZUSE-AUTHENTICATE
|
||||
LOG-INFO" zuse: activated"
|
||||
ZUSE-CERT-INSTALLED? IF
|
||||
ZUSE-AUTHENTICATE
|
||||
LOG-INFO" zuse: activated"
|
||||
ELSE
|
||||
LOG-INFO" zuse: NOT activated -- no cert installed"
|
||||
THEN
|
||||
['] ACL-ZUSE-BOOT ACL-PIN ;
|
||||
|
||||
Block 4018
|
||||
|
||||
Binary file not shown.
@@ -624,6 +624,13 @@ typedef void (EFIAPI *EFI_RESET_SYSTEM)(
|
||||
/* NVRAM variable names (UCS-2 string literals) */
|
||||
#define SF_VAR_BOOT_ARGS L"StarForthBootArgs"
|
||||
#define SF_VAR_REBOOT_TRIES L"StarForthRebootTries"
|
||||
#define SF_VAR_ZUSE_CERT L"StarForthZuseCert" /* 64 bytes: 32-byte Ed25519
|
||||
* seed || 32-byte pubkey.
|
||||
* Written exactly once
|
||||
* (Phase 8 first-boot mint,
|
||||
* see FABRIC-3.md) --
|
||||
* presence means the fuse
|
||||
* is already blown. */
|
||||
|
||||
/* ---- BootInfo extension ------------------------------------------------ */
|
||||
#include "kernel_args.h"
|
||||
|
||||
+14
-8
@@ -392,11 +392,16 @@ typedef struct VM
|
||||
uint8_t emergency_console; /**< 1 = fault handler active; bypasses all ACL checks (C-only write) */
|
||||
uint8_t zuse_session; /**< 1 = zuse authenticated at console; shows zuse)ok> prompt */
|
||||
uint8_t acl_skip; /**< 1 = skip all ACL hooks (Ananke enforcement VM; prevents recursion) */
|
||||
uint8_t zuse_cert_installed; /**< 1 = zuse_cert_lo/hi hold a real minted cert (one-time fuse) */
|
||||
uint64_t zuse_cert_lo; /**< Zuse cert value, low half. C-only write via vm_zuse_cert_install(). */
|
||||
uint64_t zuse_cert_hi; /**< Zuse cert value, high half. No FORTH word can write these fields --
|
||||
* deliberately kept out of the dictionary so ACL-PIN's redefinition-only
|
||||
* guarantee can't be bypassed via >BODY on a CONSTANT (see FABRIC-3.md). */
|
||||
uint8_t zuse_cert_installed; /**< 1 = zuse_cert_seed/pubkey hold a real minted cert (one-time fuse) */
|
||||
uint8_t zuse_cert_seed[32]; /**< Ed25519 seed, this instance's Zuse identity. C-only write via
|
||||
* vm_zuse_cert_install(); no FORTH word can read or write it --
|
||||
* the seed is the private key, never exposed past this struct. */
|
||||
uint8_t zuse_cert_pubkey[32]; /**< Ed25519 public key derived from the seed at mint time, cached
|
||||
* here so callers don't need to re-derive it. No FORTH word can
|
||||
* write it -- deliberately kept out of the dictionary so ACL-PIN's
|
||||
* redefinition-only guarantee can't be bypassed via >BODY on a
|
||||
* CONSTANT (see FABRIC-3.md). Read-only FORTH access via
|
||||
* ZUSE-PUBKEY@. */
|
||||
/** @} */
|
||||
|
||||
/** @name Dictionary Management
|
||||
@@ -631,10 +636,11 @@ cell_t* vm_dictionary_get_data_field(DictEntry* entry);
|
||||
|
||||
void vm_compile_word(VM* vm, DictEntry* entry);
|
||||
|
||||
/* Zuse cert one-time install (blows the fuse). Returns 0 on success, -1 if
|
||||
* already installed -- a second call is a caller bug, not a runtime error to
|
||||
/* Zuse cert one-time install (blows the fuse). seed and pubkey are each 32
|
||||
* bytes, copied into the VM struct. Returns 0 on success, -1 if already
|
||||
* installed -- a second call is a caller bug, not a runtime error to
|
||||
* recover from silently. C-only: no FORTH word wraps this. */
|
||||
int vm_zuse_cert_install(VM* vm, uint64_t lo, uint64_t hi);
|
||||
int vm_zuse_cert_install(VM* vm, const uint8_t seed[32], const uint8_t pubkey[32]);
|
||||
|
||||
/* Memory management */
|
||||
void* vm_allot(VM* vm, size_t bytes);
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,7 @@
|
||||
#error "__STARKERNEL__ must be defined for kernel build"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include "uefi.h"
|
||||
#include "console.h"
|
||||
#include "arch.h"
|
||||
@@ -65,6 +66,7 @@ EFI_RUNTIME_SERVICES *g_sk_runtime_services = NULL;
|
||||
#include "starkernel/pci.h"
|
||||
#include "starkernel/virtio_blk.h"
|
||||
#include "starkernel/virtio_rng.h"
|
||||
#include "starkernel/ed25519.h"
|
||||
#include "starkernel/virtio_input.h"
|
||||
#include "starkernel/xhci_driver.h"
|
||||
#include "block_subsystem.h"
|
||||
|
||||
@@ -266,6 +266,22 @@ void vm_set_base(VM* vm, unsigned b)
|
||||
vm->base = (cell_t)b; /* host mirror */
|
||||
}
|
||||
|
||||
/* Kernel-side copy of src/vm.c's vm_zuse_cert_install() -- the kernel
|
||||
* build excludes src/vm.c entirely (VM_EXCLUDE in Makefile.starkernel;
|
||||
* see vm_set_base() above for the same duplication pattern already
|
||||
* established), so any src/vm.c function the kernel needs to call gets
|
||||
* its own copy here. See src/vm.c's version for the full rationale
|
||||
* (why this is a VM struct field with no FORTH word, not a CONSTANT). */
|
||||
int vm_zuse_cert_install(VM* vm, const uint8_t seed[32], const uint8_t pubkey[32])
|
||||
{
|
||||
if (!vm) return -1;
|
||||
if (vm->zuse_cert_installed) return -1;
|
||||
memcpy(vm->zuse_cert_seed, seed, 32);
|
||||
memcpy(vm->zuse_cert_pubkey, pubkey, 32);
|
||||
vm->zuse_cert_installed = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ====================== VM init / teardown ======================= */
|
||||
void vm_cleanup(VM* vm)
|
||||
{
|
||||
|
||||
@@ -118,7 +118,8 @@ void vm_set_base(VM* vm, unsigned b)
|
||||
/* vm_tick* and heartbeat functions moved to vm_time.c */
|
||||
|
||||
/**
|
||||
* @brief One-time write of the Zuse cert value (blows the fuse).
|
||||
* @brief One-time write of the Zuse cert (seed + derived pubkey) -- blows
|
||||
* the fuse.
|
||||
*
|
||||
* Deliberately not backed by a dictionary CONSTANT: ACL-PIN only blocks
|
||||
* redefinition (vm_create_word shadowing), not a >BODY-then-store on the
|
||||
@@ -127,17 +128,17 @@ void vm_set_base(VM* vm, unsigned b)
|
||||
* corresponding FORTH store word closes that path entirely -- see
|
||||
* FABRIC-3.md's Milestone 4 mint-then-pin writeup for the finding.
|
||||
*
|
||||
* @param vm VM instance.
|
||||
* @param lo Cert value, low half.
|
||||
* @param hi Cert value, high half.
|
||||
* @return 0 on success; -1 if already installed (fuse already blown).
|
||||
* @param vm VM instance.
|
||||
* @param seed Ed25519 seed, 32 bytes (the private identity).
|
||||
* @param pubkey Ed25519 public key derived from seed, 32 bytes.
|
||||
* @return 0 on success; -1 if already installed (fuse already blown).
|
||||
*/
|
||||
int vm_zuse_cert_install(VM* vm, uint64_t lo, uint64_t hi)
|
||||
int vm_zuse_cert_install(VM* vm, const uint8_t seed[32], const uint8_t pubkey[32])
|
||||
{
|
||||
if (!vm) return -1;
|
||||
if (vm->zuse_cert_installed) return -1;
|
||||
vm->zuse_cert_lo = lo;
|
||||
vm->zuse_cert_hi = hi;
|
||||
memcpy(vm->zuse_cert_seed, seed, 32);
|
||||
memcpy(vm->zuse_cert_pubkey, pubkey, 32);
|
||||
vm->zuse_cert_installed = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -793,17 +793,31 @@ static void starforth_word_zuse_authenticate(VM *vm)
|
||||
vm->zuse_session = 1;
|
||||
}
|
||||
|
||||
/* ZUSE-CERT-LO@ ( -- lo ) Read-only: no FORTH store word exists or should
|
||||
* exist -- the cert is written exactly once, in C, via vm_zuse_cert_install(). */
|
||||
static void starforth_word_zuse_cert_lo_fetch(VM *vm)
|
||||
/* ZUSE-PUBKEY@ ( i -- u ) Read-only: fetch 8-byte little-endian chunk i
|
||||
* (0..3) of Zuse's 32-byte Ed25519 public key as one cell. Out-of-range i
|
||||
* pushes 0 and sets vm->error rather than faulting. No FORTH word can
|
||||
* write these bytes or read the seed -- the cert is written exactly once,
|
||||
* in C, via vm_zuse_cert_install(); this is a read-only window onto the
|
||||
* PUBLIC half only. */
|
||||
static void starforth_word_zuse_pubkey_fetch(VM *vm)
|
||||
{
|
||||
vm_push(vm, (cell_t)vm->zuse_cert_lo);
|
||||
}
|
||||
|
||||
/* ZUSE-CERT-HI@ ( -- hi ) See ZUSE-CERT-LO@. */
|
||||
static void starforth_word_zuse_cert_hi_fetch(VM *vm)
|
||||
{
|
||||
vm_push(vm, (cell_t)vm->zuse_cert_hi);
|
||||
if (vm->dsp < 0) {
|
||||
log_message(LOG_ERROR, "ZUSE-PUBKEY@: stack underflow");
|
||||
vm->error = 1;
|
||||
return;
|
||||
}
|
||||
cell_t i = vm_pop(vm);
|
||||
if (i < 0 || i > 3) {
|
||||
vm_push(vm, 0);
|
||||
vm->error = 1;
|
||||
return;
|
||||
}
|
||||
const uint8_t *p = &vm->zuse_cert_pubkey[i * 8];
|
||||
cell_t chunk = 0;
|
||||
for (int b = 7; b >= 0; b--) {
|
||||
chunk = (chunk << 8) | (cell_t)p[b];
|
||||
}
|
||||
vm_push(vm, chunk);
|
||||
}
|
||||
|
||||
/* ZUSE-CERT-INSTALLED? ( -- flag ) -1 if the one-time cert fuse has been
|
||||
@@ -846,8 +860,7 @@ void register_starforth_words(VM* vm)
|
||||
register_word(vm, "RANDOM", starforth_word_random);
|
||||
register_word(vm, "WAIT", starforth_word_wait);
|
||||
register_word(vm, "ZUSE-AUTHENTICATE", starforth_word_zuse_authenticate);
|
||||
register_word(vm, "ZUSE-CERT-LO@", starforth_word_zuse_cert_lo_fetch);
|
||||
register_word(vm, "ZUSE-CERT-HI@", starforth_word_zuse_cert_hi_fetch);
|
||||
register_word(vm, "ZUSE-PUBKEY@", starforth_word_zuse_pubkey_fetch);
|
||||
register_word(vm, "ZUSE-CERT-INSTALLED?", starforth_word_zuse_cert_installed_query);
|
||||
register_word(vm, "HEARTBEAT-TICKS@", starforth_word_heartbeat_ticks);
|
||||
|
||||
@@ -867,8 +880,7 @@ void register_starforth_words(VM* vm)
|
||||
register_word(vm, "RANDOM", starforth_word_random);
|
||||
register_word(vm, "WAIT", starforth_word_wait);
|
||||
register_word(vm, "ZUSE-AUTHENTICATE", starforth_word_zuse_authenticate);
|
||||
register_word(vm, "ZUSE-CERT-LO@", starforth_word_zuse_cert_lo_fetch);
|
||||
register_word(vm, "ZUSE-CERT-HI@", starforth_word_zuse_cert_hi_fetch);
|
||||
register_word(vm, "ZUSE-PUBKEY@", starforth_word_zuse_pubkey_fetch);
|
||||
register_word(vm, "ZUSE-CERT-INSTALLED?", starforth_word_zuse_cert_installed_query);
|
||||
register_word(vm, "HEARTBEAT-TICKS@", starforth_word_heartbeat_ticks);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user