urMQTT en un VPS ARM64 real: sin pantalla, sin generador de números aleatorios por hardware y con un handshake demasiado lento
Lo que pasó después del texto sobre v0.3.0: la primera ejecución en un servidor aarch64 real con KVM (instalación en su disco, un corte de corriente, cifras del disco), una entrada de diagnóstico que informa por la red porque la consola es invisible, lo que encontró sobre el generador de números aleatorios, un ensamblador P-256 para ARM64 que hizo los handshakes completos unas 1,5 veces más frecuentes, y los errores que cometimos por el camino.

El texto anterior, urMQTT v0.3.0, terminaba con cuatro puntos bajo "Lo que sigue": una decisión sobre el broker de demostración público (D1), fuzzing de los analizadores de PCI y virtio y una prueba para las tablas de páginas bloqueadas, un diálogo para la clave en la página del constructor, y bare metal. Ninguno de los cuatro se hizo en esta ronda, y lo decimos primero. Lo que hicimos en cambio es lo que faltaba desde siempre: ejecutamos urMQTT en un servidor ARM64 real con KVM, y casi todo lo que sigue sale de esa máquina. El primer punto de la lista antigua, el CI, se puso en orden (el texto anterior y las notas de la versión que lo daban por rojo se corrigieron)
La advertencia previa sigue siendo la misma: el proyecto está en fase pre-alfa, las cifras miden código, pruebas y experimentos concretos en una máquina, no madurez en producción. Donde una cifra no se midió en la máquina de destino, lo decimos.
Cómo se vio a lo largo del tiempo
Desde el último texto (3 de noviembre, 02:43) hasta hoy (4 de noviembre, 03:18) main tiene 29 commits nuevos, 244 en total. Como antes, el proyecto se construye con Claude Code (los commits llevan Co-Authored-By: Claude). Las cifras se calculan con el método de los dos últimos textos (todos los archivos .rs fuera de vendor/ y fuzz/, funciones #[test], títulos ## ADR- en docs/DECISIONS.md); el método da las mismas cifras de entonces sobre el commit antiguo.
| Momento | Commit | Hora | Líneas de Rust | Pruebas | ADR | Commits |
|---|---|---|---|---|---|---|
| último texto | f1a7fbc | 3 de noviembre, 02:43 | 115 148 | 962 | 133 | 215 |
| hoy | efddf6e | 4 de noviembre, 03:18 | 117 084 | 978 | 134 | 244 |
Crecimiento: +1936 líneas, +16 pruebas, +1 ADR. De nuevo poco código nuevo; el propio ensamblador P-256 (1509 líneas) está incorporado como código de terceros (vendored) y no cuenta en la cifra de líneas.
Una segunda máquina real
Hasta ahora el proyecto tenía un solo servidor real: un VPS de Contabo con un AMD EPYC (x86_64, KVM), usado para la comparación de rendimiento con Mosquitto. ARM64 solo se había ejecutado en QEMU, con aceleración por hardware en un Mac y sin ella en el CI. La máquina nueva es un netcup VPS 500 ARM G12.5: un núcleo Neoverse N1 a unos 3 GHz, 2 vCPU, 3,9 GiB de RAM, un disco virtio de 64 GiB, QEMU/KVM con firmware UEFI, una tarjeta de red virtio. Todo se manejó a través de la API REST del proveedor con un cliente de línea de comandos: subimos la ISO, la conectamos como CD virtual, cambiamos el dispositivo de arranque, apagamos y encendemos.
Lo que hicimos en ella, en orden:
- Primero una instantánea del sistema original. Con UEFI no es posible una instantánea en línea, así que se apagó el servidor y se hizo una instantánea sin conexión.
- Instalación. El instalador de la ISO escribió la imagen en el disco (con la cuenta atrás de 30 segundos incluida, unos 40 segundos en total) y apagó la máquina por sí mismo, como está diseñado.
- El arranque desde el disco instalado tardó unos 35 segundos hasta un handshake TLS.
- Un mensaje retenido sobrevivió a un corte de corriente brusco. Lo publicamos, cortamos la alimentación a través de la API, arrancamos de nuevo y lo leímos.
- El disco instalado, medido por la entrada de diagnóstico (solo lecturas; la prueba de escritura se omitió porque el diseño instalado no deja espacio sin asignar, y es lo previsto): lectura secuencial 332 MiB/s, lecturas aleatorias de 4 KiB 22 147 por segundo con p50 10,4 µs, p90 28,3 µs, p99 671 µs, máximo 9,9 ms (2000 lecturas).
Cinco puntos no son una batería de pruebas: es una máquina, una sola ejecución de cada uno, y la ruta de instalación se ejerció una vez sobre el disco real. Lo que sí muestra es que el controlador virtio-blk del propio kernel, el instalador y el arranque funcionan en un invitado de nube real y no solo en un emulador de laboratorio.
Sin pantalla, así que el diagnóstico va por la red
La entrada de diagnóstico de v0.3.0 dibuja sus cifras en el framebuffer. En este servidor la consola del proveedor dice "Display output is not active": la máquina virtual tiene una tarjeta gráfica virtio para la que no tenemos controlador, y el puerto serie no está expuesto. Las cifras existían y nadie podía leerlas.
La solución es una palabra de arranque, diag-serve: la máquina ejecuta las mismas mediciones, luego arranca como un broker live normal y publica el informe como un único documento JSON en el mensaje retenido $SYS/diag/report, que solo puede leer un certificado de operador. Una pequeña herramienta, urmqtt-diag-png, dibuja el panel a partir de ese JSON con el mismo código que usa el kernel en la pantalla (el mismo diseño y la misma fuente de 8x8), de modo que la imagen es la pantalla que la máquina habría mostrado:

Por el camino se añadieron a la pantalla dos cosas que echábamos de menos: el reloj de la CPU (el contador de ciclos de la PMU frente a la base de tiempo: de 2,95 a 2,98 GHz aquí; donde solo se puede leer un temporizador de frecuencia fija, dice "no medible" en lugar de imprimir la frecuencia del temporizador como si fuera la de la CPU) y un panel para el generador de números aleatorios.
De qué está hecho el generador de números aleatorios
El panel indica de dónde se inicializó el generador y prueba bytes nuevos de cada fuente de hardware. En esta máquina dice:
- virtio-rng: ningún dispositivo.
RNDR(la instrucción propia de la CPU): no disponible en esta CPU. - El generador de números aleatorios EFI del cargador: 8 de 8 valores distintos de cero.
- El recurso alternativo de jitter de tiempos de la CPU (ADR-038): 1024 muestras de 64 pasos a través del contador de ciclos de la PMU, 3 bits estimados por muestra, 768 bits acreditados (una cuarta parte de la estimación, por diseño), pruebas de salud superadas.
- La salida del generador, una prueba de sensatez sobre 4096 bytes: 49,6 % de unos, la racha más larga de 16 bits. Es una comprobación de que nada está atascado o sesgado, no una certificación estadística.
Así que el generador de este servidor se apoya en el recurso alternativo de jitter y en lo que entregó el firmware. Es exactamente lo que describe ADR-038, y es la inicialización más débil que acepta el kernel. No la llamamos rota: los bits acreditados están por encima del umbral que exige el kernel (256), y el estimador es conservador. Tampoco podemos demostrar que sea tan buena como una fuente de hardware. Si una imagen de producción debería negarse a arrancar sin una fuente de hardware, salvo que el operador lo acepte expresamente, es una pregunta abierta para el propietario del proyecto (está en la hoja de ruta, más abajo). En la práctica es también una pregunta para el proveedor: un dispositivo de números aleatorios por hardware para la máquina virtual la resolvería.
Un handshake demasiado lento
La entrada de diagnóstico mostró también lo que cuesta en el servidor un handshake completo con TLS mutuo: 1,08 ms. Casi todo es aritmética de clave pública: dos verificaciones ECDSA P-256 (el certificado del cliente y la prueba del cliente), unos 508 µs cada una, X25519 176 µs, una firma ECDSA 141 µs. En x86_64 lo habíamos resuelto antes (ADR-130, el ensamblador de OpenSSL/Intel: P-256 de tres a cuatro veces más rápido); en ARM64 seguía ejecutándose el código portable de RustCrypto.
Hicimos lo mismo para ARM64 (ADR-255). El ensamblador es el nistz256 ARMv8 de OpenSSL (Apache-2.0), tomado de ring 0.17.14 e incorporado con su procedencia y cada cambio mecánico anotado. Lo que importó en los detalles:
- Solo enteros. El ensamblador no usa registros NEON. Las dos funciones de selección de tabla que el original sí implementa con NEON las eliminamos y escribimos en Rust recorridos enmascarados en tiempo constante (cuestan como máximo 1,4 µs de una firma de 12 µs).
- Protección de saltos. El kernel se compila con BTI y autenticación de punteros. Cada uno de los 8 puntos de entrada exportados tiene un landing pad, comprobado en el desensamblado del kernel, y los seis que guardan el link register lo firman.
- Pruebas, porque esto es criptografía en la base en la que confiamos. Toda la batería de x86_64 ahora se ejecuta también en aarch64 Linux: aritmética del campo y del escalar frente a una referencia de libro de texto, toda la tabla del generador (2368 entradas) frente a RustCrypto, firmas idénticas bit a bit a las de RustCrypto, veredictos de verificación, ECDH con entradas fuera de la curva y cero, y las pruebas de respuesta conocida que se ejecutan en cada arranque por las rutas de producción. En una máquina virtual de cuatro núcleos bajo una tormenta de interrupciones se ejecutaron 59 526 rondas de respuesta conocida con 0 discrepancias.
- Una prueba de temporización con un control, y una señal que era nuestra propia prueba. Una multiplicación deliberadamente con fugas obtiene |t| de 482 a 1755, así que la prueba puede ver una fuga a esta escala. En un núcleo de Apple en un contenedor, ECDH obtuvo de 0,2 a 2,1, la firma de 0,5 a 1,9, la generación de claves de 0,8 a 5,5. En el núcleo de servidor arm64 del runner de CI, la firma ECDSA obtuvo |t| = 8,6, 9,1 y 8,3 en tres intentos, por encima de nuestro límite de 7, mientras que ECDH (3,9) y la generación de claves (1,9) pasaron en la misma máquina. Un resultado que se repite no es ruido, así que lo anotamos como hallazgo abierto (A255-W1) y, durante unas horas, elevamos a 12 el límite de ese solo caso para que la compilación pasara. Después encontramos la causa, y era el banco de pruebas: el caso firmaba un único mensaje fijo, de modo que la clave dispersa producía la misma firma cada vez y las claves aleatorias una distinta cada vez, y la codificación DER de una firma repetida es más rápida (|t| de 49 a 57 solo por la codificación). Con un mensaje aleatorio por medición en ambas clases, el caso obtiene 1,4 y 1,8 en el runner (una máquina virtual Neoverse N2), la generación del nonce sola 2,7 y 1,1, y el límite ha vuelto a 7. Una cosa sigue abierta (A255-W2): el indicador del procesador que hace que el tiempo del multiplicador sea independiente de los datos en los núcleos Arm (DIT) no se activa en ningún sitio, ni por el kernel ni alrededor de las llamadas del lado del host. Ninguna medición muestra una diferencia, pero la máquina virtual del runner no expone la función, así que la prueba tampoco podía mostrarla.
Lo que ganó
Primero el microbenchmark en la máquina donde lo desarrollamos, un Apple M4 Pro (no es la CPU de destino, así que las proporciones en el servidor pueden diferir), con el mismo código que llama el handshake, antes y después:
| Operación | antes (µs) | después (µs) | proporción |
|---|---|---|---|
| generación de claves | 30,8 | 7,0 | 4,4x |
| ECDH P-256 | 135,2 | 25,0 | 5,4x |
| firma ECDSA | 34,7 | 11,6 | 3,0x |
| verificación ECDSA (clave desconocida) | 147,4 | 29,1 | 5,1x |
| verificación de CA (emisor registrado) | 54,2 | 9,6 | 5,6x |
| X25519 (código sin cambios) | 20,4 | 20,1 | 1,0x |
| estimación del handshake (suma de lo anterior) | 292 | 104 | 2,8x |
Después lo real: el mismo generador de carga, el mismo método que en el texto anterior sobre rendimiento (handshake TLS 1.3 completo con certificado de cliente, sin reanudación, MQTT CONNECT, el servidor cierra primero), ejecutado desde una máquina a 9 ms del servidor, contra el mismo VPS de netcup, antes y después del cambio. La imagen del benchmark tiene los límites del handshake relajados, como en la comparación de x86_64; tres repeticiones, la cifra es la mediana.
| Hilos | antes (conex/s) | después (conex/s) | proporción | errores |
|---|---|---|---|---|
| 32 | 848 (408-855) | 1317 (957-1317) | 1,55x | 0 |
| 64 | 827 (815-839) | 1398 (1352-1416) | 1,69x | 0 |
| 128 | 778 (714-788) | 1316 (1313-1383) | 1,69x | 0 |
| Parte TLS de una conexión, p50, 32 hilos | 18,5 ms | 13 ms |
Los handshakes completos por segundo subieron unas 1,55 a 1,7 veces, no las tres veces que daría la aritmética sola. El motivo es sencillo y vale la pena decirlo: aceleramos P-256, y X25519 (176 µs), la pila TCP/IP, la capa de registros y MQTT no cambian, así que la fracción que queda gana peso (ley de Amdahl). El siguiente candidato es X25519.
Como orientación junto al servidor x86_64 del texto anterior. Estas cifras no son comparables como una carrera: CPU distintas, un número distinto de núcleos (4 frente a 2), una distancia distinta al generador (15 ms frente a 9 ms) y, en ambos casos, un anfitrión de carga compartido que durante las ejecuciones perdió del 5 al 60 % de su propia CPU. La columna comparable es el coste de CPU del servidor por handshake:
| Contabo, x86_64 EPYC (asm) | netcup, ARM64 N1 antes | netcup, ARM64 N1 después | |
|---|---|---|---|
| CPU del servidor por handshake | 0,26 ms (la consola del broker) | 1,08 ms (diagnóstico) | 0,55 ms (diagnóstico, una ejecución, véase abajo) |
| handshakes/s, 32 hilos | 169 (bench) | 848 | 1317 |
| handshakes/s, 64 hilos | 278 (bench) | 827 | 1398 |
| handshakes/s, 128 hilos | 413 (bench) | 778 | 1316 |
El mismo día la entrada de diagnóstico se ejecutó otra vez en esta máquina, con el ensamblador (en una compilación que también contiene el controlador gráfico descrito en la actualización del final): el coste de CPU del servidor de un handshake con TLS mutuo fue de 553 µs frente a 1,08 ms antes, 1,95 veces menos. La verificación ECDSA tardó 192 µs (508 µs antes), la firma 78 µs (141 µs) y X25519 209 µs (176 µs; ese código no ha cambiado, y el reloj de la CPU de esta ejecución era de 2,58 GHz frente a de 2,95 a 2,98 GHz en las anteriores, por lo que las cifras sueltas en esta máquina compartida varían en decenas por ciento). Es una sola ejecución, en un anfitrión compartido. Que la tasa de handshakes subiera de 1,55 a 1,7 veces mientras el coste propio del servidor por handshake se dividía entre 1,95 encaja: el resto de una conexión es la pila, el generador y la red. La variación entre ejecuciones se ve en los archivos en bruto: cada serie de tres tiene un valor atípico (408 antes y 957 después con 32 hilos), cuando el anfitrión de carga perdió gran parte de su CPU en favor de otras máquinas virtuales. Con 128 hilos, en ambas máquinas es el generador, no el servidor, el que está en su límite.
El resto de la batería en esta máquina
La misma batería que para el servidor x86_64 (rendimiento, latencia, fan-out, conexiones inactivas) se ejecutó completa contra la nueva compilación. Para estas filas no hay un "antes": la ejecución se detuvo para construir el ensamblador, así que son el estado posterior al cambio, y el ensamblador no toca estas rutas salvo por los handshakes que abren las conexiones. Un hecho explica la mayor parte de lo que sigue: esta máquina virtual tiene dos vCPU. El núcleo 0 atiende la tarjeta de red, el reparto y todo el trabajo de MQTT y TLS, el núcleo 1 es el de la base de datos y estuvo ocupado entre el 2 y el 3 % todo el tiempo. El servidor x86_64 tenía cuatro vCPU y dos núcleos de trabajo. Todo lo que sigue es, por tanto, rendimiento del broker con un solo núcleo.
| Conexiones inactivas | 5000 | 10 000 | 20 000 |
|---|---|---|---|
| conectadas | 5000 de 5000 en 4,3 s | 10 000 en 9,3 s | 20 000 en 21,2 s |
| crecimiento del heap del kernel por conexión | 0,34 KB | 0,41 KB | 0,43 KB |
| errores | 0 | 0 | 0 |
(De 0,50 a 0,66 KB por conexión en el servidor x86_64 en el commit de H7. La cifra del heap es $SYS/broker/memory/used menos su valor antes de la primera conexión.)
| Latencia a un ritmo modesto (8 pares x 100 mensajes/s, 64 B) | p50 | p99 |
|---|---|---|
| QoS 0 | 4,0 ms | 6,9 ms |
| QoS 1 | 4,1 ms | 7,0 ms |
| Fan-out (1 publicador, 64 B) | entregados por segundo | p50 | p99 |
|---|---|---|---|
| 100 suscriptores, QoS 0, 50 msg/s | 5000 (todos) | 5,9 ms | 8,6 ms |
| 100 suscriptores, QoS 1, 50 msg/s | 5000 (todos) | 6,3 ms | 10,0 ms |
| 1000 suscriptores, QoS 0, 10 msg/s | 8805 de 10 000 | 207 ms | 5,4 s |
| Flood, 16 pares, publicadores a la máxima velocidad que admite el broker | publicados/s | entregados/s | p50 / p99 | conexiones perdidas |
|---|---|---|---|---|
| QoS 0, 64 B | 231 317 | 85 140 | 1,36 s / 2,8 s | 0 |
| QoS 0, 1 KiB | 62 110 | 25 506 | 336 ms / 873 ms | 0 |
| QoS 1, 64 B | 107 349 | 55 965 | 21 ms / 29 ms | 0 |
| QoS 1, 1 KiB | 51 369 | 25 054 | 47 ms / 60 ms | 0 |
| QoS 1, 64 B, 64 pares | 130 462 | 69 411 | 69 ms / 87 ms | 0 |
Cómo leerlo. La latencia a un ritmo modesto es la aportación del broker más un viaje hasta el generador: el generador está más cerca de este servidor que el de la comparación de x86_64, así que las dos columnas de latencia no son comparables. El flood de QoS 0 muestra el cuadro habitual de un único núcleo sobrecargado: el broker acepta 231 000 mensajes por segundo y entrega 85 000 (el resto lo descartan las reglas de contrapresión, que para eso están) con latencias de segundos, y no se perdió ninguna conexión; en el servidor x86_64 el mismo tipo de flood costaba varias conexiones por ejecución al watchdog de 20 ms (desde entonces ADR-221 cambió cómo se cuenta ese presupuesto). La fila de 1000 suscriptores tiene el mismo carácter que antes, colas de segundos, en parte del propio generador. Las cifras en el commit de H7 en el servidor x86_64 están en el texto anterior; no las ponemos en la misma tabla que estas, porque el código entre las dos mediciones cambió en la ruta de datos (de ADR-221 a ADR-224) y las máquinas difieren en todo.
Errores que encontramos nosotros mismos
- La imagen decía "no se encontraron discos" y el disco estaba en el informe. La primera versión de
urmqtt-diag-pngno leía en absoluto el campodisks. El propietario del proyecto miró la imagen, dijo que parecía incorrecta, y tenía razón. Corregido, y ahora una prueba vuelve a leer un disco del JSON. - Dimos el instalador por terminado demasiado pronto. El estado del servidor pasó a "apagado" dos veces: una al final del ciclo de alimentación y otra cuando terminó el instalador. Nuestro script tomó la primera por la segunda, desconectó la ISO y arrancó el sistema antiguo, que informó del número de versión antiguo y nos hizo buscar un fallo en el instalador que no existía. Ahora el script espera primero un arranque nuevo.
- Se perdió el historial de avance. Un análisis para la hoja de ruta descubrió que el commit del hito S6 había sustituido
docs/PROGRESS.md(1763 líneas, hitos de M0 a S5) por su propia entrada de 26 líneas. Pasó desapercibido durante días, aunque el README llama a este archivo "la fuente de verdad". Se restauró desde el historial. - El CI falló tras el ensamblador, en dos sitios. Tres pruebas de humo tenían escrito dentro el tamaño de la imagen de arranque ("10 MiB"), que creció a 11 MiB con el kernel más grande; ya no suponen el tamaño. El otro fallo fue la prueba de temporización descrita arriba: la primera vez parecía ruido en un runner compartido, así que hicimos que la prueba se repitiera tres veces; falló en los tres intentos, y así supimos que era una señal, y la señal resultó ser nuestra propia prueba (firmaba el mismo mensaje cada vez). Elevamos ese único límite durante unas horas, lo dijimos abiertamente y lo bajamos de nuevo cuando se corrigió la causa.
- La documentación cuenta tres palabras de arranque y el código tiene cuatro (
diag-serve), y la tabla de hitos del README sigue mostrando como planeado el trabajo de la última semana. Ambas cosas están en la hoja de ruta como puntos por corregir.
Una hoja de ruta, con preguntas
Por primera vez hay una hoja de ruta: unos 40 puntos en tres fases (v0.4 endurecimiento y operación; protocolo y red; scale-out, más nubes y bare metal), cada uno con un motivo tomado de un documento, un esfuerzo, un riesgo y una forma de verificarlo, 16 preguntas que solo puede responder el propietario del proyecto, y una lista de lo que queda fuera del alcance. Algunas cosas las dice sin rodeos: solo se admite MQTT 3.1.1, una publicación QoS 2 cierra la conexión, no hay IPv6 (el proveedor entrega un /64), ni WebSocket ni conexiones salientes desde el broker, y no hay controladores para los dispositivos de red y de almacenamiento de las nubes grandes.
Lo que todavía no está demostrado
- El coste de CPU por handshake con el ensamblador se midió en el núcleo Neoverse una sola vez (553 µs frente a 1,08 ms antes, a un reloj más bajo, en un anfitrión compartido); el factor de 1,55 a 1,7 sale de la tasa de handshakes completos, y el de 3 a 5 de un núcleo de Apple. El lado x86_64 de la refactorización (el código movido a su propio archivo) se comprobó en tipos y se compiló, pero sus pruebas unitarias no se ejecutaron y el kernel x86_64 no se arrancó después: las suites x86_64 no están en el CI, se ejecutan a mano en el VPS de Contabo, y eso todavía no se ha hecho para este cambio.
- El tiempo constante: para la generación de claves P-256, ECDH y la firma (la clave y el nonce), la prueba estadística no encontró dependencia de un secreto disperso. Se ejecutó en un núcleo de Apple y en el runner de CI (una máquina virtual Neoverse N2), no en el núcleo Neoverse N1 de netcup, y nunca con el indicador DIT activado en un núcleo de servidor (A255-W2, abierto: el kernel no lo activa). No se ejecutó para X25519 ni para Ed25519. Una prueba de caja negra solo acota lo que puede resolver con su tamaño de muestra.
- Un servidor, un proveedor, una instalación. Varios resultados (instalación, un corte de corriente) son una sola ejecución. Aparte de los handshakes, las filas de rendimiento no tienen un "antes", y una máquina de dos vCPU da al broker un solo núcleo de datos.
- El generador de números aleatorios de este servidor se inicializa a partir del jitter y del firmware, como se describió arriba.
- La clave de firma sigue siendo una clave de prueba, las imágenes de la versión son compilaciones de desarrollo, y las traducciones de este texto las escribió una IA y no las han revisado hablantes nativos.
- Sin auditoría independiente, sin bare metal, sin IOMMU.
Lo que sigue
- X25519 en ensamblador para ARM64 (son 176 µs del handshake restante), y repetir la medición por handshake en el núcleo Neoverse más de una vez.
- Una decisión sobre el valor por defecto del generador de números aleatorios: negarse a arrancar sin una fuente de hardware salvo que el operador diga lo contrario, o mantener el recurso alternativo de jitter y decirlo claramente.
- La lista antigua: una decisión sobre D1, fuzzing de los analizadores de PCI y virtio, el diálogo para la clave en la página del constructor.
- De la hoja de ruta, para la próxima versión: una clave de firma real y una PKI de producción, MQTT 5.0 y QoS 2.
El código está en github.com/zeljan-alduk/urMQTT; docs/PROGRESS.md sigue siendo la fuente de verdad sobre lo que funciona, docs/PERFORMANCE.md sobre lo que se midió (la sección 10 tiene las cifras del ensamblador), docs/AUDIT.md sobre lo que no está demostrado. Pre-alfa, como antes; los informes de errores y los informes de seguridad que sigan las instrucciones de SECURITY.md son bienvenidos.
Actualización, 4 de noviembre, más tarde el mismo día. Cuatro cosas de las horas posteriores a la publicación de este texto.
- La cifra que faltaba. La entrada de diagnóstico se ejecutó otra vez en el servidor de netcup con el ensamblador y dio el número que falta en la primera tabla: 553 µs de CPU del servidor por handshake con TLS mutuo frente a 1,08 ms antes (detalles bajo esa tabla).
- La señal de la firma era nuestra prueba. La causa de la señal de temporización descrita arriba se encontró y se corrigió en el banco de pruebas (firmaba el mismo mensaje cada vez); el límite ha vuelto a 7. Lo que sigue abierto es que el indicador DIT no se activa en ningún sitio (A255-W2).
-
El problema de la pantalla está resuelto, y está en
main. Escribimos un controlador para la tarjeta gráfica virtio (solo 2D, ADR-256), activado por una clave de configuración que va integrada en la imagen (console.virtio_gpu,offpor defecto,autoyonrequieren el formato de configuración 5). Conon, la pestaña Screen del proveedor muestra el panel de diagnóstico en vivo en el servidor de netcup, la misma imagen de arriba, incluida la última pantalla con "press any key". Su objetivo de fuzzing encontró un error real en la primera versión: una respuesta de información de pantalla cuatro bytes más corta que la especificación se aceptaba como válida (A256-W1); un dispositivo real siempre envía la respuesta entera, así que el servidor no estaba afectado, y el analizador ahora rechaza la corta. Lo que no hemos verificado: que la pantalla oscura viniera realmente del firmware que reinicia la tarjeta al ceder el control al kernel (probable, no confirmado), la interacción con Secure Boot y x86_64 en una máquina real (solo se ejecutó bajo emulación). Un límite conocido (A256-W2): el ajusteautono puede distinguir un framebuffer emulado vivo junto a una tarjeta virtio de uno muerto, así que en esa única configuración toma la tarjeta virtio. El controlador añade al kernel un analizador de las respuestas del hipervisor, por eso está desactivado salvo que la configuración lo pida. -
La versión. Todo esto está en v0.4.0, publicada la misma noche como versión preliminar (versión en GitHub). La página del constructor de este sitio la ofrece primero; requiere el formato de configuración 6, y las plantillas anteriores (v0.2.0 a v0.3.0) rechazan las claves nuevas con un mensaje que dice que se use una versión más reciente. Los límites de v0.3.0 se mantienen: la clave de firma sigue siendo una clave de prueba, y las imágenes son compilaciones de desarrollo con certificados de prueba. El CI sobre el commit de la versión solo estuvo en verde después de volver a ejecutar un trabajo (una prueba de cliente dentro de un escenario de prueba de humo agotó el tiempo una vez y pasó la segunda). Lo que comprobamos en los archivos publicados: la firma minisign, las sumas de verificación, las atestaciones de compilación del kernel y del SBOM, y la plantilla con las claves nuevas (v0.4.0 las acepta, v0.3.0 las rechaza). El controlador de los nuevos dispositivos virtio (consola, balloon, input, vsock) está en la versión, pero desactivado por defecto y no se ha ejecutado en un servidor real.
Comentarios
Normas de los comentarios