mirror of
https://dev.lirent.ru/Vatrog/vm-automation-signaling.git
synced 2026-08-11 14:16:41 +03:00
memwrite: per-process (cr3) target and full-extent socket SRC
- CMD_MEMWRITE now carries a target page-table root (cr3) as its first field; cr3 == 0 keeps the kernel address-space default (backward-compatible). A control that has discovered a process's cr3 through its own read-only perception can write that process's private memory under the same exclusive write lease. Freshness of the cr3 is the control's responsibility — signaling does not validate it (that is perception, not coherence), mirroring the read side. - A socket control can now carry an SRC larger than the inline frame budget: a length-prefixed SRC tail follows the CMD_MEMWRITE frame (flag SRC_PAYLOAD, the length being the frame's own len). A per-connection two-phase receiver accumulates the tail into a fixed conn-owned buffer up to the extent bound, matching the in-process payload path. A zero or over-bound length is a framing violation that closes the connection: leaving the promised tail unread would desync the stream and draining an arbitrary length would be a denial of service. The capability, exclusive lease, source and extent gates are unchanged and reused; only the event header gained the cr3 field and the socket transport gained the tail receiver. The adapter resolves cr3 == 0 to the kernel root on its worker thread and writes atomically.
This commit is contained in:
@@ -90,13 +90,14 @@ static int acquire_mw(void* ctl) {
|
||||
return vmsig_inproc_send(ctl, &d);
|
||||
}
|
||||
|
||||
/* CMD_MEMWRITE with inline SRC; corr for tracking. flags: VMSIG_MW_SRC_* (0 => no SRC). */
|
||||
static int send_write(void* ctl, uint64_t gva, uint32_t len, uint32_t flags,
|
||||
/* CMD_MEMWRITE with inline SRC; corr for tracking. flags: VMSIG_MW_SRC_* (0 => no SRC).
|
||||
* cr3 selects the target AS: 0 => kernel default (System DTB), non-zero => a process AS. */
|
||||
static int send_write(void* ctl, uint64_t cr3, uint64_t gva, uint32_t len, uint32_t flags,
|
||||
const void* src, uint32_t corr) {
|
||||
vmsig_event d; memset(&d, 0, sizeof d);
|
||||
d.kind = VMSIG_EV_CMD_MEMWRITE; d.source = VMSIG_SRC_MEMCTX; d.dir = VMSIG_DIR_DOWN;
|
||||
d.endpoint = 0; d.prio = VMSIG_PRIO_HIGH; d.corr = corr;
|
||||
vmsig_memwrite mw = { gva, len, flags };
|
||||
vmsig_memwrite mw = { cr3, gva, len, flags };
|
||||
memcpy(d.inln, &mw, sizeof mw);
|
||||
if ((flags & VMSIG_MW_SRC_INLINE) && src && len <= VMSIG_MEMWRITE_INLINE)
|
||||
memcpy(d.inln + sizeof mw, src, len);
|
||||
@@ -123,21 +124,21 @@ static void test_path_and_deny(void) {
|
||||
|
||||
/* 3) lease gate: without ACQUIRE the write is dropped at the gate (-1, no actuation). */
|
||||
uint8_t pat[8] = { 0xDE, 0xAD, 0xBE, 0xEF, 1, 2, 3, 4 };
|
||||
CHECK(send_write(A, 0x1000, 8, VMSIG_MW_SRC_INLINE, pat, 99) == -1,
|
||||
CHECK(send_write(A, 0, 0x1000, 8, VMSIG_MW_SRC_INLINE, pat, 99) == -1,
|
||||
"3: CMD_MEMWRITE without a lease is dropped by the gate");
|
||||
|
||||
/* acquire the MEMWRITE lease (synchronous intercept; UP reply paced by ctx). */
|
||||
CHECK(acquire_mw(A) == 0, "acquire submitted");
|
||||
|
||||
/* 1) happy path: inline write -> queued -> ACT_ACK{ok=1}. Also drains the GRANTED reply. */
|
||||
CHECK(send_write(A, 0x1000, 8, VMSIG_MW_SRC_INLINE, pat, 11) == 0,
|
||||
CHECK(send_write(A, 0, 0x1000, 8, VMSIG_MW_SRC_INLINE, pat, 11) == 0,
|
||||
"1: owner's CMD_MEMWRITE passes the gate");
|
||||
|
||||
/* 2) extent: len > MAX -> ACK{ok=0}, NOT actuated (queued ack on the loop thread). */
|
||||
CHECK(send_write(A, 0x2000, VMSIG_MEMWRITE_MAX + 1, VMSIG_MW_SRC_INLINE, pat, 22) == 0,
|
||||
CHECK(send_write(A, 0, 0x2000, VMSIG_MEMWRITE_MAX + 1, VMSIG_MW_SRC_INLINE, pat, 22) == 0,
|
||||
"2: over-extent write is accepted by the gate (denied inside the adapter)");
|
||||
/* 2b) missing SRC flag -> ACK{ok=0}. */
|
||||
CHECK(send_write(A, 0x3000, 4, 0u, NULL, 33) == 0,
|
||||
CHECK(send_write(A, 0, 0x3000, 4, 0u, NULL, 33) == 0,
|
||||
"2b: no-SRC-flag write is accepted by the gate (denied inside the adapter)");
|
||||
|
||||
/* expect 3 ACKs (corr 11/22/33) + the GRANTED reply. */
|
||||
@@ -160,6 +161,45 @@ static void test_path_and_deny(void) {
|
||||
vmsig_ctx_free(ctx);
|
||||
}
|
||||
|
||||
/* ---- 1b: cr3 default + explicit — the cr3 field threads the whole seam -------
|
||||
* Stub does not actuate, so this asserts CONTRACT/ROUTE only: both a kernel-default
|
||||
* (cr3==0) and an arbitrary process cr3 (cr3!=0) pass cap -> grant -> lease-gate ->
|
||||
* route -> adapter -> ACT_ACK{ok=1}. A non-zero cr3 does NOT bypass any gate (it is
|
||||
* read by the adapter only after the core admitted the command). */
|
||||
static void test_cr3_default_and_explicit(void) {
|
||||
printf("test_cr3_default_and_explicit\n");
|
||||
vmsig_ctx* ctx = vmsig_ctx_new();
|
||||
vmsig_core* core = vmsig_core_new(ctx);
|
||||
cstate s; memset(&s, 0, sizeof s); s.core = core;
|
||||
|
||||
void* A = add_ctl(core, &s, VMSIG_CAP_MEMWRITE, 10);
|
||||
CHECK(vmsig_core_add_adapter(core, vmsig_memctx_ops(), NULL, 0) >= 0, "add memctx");
|
||||
|
||||
CHECK(acquire_mw(A) == 0, "acquire submitted");
|
||||
|
||||
uint8_t pat[8] = { 0xAA, 0xBB, 0xCC, 0xDD, 5, 6, 7, 8 };
|
||||
/* (a) kernel default: cr3 == 0 -> resolves to the adapter's System DTB on the worker. */
|
||||
CHECK(send_write(A, 0, 0x4000, 8, VMSIG_MW_SRC_INLINE, pat, 71) == 0,
|
||||
"1b-a: cr3==0 (kernel default) passes the gate");
|
||||
/* (b) explicit process AS: an arbitrary non-zero cr3 is carried through unchanged. */
|
||||
CHECK(send_write(A, 0xDEADBEEF000ull, 0x5000, 8, VMSIG_MW_SRC_INLINE, pat, 72) == 0,
|
||||
"1b-b: cr3!=0 (process AS) passes the gate");
|
||||
|
||||
run_until_acks(&s, 2);
|
||||
|
||||
CHECK(s.granted == 1, "1b: lease GRANTED once");
|
||||
int saw71_ok = -1, saw72_ok = -1;
|
||||
for (int i = 0; i < s.nack; i++) {
|
||||
if (s.ack_corr[i] == 71) saw71_ok = s.ack_ok[i];
|
||||
if (s.ack_corr[i] == 72) saw72_ok = s.ack_ok[i];
|
||||
}
|
||||
CHECK(saw71_ok == 1, "1b-a: cr3==0 write ACKs ok=1 (kernel default, stub)");
|
||||
CHECK(saw72_ok == 1, "1b-b: cr3!=0 write ACKs ok=1 (route proven, stub does not actuate)");
|
||||
|
||||
vmsig_core_free(core);
|
||||
vmsig_ctx_free(ctx);
|
||||
}
|
||||
|
||||
/* ---- 4: cap gate — no CAP_MEMWRITE cannot acquire the lease ----------------- */
|
||||
static void test_cap_gate(void) {
|
||||
printf("test_cap_gate\n");
|
||||
@@ -195,11 +235,11 @@ static void test_inflight_fence(void) {
|
||||
uint8_t pat[4] = { 1, 2, 3, 4 };
|
||||
CHECK(acquire_mw(A) == 0, "A acquires");
|
||||
/* A queues a write (corr=55): passes the gate (A owns), lands in the DOWN queue. */
|
||||
CHECK(send_write(A, 0x1000, 4, VMSIG_MW_SRC_INLINE, pat, 55) == 0, "A queues write 55");
|
||||
CHECK(send_write(A, 0, 0x1000, 4, VMSIG_MW_SRC_INLINE, pat, 55) == 0, "A queues write 55");
|
||||
/* B preempts SYNCHRONOUSLY (acquire does not go through ctx). */
|
||||
CHECK(acquire_mw(B) == 0, "B preempts");
|
||||
/* B's own write (corr=66) — should actuate. */
|
||||
CHECK(send_write(B, 0x2000, 4, VMSIG_MW_SRC_INLINE, pat, 66) == 0, "B queues write 66");
|
||||
CHECK(send_write(B, 0, 0x2000, 4, VMSIG_MW_SRC_INLINE, pat, 66) == 0, "B queues write 66");
|
||||
|
||||
run_until_acks(&s, 1); /* B's 66 acks; A's 55 must be fenced (no ack) */
|
||||
|
||||
@@ -218,7 +258,11 @@ static void test_inflight_fence(void) {
|
||||
|
||||
int main(void) {
|
||||
printf("test_memwrite\n");
|
||||
/* POD layout: cr3+gva (2*u64) + len+flags (2*u32) = 24, +24 inline tail = inln[48]. */
|
||||
CHECK(sizeof(vmsig_memwrite) == 24, "vmsig_memwrite header is 24 bytes");
|
||||
CHECK(VMSIG_MEMWRITE_INLINE == 24u, "VMSIG_MEMWRITE_INLINE is 24");
|
||||
test_path_and_deny();
|
||||
test_cr3_default_and_explicit();
|
||||
test_cap_gate();
|
||||
test_inflight_fence();
|
||||
cref_free_all();
|
||||
|
||||
Reference in New Issue
Block a user