tawasuyu

tawasuyu/brahman

Rust 332TOML 127JSON 17Markdown 154710 Definitions

⎇ 65c88ccf250831dad70bbffc3d27ec96d80f4274 renaser/kernel/src/wasm/env.rs Rust · 448 lines · 18.2 KB History Who touched what raw

feat(renaser): Fase 8a — el compositor teselante 65c88ccf2 · sergio ·

1// =============================================================================
2//  renaser :: kernel/src/wasm/env.rs — Fase 4/5/6 :: la matriz de capacidades
3// -----------------------------------------------------------------------------
4//  El aislamiento de renaser no descansa en `int 0x80` ni en `sysenter`: no hay
5//  vectores de syscall. Una aplicacion WASM solo puede hacer aquello para lo
6//  que el kernel le haya inyectado una FUNCION DEL HOST. Esta matriz concede:
7//
8//    * sys_render_frame      — componer un fotograma en su region de pantalla;
9//    * sys_get_scancode      — consultar su canal de teclado;
10//    * sys_object_put        — grabar un objeto en el grafo (Fase 6.1c);
11//    * sys_object_datos      — leer la carga util de un objeto;
12//    * sys_object_hijo       — recorrer las aristas del DAG;
13//    * sys_object_raiz       — leer la raiz del grafo;
14//    * sys_object_fijar_raiz — coronar un objeto como raiz;
15//    * sys_estado_cargar     — leer el estado persistido de la app (Fase 7c);
16//    * sys_estado_guardar    — anclar el estado persistido de la app (Fase 7c).
17//
18//  GUARDARRAIL: el kernel valida MATEMATICAMENTE todo puntero que el modulo le
19//  entrega contra los limites reales de su memoria lineal. No se confia en que
20//  el runtime lo haga; se verifica aqui, antes de leer o escribir un solo byte.
21//
22//  DOS CLASES DE FALLO. Un puntero fuera de limites es CULPA DE LA APP: se
23//  devuelve un `Error` que la ABORTA (el kernel la captura y la desaloja). Un
24//  fallo del almacenamiento —disco, objeto inexistente— NO es culpa de la app:
25//  se le devuelve un codigo de error negativo, y la app decide que hacer.
26// =============================================================================
27
28use wasmi::{Caller, Error, Extern, Linker, Memory, StoreLimits};
29
30use crate::almacen::Hash;
31use crate::async_system::teclado::CanalTeclado;
32use crate::grafico::RegionPantalla;
33
34/// El estado del host adscrito al `Store` de una aplicacion: cuanto necesita
35/// una capacidad para servir a ESA app y a ninguna otra — su region de pantalla,
36/// su canal de teclado y sus cuotas de recursos. Dos apps jamas comparten nada.
37pub(crate) struct ContextoCapacidades {
38    /// El marco que el compositor (Fase 8) asigno a la app — el rectangulo de
39    /// pantalla donde vive. El kernel centra en el el fotograma de la app.
40    pub(crate) marco: RegionPantalla,
41    /// El tamaño natural del lienzo de la app, en pixeles. El fotograma que
42    /// entrega `sys_render_frame` mide exactamente `natural_ancho × natural_alto`;
43    /// el compositor lo coloca, sin deformarlo, dentro del `marco`.
44    pub(crate) natural_ancho: usize,
45    pub(crate) natural_alto: usize,
46    /// El canal de teclado propio de la aplicacion.
47    pub(crate) canal: CanalTeclado,
48    /// El techo de recursos de la aplicacion — hoy, su memoria lineal maxima.
49    /// `wasmi` lo consulta en cada `memory.grow` via `Store::limiter`.
50    pub(crate) limites: StoreLimits,
51    /// El indice de esta app en el Manifiesto de Genesis — su identidad. Las
52    /// capacidades de estado (Fase 7c) lo usan para hallar la `EntradaApp`
53    /// correcta: cada app persiste en SU ranura, jamas en la de otra.
54    pub(crate) indice_app: usize,
55}
56
57/// Recupera la memoria lineal exportada por el modulo. Que no la exporte es un
58/// modulo mal formado: se aborta.
59fn obtener_memoria(caller: &Caller<'_, ContextoCapacidades>) -> Result<Memory, Error> {
60    caller
61        .get_export("memory")
62        .and_then(Extern::into_memory)
63        .ok_or_else(|| Error::new("WASM :: el modulo no exporta su memoria lineal"))
64}
65
66/// VALIDACION INFRANQUEABLE DE LIMITES. Comprueba que `[ptr, ptr + len)` cae
67/// entera dentro de la memoria lineal `m` y devuelve ese sub-slice. Un rango
68/// que se desborde aborta la app — el `Error` se traduce en una trampa de WASM.
69fn rango<'a>(m: &'a [u8], ptr: u32, len: usize, fallo: &'static str) -> Result<&'a [u8], Error> {
70    let inicio = ptr as usize;
71    match inicio.checked_add(len) {
72        Some(fin) if fin <= m.len() => Ok(&m[inicio..fin]),
73        _ => Err(Error::new(fallo)),
74    }
75}
76
77/// Lee un hash de 32 bytes de la memoria lineal, con sus limites verificados.
78fn leer_hash(m: &[u8], ptr: u32, fallo: &'static str) -> Result<Hash, Error> {
79    let bytes = rango(m, ptr, 32, fallo)?;
80    let mut hash = [0u8; 32];
81    hash.copy_from_slice(bytes);
82    Ok(hash)
83}
84
85/// Inyecta en el enlazador la matriz de capacidades del modulo WASM. Todo lo
86/// que no se defina aqui le queda, al modulo, fisicamente fuera de alcance.
87///
88/// Devuelve `Err` si una capacidad no se pudo enlazar — un fallo del kernel,
89/// no de la app; aun asi se propaga como `Result` para no incendiar nada.
90pub(crate) fn enlazar_capacidades(
91    enlazador: &mut Linker<ContextoCapacidades>,
92) -> Result<(), Error> {
93    // --- CAPACIDAD 1 :: sys_render_frame(ptr, len) ---
94    // El modulo entrega (ptr, len) hacia su PROPIA memoria lineal; el kernel
95    // valida esos limites y, solo entonces, compone el fotograma DENTRO de la
96    // region asignada a la app.
97    enlazador.func_wrap(
98        "renaser",
99        "sys_render_frame",
100        |caller: Caller<'_, ContextoCapacidades>, ptr: u32, len: u32| -> Result<(), Error> {
101            let marco = caller.data().marco;
102            let nat_ancho = caller.data().natural_ancho;
103            let nat_alto = caller.data().natural_alto;
104
105            // El fotograma debe medir EXACTAMENTE el lienzo natural de la app.
106            // Un tamaño distinto delata a una app que pinta fuera de su lienzo:
107            // se aborta antes de tocar un byte.
108            let esperado = nat_ancho * nat_alto * 4;
109            if len as usize != esperado {
110                return Err(Error::new(
111                    "WASM :: sys_render_frame con un fotograma ajeno al lienzo natural",
112                ));
113            }
114
115            let memoria = obtener_memoria(&caller)?;
116            let datos: &[u8] = memoria.data(&caller);
117
118            // VALIDACION INFRANQUEABLE: si (ptr, len) se sale de la memoria
119            // lineal del modulo, se aborta la app —no el kernel—.
120            let fotograma = rango(
121                datos,
122                ptr,
123                len as usize,
124                "WASM :: sys_render_frame desbordo la memoria lineal del modulo",
125            )?;
126
127            // Limites verificados: el compositor centra el fotograma natural
128            // de la app dentro del marco que el teselado le asigno.
129            crate::volcar_marco_wasm(marco, nat_ancho, nat_alto, fotograma);
130            Ok(())
131        },
132    )?;
133
134    // --- CAPACIDAD 2 :: sys_get_scancode() -> u32 ---
135    // Expone, sin bloquear, el siguiente scancode del canal PROPIO de la app.
136    enlazador.func_wrap(
137        "renaser",
138        "sys_get_scancode",
139        |caller: Caller<'_, ContextoCapacidades>| -> u32 {
140            caller.data().canal.pop().unwrap_or(0) as u32
141        },
142    )?;
143
144    // --- CAPACIDAD 3 :: sys_object_put(datos, datos_len, hijos, hijos_cnt, salida) -> i32 ---
145    // Graba un objeto en el grafo. El modulo entrega, en su memoria lineal, la
146    // carga util y un arreglo de `hijos_cnt` hashes de 32 bytes (las aristas).
147    // El kernel escribe el hash resultante —la identidad del objeto— en
148    // `salida`. Devuelve 0 si el objeto se grabo (o ya existia), -1 si el
149    // almacenamiento fallo.
150    enlazador.func_wrap(
151        "renaser",
152        "sys_object_put",
153        |mut caller: Caller<'_, ContextoCapacidades>,
154         datos_ptr: u32,
155         datos_len: u32,
156         hijos_ptr: u32,
157         hijos_cnt: u32,
158         salida: u32|
159         -> Result<i32, Error> {
160            let memoria = obtener_memoria(&caller)?;
161
162            // --- Leer las entradas de la memoria lineal, con limites firmes. ---
163            let (datos, hijos) = {
164                let m = memoria.data(&caller);
165
166                let datos = rango(
167                    m,
168                    datos_ptr,
169                    datos_len as usize,
170                    "WASM :: sys_object_put desbordo la memoria lineal (datos)",
171                )?
172                .to_vec();
173
174                // El arreglo de hijos: `hijos_cnt` hashes contiguos de 32 bytes.
175                let bytes_hijos = (hijos_cnt as usize).checked_mul(32).ok_or_else(|| {
176                    Error::new("WASM :: sys_object_put con un conteo de hijos imposible")
177                })?;
178                let crudo = rango(
179                    m,
180                    hijos_ptr,
181                    bytes_hijos,
182                    "WASM :: sys_object_put desbordo la memoria lineal (hijos)",
183                )?;
184                let mut hijos: alloc::vec::Vec<Hash> =
185                    alloc::vec::Vec::with_capacity(hijos_cnt as usize);
186                for trozo in crudo.chunks_exact(32) {
187                    let mut h = [0u8; 32];
188                    h.copy_from_slice(trozo);
189                    hijos.push(h);
190                }
191
192                // Verificar que el hash de salida cabe ANTES de tocar el disco.
193                rango(
194                    m,
195                    salida,
196                    32,
197                    "WASM :: sys_object_put desbordo la memoria lineal (salida)",
198                )?;
199
200                (datos, hijos)
201            };
202
203            // --- Grabar. Un fallo del almacen NO es culpa de la app: -1. ---
204            match crate::almacen::almacenar(datos, hijos) {
205                Ok(hash) => {
206                    let m = memoria.data_mut(&mut caller);
207                    m[salida as usize..salida as usize + 32].copy_from_slice(&hash);
208                    Ok(0)
209                }
210                Err(_) => Ok(-1),
211            }
212        },
213    )?;
214
215    // --- CAPACIDAD 4 :: sys_object_datos(hash, salida, capacidad) -> i32 ---
216    // Copia la carga util del objeto `hash` en `salida`. Devuelve el numero de
217    // bytes copiados, o -1 si el objeto no existe, -2 si `capacidad` no basta,
218    // -3 si el almacenamiento fallo.
219    enlazador.func_wrap(
220        "renaser",
221        "sys_object_datos",
222        |mut caller: Caller<'_, ContextoCapacidades>,
223         hash_ptr: u32,
224         salida: u32,
225         capacidad: u32|
226         -> Result<i32, Error> {
227            let memoria = obtener_memoria(&caller)?;
228
229            let hash = {
230                let m = memoria.data(&caller);
231                leer_hash(
232                    m,
233                    hash_ptr,
234                    "WASM :: sys_object_datos desbordo la memoria lineal (hash)",
235                )?
236            };
237
238            let objeto = match crate::almacen::recuperar(&hash) {
239                Ok(Some(objeto)) => objeto,
240                Ok(None) => return Ok(-1),
241                Err(_) => return Ok(-3),
242            };
243            if objeto.datos.len() > capacidad as usize {
244                return Ok(-2);
245            }
246
247            // Verificar que el destino cabe, y solo entonces copiar.
248            {
249                let m = memoria.data(&caller);
250                rango(
251                    m,
252                    salida,
253                    objeto.datos.len(),
254                    "WASM :: sys_object_datos desbordo la memoria lineal (salida)",
255                )?;
256            }
257            let n = objeto.datos.len();
258            let m = memoria.data_mut(&mut caller);
259            m[salida as usize..salida as usize + n].copy_from_slice(&objeto.datos);
260            Ok(n as i32)
261        },
262    )?;
263
264    // --- CAPACIDAD 5 :: sys_object_hijo(hash, indice, salida) -> i32 ---
265    // Recorre las aristas del DAG. Devuelve el NUMERO de hijos del objeto
266    // `hash`; si `indice` es valido, ademas escribe el hash de ese hijo en
267    // `salida`. Devuelve -1 si el objeto no existe, -3 si el almacen fallo.
268    enlazador.func_wrap(
269        "renaser",
270        "sys_object_hijo",
271        |mut caller: Caller<'_, ContextoCapacidades>,
272         hash_ptr: u32,
273         indice: u32,
274         salida: u32|
275         -> Result<i32, Error> {
276            let memoria = obtener_memoria(&caller)?;
277
278            let hash = {
279                let m = memoria.data(&caller);
280                leer_hash(
281                    m,
282                    hash_ptr,
283                    "WASM :: sys_object_hijo desbordo la memoria lineal (hash)",
284                )?
285            };
286
287            let objeto = match crate::almacen::recuperar(&hash) {
288                Ok(Some(objeto)) => objeto,
289                Ok(None) => return Ok(-1),
290                Err(_) => return Ok(-3),
291            };
292            let total = objeto.hijos.len();
293
294            // Si el indice apunta a un hijo real, entregar su hash.
295            if let Some(hijo) = objeto.hijos.get(indice as usize) {
296                {
297                    let m = memoria.data(&caller);
298                    rango(
299                        m,
300                        salida,
301                        32,
302                        "WASM :: sys_object_hijo desbordo la memoria lineal (salida)",
303                    )?;
304                }
305                let m = memoria.data_mut(&mut caller);
306                m[salida as usize..salida as usize + 32].copy_from_slice(hijo);
307            }
308            Ok(total as i32)
309        },
310    )?;
311
312    // --- CAPACIDAD 6 :: sys_object_raiz(salida) -> i32 ---
313    // Escribe en `salida` el hash de la raiz del grafo. Devuelve 1 si hay
314    // raiz, 0 si el grafo aun no tiene ninguna.
315    enlazador.func_wrap(
316        "renaser",
317        "sys_object_raiz",
318        |mut caller: Caller<'_, ContextoCapacidades>, salida: u32| -> Result<i32, Error> {
319            let memoria = obtener_memoria(&caller)?;
320            match crate::almacen::raiz() {
321                Some(hash) => {
322                    {
323                        let m = memoria.data(&caller);
324                        rango(
325                            m,
326                            salida,
327                            32,
328                            "WASM :: sys_object_raiz desbordo la memoria lineal (salida)",
329                        )?;
330                    }
331                    let m = memoria.data_mut(&mut caller);
332                    m[salida as usize..salida as usize + 32].copy_from_slice(&hash);
333                    Ok(1)
334                }
335                None => Ok(0),
336            }
337        },
338    )?;
339
340    // --- CAPACIDAD 7 :: sys_object_fijar_raiz(hash) -> i32 ---
341    // Corona el objeto `hash` como raiz del grafo. Devuelve 0 si se logro, -3
342    // si el almacenamiento fallo.
343    enlazador.func_wrap(
344        "renaser",
345        "sys_object_fijar_raiz",
346        |caller: Caller<'_, ContextoCapacidades>, hash_ptr: u32| -> Result<i32, Error> {
347            let memoria = obtener_memoria(&caller)?;
348            let hash = {
349                let m = memoria.data(&caller);
350                leer_hash(
351                    m,
352                    hash_ptr,
353                    "WASM :: sys_object_fijar_raiz desbordo la memoria lineal (hash)",
354                )?
355            };
356            match crate::almacen::fijar_raiz(hash) {
357                Ok(()) => Ok(0),
358                Err(_) => Ok(-3),
359            }
360        },
361    )?;
362
363    // --- CAPACIDAD 8 :: sys_estado_cargar(salida, capacidad) -> i32 ---
364    // Copia el estado persistido de ESTA app —el objeto que su `EntradaApp` del
365    // manifiesto tiene anclado— en `salida`. Devuelve el numero de bytes
366    // copiados, 0 si la app no tiene estado previo, -1 si el objeto anclado no
367    // existe, -2 si `capacidad` no basta, -3 si el almacenamiento fallo.
368    enlazador.func_wrap(
369        "renaser",
370        "sys_estado_cargar",
371        |mut caller: Caller<'_, ContextoCapacidades>,
372         salida: u32,
373         capacidad: u32|
374         -> Result<i32, Error> {
375            let indice = caller.data().indice_app;
376            // El hash del estado de esta app, segun el manifiesto vivo.
377            let hash = match crate::manifiesto::estado_de(indice) {
378                Some(hash) => hash,
379                None => return Ok(0), // Sin estado previo: nada que cargar.
380            };
381            let objeto = match crate::almacen::recuperar(&hash) {
382                Ok(Some(objeto)) => objeto,
383                Ok(None) => return Ok(-1),
384                Err(_) => return Ok(-3),
385            };
386            if objeto.datos.len() > capacidad as usize {
387                return Ok(-2);
388            }
389
390            let memoria = obtener_memoria(&caller)?;
391            // Verificar que el destino cabe, y solo entonces copiar.
392            {
393                let m = memoria.data(&caller);
394                rango(
395                    m,
396                    salida,
397                    objeto.datos.len(),
398                    "WASM :: sys_estado_cargar desbordo la memoria lineal (salida)",
399                )?;
400            }
401            let n = objeto.datos.len();
402            let m = memoria.data_mut(&mut caller);
403            m[salida as usize..salida as usize + n].copy_from_slice(&objeto.datos);
404            Ok(n as i32)
405        },
406    )?;
407
408    // --- CAPACIDAD 9 :: sys_estado_guardar(datos, datos_len) -> i32 ---
409    // Graba `datos` como el estado persistido de ESTA app: el kernel lo
410    // almacena como un objeto del grafo y ancla su hash en la `EntradaApp` de
411    // la app, re-grabando y re-anclando el manifiesto. El estado sobrevivira al
412    // reinicio. Devuelve 0 si se logro, -3 si el almacenamiento fallo.
413    enlazador.func_wrap(
414        "renaser",
415        "sys_estado_guardar",
416        |caller: Caller<'_, ContextoCapacidades>,
417         datos_ptr: u32,
418         datos_len: u32|
419         -> Result<i32, Error> {
420            let indice = caller.data().indice_app;
421            let memoria = obtener_memoria(&caller)?;
422            // Leer el estado de la memoria lineal, con limites firmes.
423            let datos = {
424                let m = memoria.data(&caller);
425                rango(
426                    m,
427                    datos_ptr,
428                    datos_len as usize,
429                    "WASM :: sys_estado_guardar desbordo la memoria lineal (datos)",
430                )?
431                .to_vec()
432            };
433            // Grabar el objeto de estado. Un fallo del almacen NO es culpa de
434            // la app: se le devuelve -3, y ella decide que hacer.
435            let hash = match crate::almacen::almacenar(datos, alloc::vec::Vec::new()) {
436                Ok(hash) => hash,
437                Err(_) => return Ok(-3),
438            };
439            // Anclarlo: muta el manifiesto vivo, lo re-graba y lo re-ancla.
440            match crate::manifiesto::fijar_estado(indice, hash) {
441                Ok(()) => Ok(0),
442                Err(_) => Ok(-3),
443            }
444        },
445    )?;
446
447    Ok(())
448}