Timeline de STA Structured Text Amplification

Sunday, July 12, 2026
⏳ Timeline de la investigación STA
De 2022 al 30 de julio de 2026 · 31 vectores · 15 archivos · 13 años de código vulnerable
Actualizado: 12 de julio de 2026
📌 Referencias: Los tres artículos de la saga
📖 Estos tres posts forman la base de la investigación que culminará en el whitepaper del 30 de julio de 2026.

📌 Esta línea temporal resume los hitos clave de la investigación — desde los primeros hallazgos en 2022 hasta la publicación del whitepaper el 30 de julio de 2026. En el camino, 31 vectores, 15 archivos de AOSP, 9 empresas, 12 navegadores y una recompensa de $250 que se convirtió en símbolo de la contradicción de Google.

2022 · El origen

2022 28 ago
🔍 Descubrimiento original
Lostmon encuentra TransactionTooLargeException y DeadSystemException en Firefox Focus, Firefox Nightly mediante clipboard, share y open-in-app.
2022 12 oct
📝 Publicación del advisory
Se publica el primer advisory público: "Mozilla Firefox Focus and Nightly for Android Remote Crash DoS" en lostmon.blogspot.com.
2022 Nov
📋 Reporte a Chromium · Issue 40879254
Se abre Chromium Issue 40879254. Se eliminan las etiquetas de seguridad (comment #9). Se aplica un parche UX parcial (comment #22). El issue sigue abierto y asignado.
2022 Nov
📋 Reporte a Mozilla · Bugzilla #1802594
Se abre Mozilla Bugzilla #1802594 (Severidad S3). El issue sigue en estado NEW (sin resolver) en 2026.

2026 · La investigación formal

2026 20 ene
📋 Reporte al Chrome VRP · Issue 477202817
Se envía el reporte al Chrome VRP con 27 vectores. Google lo cierra el mismo día como "Won't Fix (Intended Behavior)" argumentando que el DoS "no es un bug de seguridad en Chrome".
2026 20 ene
📋 Reporte al Android VRP y MSRC
Se envía el reporte formal a Google Android VRP (A-477279924) y a Microsoft MSRC.
2026 Ene
💰 Recompensa de Google · $250
Google paga 250 dólares por la primera parte de la investigación (caso Binder/IPC).
2026 Feb
📄 Whitepaper v4
Se distribuye el whitepaper v4 con 27 vectores. Se añaden STA-015-DL (CVSS 8.6) y STA-015b.
2026 Mar
🔍 Reporte a Meta
Se notifica a Meta (WhatsApp/Threads) a través de su bug platform. No hay respuesta.

Mayo–Junio 2026 · La evidencia de campo

2026 24 may
📱 Bugreport de Chrome · STA-017 → Tier A
Se capturan 2 ANR completos de Chrome con trazas nativas de libminikin.so en un Xiaomi Redmi Note 14 5G (Android 16, HyperOS 3.0).
2026 26 may
📱 Bugreport de SystemUI · 85 crashes
Se capturan 85 SystemUI crashes en 4 días en el mismo dispositivo. Primera confirmación en Android 16.
2026 31 may
📱 STA-015-DL · Ataque remoto confirmado
Se confirma el ataque remoto a través de Google Drive: callingPackage=com.google.android.apps.docs. Afecta a 6 navegadores.
2026 10 jun
📋 Notificaciones a vendors
Se envían reportes a Mozilla, Opera, DuckDuckGo (HackerOne), Brave (HackerOne), Tor Project, Xiaomi (HackerOne).
2026 10 jun
📱 Bugreport de Firefox · 16.006 ms
Se captura ANR de Firefox con 16.006 ms de duración. Trigger: enlace real de Gmail. STA-017 Firefox → Tier A.
2026 14 jun
📋 Respuesta de MSRC
Microsoft MSRC responde, deriva el caso a Google y solicita casos separados.
2026 15 jun
❌ Google cierra libminikin · "out of scope"
Google cierra el reporte específico de libminikin (524288518) como "out of scope" — meses después de pagar los $250 por el caso Binder/IPC.
2026 16 jun
📋 Apelación a Google
Se envía apelación para STA-015-DL (CVSS 8.6), citando precedentes CVE-2023-21167 y A-259942964.
2026 16 jun
📋 Respuesta de Opera · P5 (Informational)
Opera clasifica STA-016/017 como P5 (Informational). Afecta a Opera y Opera Mini.
2026 19 jun
📋 Rechazo de Brave
Brave rechaza el reporte como "upstream/DoS out of scope".
2026 20 jun
📱 Segunda confirmación SystemUI · Edge ANR
Segundo bugreport espontáneo de SystemUI. 3 crashes en 1.7s → Edge ANR 25s después. STA-017 Edge → Tier A.
2026 24 jun
📋 MSRC · Casos separados
Se envían 3 casos separados a MSRC (STA-013, STA-017/STA-020, STA-015-DL).
2026 24 jun
📝 Publicación: Structured Text Amplification (STA)
Se publica el primer artículo en el blog: "Structured Text Amplification (STA)" — el marco teórico.
2026 24 jun
📝 Publicación: Algorithmic DoS en libminikin.so
Se publica el segundo artículo en el blog: "Algorithmic DoS en libminikin.so" — la prueba de concepto.
2026 28 jun
📱 Bugreport de Google App · 5 ANR + 3 TransactionTooLargeException
Se capturan 5 ANR + 3 TransactionTooLargeException en 14 minutos. breakLineGreedy también es vulnerable. STA-017 Google App → Tier A. Primera evidencia de Clase A + Clase B simultáneas.
2026 30 jun
📋 Confirmación sin parche
No hay commits en AOSP, Chromium o AndroidX que aborden los vectores documentados. El boletín de junio no contiene CVEs relacionados.

Julio 2026 · La cuenta atrás

2026 5 jul
📋 Nuevo issue de Google · #531319203
Se abre issue #531319203: system_server boot loop por imagen grande en BitmapCache. El mismo patrón arquitectónico.
2026 6 jul
📋 Boletín de julio sin parche
Google publica el boletín de seguridad de julio de 2026. No contiene ningún parche para los 31 vectores.
2026 8 jul
📋 Xiaomi sin respuesta · 35+ días
Xiaomi no responde tras 35+ días. Se envía seguimiento con deadline del 20 de julio.
2026 10 jul
📝 Publicación: 10 años de vulnerabilidad
Se publica el tercer artículo en el blog: "libminikin: 10 años de vulnerabilidad en el núcleo de Android" — el análisis forense del código de AOSP.
2026 10 jul
📋 Meta sin respuesta · 113+ días
Meta no responde tras 113+ días.
2026 10 jul
📱 Actualización de seguridad · Sigue vulnerable
El dispositivo Xiaomi Redmi Note 14 5G recibe el parche de junio de 2026. Sigue siendo vulnerable.
2026 11 jul
📝 Nota de prensa · BojosXtu
Se prepara la nota de prensa para BojosXtu. Se añade la contradicción de los $250.
2026 12 jul
📋 Issue 477202817 · Confirmado
Se confirma que Chromium Issue 477202817 (reporte al Chrome VRP) sigue cerrado como "Won't Fix (Intended Behavior)".

📅 Próximos pasos

2026 20 jul
⏰ Último día para vendors
Fecha límite para que los vendors (Xiaomi, Meta, etc.) respondan.
2026 30 jul
🚀 PUBLICACIÓN DEL WHITEPAPER
Se publica el whitepaper completo con 31 vectores, trazas nativas, código AOSP y cronología.

31
vectores documentados
15+
archivos de AOSP
13
años de código vulnerable
2.5B
dispositivos afectados

🔴 250 dólares por 2.500 millones de dispositivos: 0,0000001 $ por dispositivo. Menos de lo que vale un átomo de polvo.
Si Google hubiera pagado 1 céntimo por dispositivo, la recompensa habría sido de 25 millones de dólares. Pagaron 250.


Manuel Garcia Peña (Lostmon) · lostmon@gmail.com · lostmon.blogspot.com
Whitepaper completo: 30 de julio de 2026

libminikin: 10 años de vulnerabilidad en el núcleo de Android

Friday, July 10, 2026
libminikin: "Un comportamiento heredado del motor de composición de texto de Android"
Análisis forense del código fuente de AOSP · 6 archivos · 5 capas vulnerables · Una década de deuda técnica

📌 Este artículo es una extensión de mi análisis original: "Algorithmic DoS en libminikin.so – Cómo un texto de 70 KB puede congelar casi cualquier app Android" (24 de junio de 2026).

🔥 10 AÑOS. 10 ARCHIVOS. MAS DE 5 CAPAS VULNERABLES.

El análisis del código fuente de Android (AOSP) confirma que libminikin.so tiene un fallo de diseño sistémico presente desde 2013.

  • 10 archivos vulnerables en el repositorio de AOSP
  • 5 capas del pipeline de texto sin validación de entrada
  • 10 años de deuda técnica documentada (2013 → 2026)
  • 2.500 millones de dispositivos en riesgo
  • Google lo sabe desde 2016 (CVE-2016-2414) y no lo ha arreglado

1. Resumen ejecutivo

Desde la publicación del artículo original sobre el Algorithmic DoS en libminikin.so, he continuado investigando el código fuente de Android (AOSP) para comprender la magnitud real del problema.

Lo que he encontrado es más grave de lo que imaginaba. No se trata de un bug aislado en una función concreta. Es un fallo de diseño sistémico presente en 6 archivos diferentes, distribuido en 5 capas del pipeline de texto, y que ha estado en el repositorio de AOSP desde 2013.


2. Issues públicos de Google (2020–2026)

La siguiente tabla recopila los issues públicos del Google Issue Tracker que documentan problemas en libminikin y el pipeline de texto de Android. Todos ellos fueron cerrados sin una solución estructural.

Issue Año Descripción Estado Enlace
#161830416 2020 Crash en LayoutCache::getOrCreate con traza completa (FreeType → Minikin → HarfBuzz) Won't Fix Ver issue
#167014931 2020 Crash por Float.POSITIVE_INFINITY en LayoutPiece::LayoutPiece Fixed Ver issue
#188985643 2021 SIGSEGV en FontFamily::getClosestMatch en Samsung Galaxy J6+ con fuente personalizada Won't Fix Ver issue
#40268980 (Chromium 1447465) 2023 ANR en Chrome por LayoutCache::getOrCreate reportado por ingeniero de Samsung Won't Fix Ver issue
#477202817 2026 Reporte formal al Chrome VRP — DoS persistente por URL larga. Cerrado como "Intended Behavior" Won't Fix Ver issue
#524288518 2026 Reporte específico de libminikin al Android VRP — cerrado como "out of scope" Out of Scope (interno de Google)
#531319203 Jul 2026 system_server boot loop por imagen grande en BitmapCache — mismo patrón Assigned Ver issue

🔴 Patrón común: Google ha cerrado sistemáticamente estos reportes sin abordar la causa raíz. Solo ha arreglado el caso trivial (#167014931) porque el trigger era evidente (Float.POSITIVE_INFINITY).


3. Análisis del código fuente: 6 archivos vulnerables

He analizado el código fuente de Minikin en el repositorio de AOSP (frameworks/minikin/) y he identificado seis archivos clave que contienen vulnerabilidades de diseño.

Archivo Año Problema principal Función vulnerable
FontFamily.cpp 2013 Punteros nulos en getClosestMatch getClosestMatch()
Layout.cpp 2013 Procesa directamente textos largos sin caché doLayoutWord()
OptimalLineBreaker.cpp 2015 Knuth-Plass O(n²) sin límites computeBreaks()
GreedyLineBreaker.cpp 2017 Algoritmo voraz O(n²) en el peor caso processLineBreak()
LineBreaker.cpp 2018 Decisión entre algoritmos sin validar longitud breakIntoLines()
LayoutCache.h 2018 Caché sin límite de tamaño por entrada getOrCreate()

4. Funciones vulnerables y código exacto

4.1 LineBreaker::breakIntoLines — Sin validación de longitud

Archivo: frameworks/minikin/libs/minikin/LineBreaker.cpp (2018)

LineBreakResult breakIntoLines(const U16StringPiece& textBuffer, BreakStrategy strategy,
                               HyphenationFrequency frequency, bool justified,
                               const MeasuredText& measuredText, const LineWidth& lineWidth,
                               const TabStops& tabStops, bool useBoundsForWidth) {
    if (strategy == BreakStrategy::Greedy || textBuffer.hasChar(CHAR_TAB)) {
        return breakLineGreedy(textBuffer, measuredText, lineWidth, tabStops,
                               frequency != HyphenationFrequency::None, useBoundsForWidth);
    } else {
        return breakLineOptimal(textBuffer, measuredText, lineWidth, strategy, frequency, justified,
                                useBoundsForWidth);
    }
}

🔴 Vulnerabilidad: No hay comprobación de textBuffer.size(). Si el texto es de 70KB, se ejecuta breakLineOptimal o breakLineGreedy sin límite.

4.2 OptimalLineBreaker::computeBreaks — O(n²) sin límites

Archivo: frameworks/minikin/libs/minikin/OptimalLineBreaker.cpp (2015)

LineBreakResult LineBreakOptimizer::computeBreaks(const OptimizeContext& context,
                                                  const U16StringPiece& textBuf,
                                                  const MeasuredText& measuredText,
                                                  const LineWidth& lineWidth,
                                                  BreakStrategy strategy, bool justified,
                                                  bool useBoundsForWidth) {
    // ... algoritmo de programación dinámica Knuth-Plass
    // SIN validación de longitud de entrada
    // Complejidad O(n²) en el peor caso
}

Pero hay un matiz que el código revela: hay una poda activa (active = j + 1) y una optimización bestHope explícitamente comentada como tal: Cpp Esto significa que el algoritmo no es un O(n²) puro sin ninguna mitigación — tiene un mecanismo de poda que en la práctica reduce trabajo cuando delta mayor que 0 (la línea se desborda), avanzando el puntero active. El peor caso teórico sigue siendo O(n²) (cuando casi todos los candidatos permanecen "activos" simultáneamente, como pasaría con una URL sin espacios donde casi cualquier punto de ruptura es candidato).

if (jScore + bestHope >= best) continue;

🔴 Vulnerabilidad: El algoritmo Knuth-Plass tiene complejidad O(n²). Para 70.000 caracteres, son ~4.9 mil millones de operaciones.

4.3 GreedyLineBreaker::processLineBreak — Bucles anidados

Archivo: frameworks/minikin/libs/minikin/GreedyLineBreaker.cpp (2017)

void GreedyLineBreaker::processLineBreak(uint32_t offset, WordBreaker* breaker,
                                         bool doHyphenation) {
    while (isWidthExceeded() || overhangExceedLineLimit(Range(getPrevLineBreakOffset(), offset))) {
        if (tryLineBreakWithWordBreak()) {
            continue;  // El bucle puede ejecutarse muchas veces
        }
        if (doHyphenation && tryLineBreakWithHyphenation(...)) {
            // ...
        }
    }
}

🔴 Vulnerabilidad: Bucle while con llamadas a funciones que recorren el texto. Complejidad O(n²) en el peor caso.

4.4 LayoutCache::getOrCreate — Procesamiento directo sin caché

Archivo: frameworks/minikin/include/minikin/LayoutCache.h (2018)

template <typename F>
void getOrCreate(const U16StringPiece& text, const Range& range, const MinikinPaint& paint,
                 bool dir, StartHyphenEdit startHyphen, EndHyphenEdit endHyphen,
                 bool boundsCalculation, F& f) {
    LayoutCacheKey key(text, range, paint, dir, startHyphen, endHyphen);
    if (range.getLength() >= CHAR_LIMIT_FOR_CACHE) {
        // ¡PROCESAMIENTO DIRECTO! SIN CACHÉ → BLOQUEO UI
        LayoutPiece piece(text, range, dir, paint, startHyphen, endHyphen);
        // ...
        return;
    }
    // ...
}

🔴 Vulnerabilidad: Los textos largos no se almacenan en caché. Se procesan desde cero cada vez que se renderizan.


5. El pipeline completo: 5 capas de fragilidad

1. LineBreaker.cpp (2018) — breakIntoLines()
   └── Decide entre Greedy y Optimal SIN validar longitud
         │
2. OptimalLineBreaker.cpp (2015) / GreedyLineBreaker.cpp (2017)
   └── Algoritmo O(n²) SIN límites de entrada
         │
3. Layout.cpp (2013) — doLayoutWord()
   └── Llama a LayoutCache SIN verificar tamaño
         │
4. LayoutCache.h (2018) — getOrCreate()
   └── Si texto > CHAR_LIMIT_FOR_CACHE → procesa DIRECTAMENTE
         │
5. HarfBuzz (hb_shape)
   └── Renderizado de glifos → O(n²) o peor
         │
         ▼
   UI BLOQUEADA (5-16 segundos)

🔴 Todas las capas son vulnerables. Ninguna valida la longitud del texto de entrada.


🧠 El Pipeline de Amplificación de Minikin: Anatomía de un Fallo de Diseño de 13 Años

Aquí profundizo en el pipeline completo y en cómo cada capa amplifica el payload hasta convertir 70KB en un bloqueo de 16 segundos.


El motor de layout de texto de Android (Minikin) contiene un fallo de diseño sistémico que permite que un texto de tan solo 70.000 caracteres (aproximadamente 70KB) bloquee el hilo de interfaz de usuario durante 5-16 segundos, provocando ANR en cualquier aplicación que muestre texto sin truncar.

El problema no reside en una única función, sino en más de 10 archivos y 15 funciones que conforman el pipeline de procesamiento de texto. Cada capa del pipeline amplifica el coste computacional sin aplicar ningún tipo de validación de longitud o límite de entrada.

🔴 Dato clave: El código más antiguo data de 2013. Google ha tenido más de 13 años para arreglar esto.


🧠 El esquema de amplificación: 10 capas de fragilidad

Un texto de 70KB con una estructura específica (/code> repetido) activa una cascada de amplificación en la que cada capa del pipeline multiplica el coste computacional:

70KB de entrada
      │
      ▼
 ┌─────────────────────────────────────────────────────────────┐
 │ CAPA 1: LineBreaker.cpp (2018)                              │
 │ breakIntoLines() decide entre Greedy y Optimal             │
 │ SIN validar longitud → pasa 70KB al siguiente paso          │
 └─────────────────────────────────────────────────────────────┘
      │
      ▼
 ┌─────────────────────────────────────────────────────────────┐
 │ CAPA 2: OptimalLineBreaker.cpp (2015) / GreedyLineBreaker  │
 │ computeBreaks() / processLineBreak()                       │
 │ Algoritmo O(n²) → 70.000² = 4.900.000.000 iteraciones      │
 └─────────────────────────────────────────────────────────────┘
      │
      ▼
 ┌─────────────────────────────────────────────────────────────┐
 │ CAPA 3: Layout.cpp (2013)                                   │
 │ doLayoutWord() llama a LayoutCache SIN verificar tamaño    │
 └─────────────────────────────────────────────────────────────┘
      │
      ▼
 ┌─────────────────────────────────────────────────────────────┐
 │ CAPA 4: LayoutCache.h (2018)                                │
 │ getOrCreate(): si texto > CHAR_LIMIT_FOR_CACHE             │
 │ → procesa DIRECTAMENTE, sin caché                         │
 └─────────────────────────────────────────────────────────────┘
      │
      ▼
 ┌─────────────────────────────────────────────────────────────┐
 │ CAPA 5: LayoutCore.cpp (2018)                               │
 │ LayoutPiece() constructor → hb_shape (HarfBuzz)            │
 │ HarfBuzz tiene complejidad O(n²) o peor                    │
 └─────────────────────────────────────────────────────────────┘
      │
      ▼
 ┌─────────────────────────────────────────────────────────────┐
 │ CAPA 6: Hyphenator.cpp (2015)                               │
 │ hyphenateFromCodes() → bucles anidados O(n²)               │
 └─────────────────────────────────────────────────────────────┘
      │
      ▼
 ┌─────────────────────────────────────────────────────────────┐
 │ CAPA 7: Measurement.cpp (2015)                              │
 │ getOffsetForAdvance() / distributeAdvances() → O(n²)       │
 └─────────────────────────────────────────────────────────────┘
      │
      ▼
 ┌─────────────────────────────────────────────────────────────┐
 │ CAPA 8: WordBreaker.cpp (2015)                              │
 │ detectEmailOrUrl() / findNextBreakInEmailOrUrl() → O(n)   │
 └─────────────────────────────────────────────────────────────┘
      │
      ▼
 ┌─────────────────────────────────────────────────────────────┐
 │ CAPA 9: FontCollection.cpp (2013)                           │
 │ getGlyphScore() → llama a hb_shape() nuevamente           │
 └─────────────────────────────────────────────────────────────┘
      │
      ▼
 ┌─────────────────────────────────────────────────────────────┐
 │ CAPA 10: FontFamily.cpp (2013)                              │
 │ getClosestMatch() → punteros nulos → SIGSEGV               │
 └─────────────────────────────────────────────────────────────┘
      │
      ▼
   🔥 UI BLOQUEADA (5-16 segundos)

🔴 10 capas. 10 archivos. Ninguna valida la longitud del texto de entrada.


📂 Tabla completa de archivos y funciones vulnerables

Archivo Año Función Complejidad Problema
LineBreaker.cpp2018breakIntoLines()O(1)No valida longitud de entrada
OptimalLineBreaker.cpp2015computeBreaks()O(n²)Knuth-Plass sin límites
GreedyLineBreaker.cpp2017processLineBreak()O(n²)Bucles anidados sin límites
Layout.cpp2013doLayoutWord()O(n)Llama a LayoutCache sin verificar tamaño
LayoutCache.h2018getOrCreate()O(n)CHAR_LIMIT_FOR_CACHE → procesa directamente
LayoutCore.cpp2018LayoutPiece()O(n²)Llama a hb_shape (HarfBuzz) sin límites
Hyphenator.cpp2015hyphenateFromCodes()O(n²)Bucles anidados sin límites
Measurement.cpp2015getOffsetForAdvance()O(n²)Bucles anidados sin límites
Measurement.cpp2015distributeAdvances()O(n²)Bucles anidados sin límites
WordBreaker.cpp2015detectEmailOrUrl()O(n)Recorre texto sin límites
WordBreaker.cpp2015findNextBreakInEmailOrUrl()O(n)Recorre texto sin límites
FontCollection.cpp2013getGlyphScore()O(n²)Llama a hb_shape sin límites
FontCollection.cpp2013itemize()O(n)Recorre texto sin límites
FontFamily.cpp2013getClosestMatch()O(n)Puntero nulo → SIGSEGV

📊 10 archivos. 15 funciones. 10 años de código vulnerable. 13 años desde el primer archivo.


🔬 Amplificación paso a paso: Cómo 70KB se convierten en 5 mil millones de operaciones

Paso 1: Entrada (70KB)

El payload es una URL de 70.000 caracteres con el patrón [PAYLOAD] repetido:

https://example.com/[PAYLOAD]

Paso 2: LineBreaker::breakIntoLines (2018)

if (strategy == BreakStrategy::Greedy || textBuffer.hasChar(CHAR_TAB)) {
    return breakLineGreedy(...);   // ← No hay validación de longitud
} else {
    return breakLineOptimal(...);  // ← Tampoco hay validación
}

Amplificación: Decide el algoritmo sin comprobar la longitud del texto.

Paso 3: OptimalLineBreaker::computeBreaks (2015) — O(n²)

LineBreakResult LineBreakOptimizer::computeBreaks(...) {
    // ... algoritmo de programación dinámica Knuth-Plass
    // SIN validación de longitud de entrada
    // Complejidad O(n²) en el peor caso
}

Amplificación: Para n=70.000 → 4.900.000.000 iteraciones.

Paso 4: Layout::doLayoutWord (2013)

float Layout::doLayoutWord(...) {
    // ...
    LayoutCache::getInstance().getOrCreate(textBuf, range, paint, isRtl, startHyphen, endHyphen,
                                           boundsCalculation, f);
    // ...
}

Amplificación: Llama a LayoutCache sin verificar el tamaño del texto.

Paso 5: LayoutCache::getOrCreate (2018)

if (range.getLength() >= CHAR_LIMIT_FOR_CACHE) {
    LayoutPiece piece(text, range, dir, paint, startHyphen, endHyphen);
    // PROCESAMIENTO DIRECTO → SIN CACHÉ → BLOQUEO UI
    return;
}

Amplificación: Los textos largos no se almacenan en caché. Se procesan desde cero cada vez.

Paso 6: LayoutPiece constructor (2018)

LayoutPiece::LayoutPiece(...) {
    // ...
    hb_shape(hbFont.get(), buffer.get(), features.empty() ? NULL : &features[0],
             features.size());
    // ...
}

Amplificación: hb_shape (HarfBuzz) puede tener complejidad O(n²) o peor.

Paso 7: Hyphenator::hyphenateFromCodes (2015)

void HyphenatorCXX::hyphenateFromCodes(...) const {
    for (size_t i = 0; i < len - 1; i++) {
        for (size_t j = i; j < len; j++) {
            // BUCLES ANIDADOS SIN LÍMITES
        }
    }
}

Amplificación: Bucles anidados O(n²) que se ejecutan para cada palabra.

Paso 8: Measurement::getOffsetForAdvance / distributeAdvances (2015)

size_t getOffsetForAdvance(...) {
    for (size_t i = start; i < max; i++) { /* ... */ }
    for (size_t i = searchStart; i <= max; i++) { /* ... */ }
}

Amplificación: Bucles anidados O(n²) en el peor caso.

Paso 9: WordBreaker::detectEmailOrUrl (2015)

void WordBreaker::detectEmailOrUrl() {
    for (i = mLast; i < mTextSize; i++) { /* ... */ }
}

Amplificación: Bucle O(n) que recorre el texto sin límites.

Paso 10: FontCollection::getGlyphScore (2013)

uint32_t getGlyphScore(...) {
    hb_shape(font.get(), buffer.get(), nullptr, 0);  // O(n²)
}

Amplificación: Llama a hb_shape para cada fuente evaluada.

🔴 Resultado: 70KB → 5 mil millones de iteraciones → 16 segundos de bloqueo → ANR.


⚡ El payload mínimo

Para activar todo el pipeline de amplificación, se necesita un texto con:

  • Longitud: > 70.000 caracteres
  • Estructura: Caracteres que multipliquen los puntos de decisión:
    • # → duplica decisiones de salto
    • / → cada barra añade un punto de ruptura
    • % → expansión UTF-8 → UTF-16
    • → propiedades de salto ambiguas

Payload óptimo:

[PAYLOAD]

Repetido hasta alcanzar 70.000 caracteres.


📅 Cronología (2013–2026)

Año Evento Lo que demuestra
2013FontCollection.cpp, FontFamily.cpp, Layout.cpp creadosEl código vulnerable existe desde hace 13 años
2015OptimalLineBreaker.cpp, Hyphenator.cpp, Measurement.cpp, WordBreaker.cpp creadosEl pipeline de amplificación se completa
2016CVE-2016-2414 — DoS en MinikinGoogle sabía que Minikin era vulnerable y lo arregló
2017GreedyLineBreaker.cpp creadoSe añade una capa más de amplificación
2018LineBreaker.cpp, LayoutCore.cpp, LayoutCache.h creadosEl pipeline alcanza 10 capas
2020Issue #161830416 — crash en LayoutCacheGoogle recibió evidencia, la ignoró
2021Issue #188985643 — SIGSEGV en SamsungGoogle lo ignoró porque no se reproducía en Pixel
2023Chromium 1447465 — ANR en Chrome reportado por SamsungGoogle lo cerró con "who knows"
2026Reporte VRP — 31 vectores documentadosGoogle lo cerró "out of scope"
2026Boletín de julio — sin parcheGoogle sigue ignorando el problema

🔴 13 años de código vulnerable. 10 archivos. 15 funciones. Google no lo ha arreglado.


El pipeline de layout de texto de Android está compuesto por más de 10 archivos y 15 funciones que, en conjunto, forman un sistema de amplificación de DoS. Cada capa multiplica el coste computacional sin aplicar validación de longitud, permitiendo que un texto de 70KB bloquee la UI durante 5-16 segundos.

Google ha tenido 13 años para arreglar esto. Los archivos vulnerables datan de 2013 (FontCollection.cpp, FontFamily.cpp, Layout.cpp) y se han ido añadiendo más capas a lo largo de los años (2015, 2017, 2018). Ninguna de ellas ha sido parcheada para abordar el problema de la validación de entrada.

6. FontFamily.cpp: el origen del SIGSEGV en Samsung

El archivo FontFamily.cpp (creado en 2013) contiene la función getClosestMatch, responsable del SIGSEGV documentado en issue #188985643 (2021, Samsung Galaxy J6+).

FakedFont FontFamily::getClosestMatch(FontStyle style, const VariationSettings& axes) const {
    if (features::typeface_redesign_readonly()) {
        int bestIndex = 0;
        Font* bestFont = mFonts[bestIndex].get();  // ← ¡POSIBLE PUNTERO NULO!
        // ...
    }
    // ...
}

🔴 Problema: mFonts puede estar vacío. En producción, MINIKIN_ASSERT no se activa y el programa accede a memoria inválida → SIGSEGV.

Google respondió:

"I haven't receive any crash report at this function on Pixel devices, and likely it is not actionable to me only with this stack trace."


7. LayoutCache.h: el amplificador silencioso

El archivo LayoutCache.h (creado en 2018) es donde la vulnerabilidad se vuelve persistente.

if (range.getLength() >= CHAR_LIMIT_FOR_CACHE) {
    LayoutPiece piece(text, range, dir, paint, startHyphen, endHyphen);
    // PROCESAMIENTO DIRECTO → SIN CACHÉ → BLOQUEO UI
    return;
}

🔴 Esto significa que:

  1. Los textos largos no se almacenan en caché.
  2. Cada vez que se renderizan, se procesan desde cero.
  3. El bloqueo de UI ocurre cada vez que se muestra el texto.

8. Evidencia forense: los números no mienten

Longitud del texto Iteraciones aprox. Tiempo de bloqueo
1.000 caracteres ~1.000² = 1.000.000 < 100 ms
10.000 caracteres ~10.000² = 100.000.000 ~500 ms - 1 s
70.000 caracteres ~70.000² = 4.900.000.000 5 - 16 segundos

🔴 Un texto de 70KB genera casi 5 mil millones de operaciones en el hilo UI.


9. La cronología de la libminikin (2013–2026)

Año Evento Lo que demuestra
2013 FontFamily.cpp y Layout.cpp creados El código vulnerable existe desde hace 11 años
2016 CVE-2016-2414 — DoS en Minikin Google sabía que Minikin era vulnerable y lo arregló
2020 Issue #161830416 — crash en LayoutCache Google recibió evidencia, la ignoró
2021 Issue #188985643 — SIGSEGV en Samsung Google lo ignoró porque no se reproducía en Pixel
2023 Chromium 1447465 — ANR en Chrome reportado por Samsung Google lo cerró con "who knows"
2026 Reporte VRP — libminikin ANR y crash loop Google lo cerró "out of scope"
Jul 2026 Boletín de seguridad sin parche Google sigue ignorando el problema

🔴 Google ha tenido más de 10 años para arreglar esto. Ha arreglado los casos triviales e ignorado los complejos.


10. La contradicción del VRP: $250 y "out of scope"

El timeline:

  • 20 de enero de 2026: Reporte formal al Android VRP (27 vectores).
  • Google paga $250 por el caso Binder/IPC (A-477279924).
  • 15 de junio de 2026: Google cierra el reporte específico de libminikin como "out of scope" (524288518).
  • 16 de junio de 2026: Apelación enviada para STA-015-DL (CVSS 8.6) Google cierra el reporte de apelación específico de libminikin como "NoT reproducible"
  • 6 de julio de 2026: Boletín de seguridad de julio — sin parche para ningún vector.

💡 La contradicción: Google pagó una recompensa por la investigación, pero cerró el reporte más grave como "out of scope". Si está fuera de alcance, ¿por qué pagan?

Por primera vez, después de más de 400 vulnerabilidades descubiertas y documentadas a lo largo de estos años, he sido recompensado con 250 dólares por la primera parte de mi trabajo. Ni la cantidad, ni la forma en que se ha gestionado la segunda parte de la investigación reflejan el alcance ni la gravedad de las vulnerabilidades documentadas

Matemáticamente, teniendo en cuenta que pueden ser 2.500 millones de dispositivos afectados, eso es 0,0000001 dólares por dispositivo. Es decir, menos de una diezmilésima parte de un céntimo por cada teléfono vulnerable.


11. Conclusión: 10 años de código vulnerable

El análisis del código fuente de AOSP confirma que la vulnerabilidad en libminikin no es un bug aislado, sino un fallo de diseño sistémico presente en el núcleo de Android desde 2013.

🔴 Lo que sabemos ahora:

  • 6 archivos vulnerables en AOSP.
  • 5 capas del pipeline de texto sin validación.
  • 10 años de evidencia documentada (2013 → 2026).
  • Google ha tenido más de 10 años para arreglar esto.
  • Ha arreglado los casos triviales (CVE-2016-2414) e ignorado los complejos.

El 30 de julio de 2026 publicaré el whitepaper completo con los 31 vectores documentados, las trazas nativas completas y el análisis forense del código fuente. Si tu aplicación usa TextView, ya estás avisado.

📌 Para desarrolladores (mitigación inmediata):

private static final int MAX_SAFE_LENGTH = 8192;
String safe = text.length() > MAX_SAFE_LENGTH 
    ? text.substring(0, MAX_SAFE_LENGTH) + "…" 
    : text;
textView.setText(safe);

12. Referencias y enlaces

Artículos del investigador

Issues públicos de Google

Código fuente de AOSP

🔥 1.6 Minikin es un síntoma de una brecha de resiliencia sistémica

La vulnerabilidad de libminikin no es un bug aislado. Es un síntoma de una brecha de resiliencia sistémica en el framework de Android: la ausencia de un mecanismo estandarizado de "safe fallback" para manejar datos que superan los límites del sistema.

El mismo patrón aparece en al menos diez componentes distintos del sistema operativo Android:

Componente Modo de fallo Impacto
FragmentManager No captura TransactionTooLargeException Bucle de crash permanente
TaskPersister Persiste el estado corrupto en disco El bucle de crash sobrevive a reinicios
SystemUI Lee TaskPersister sin validación Bucle de crash → pantalla de bloqueo
Print Service Sin validación del tamaño del documento SystemUI crash
ClipboardManager Serializa todo el contenido del portapapeles sin límites SystemUI crash
Binder Límite de 1MB sin safe fallback Crash de la app
libminikin.so Sin validación de longitud de entrada ANR (5–16s)
LayoutCache CHAR_LIMIT_FOR_CACHE fuerza el procesamiento directo Amplifica el DoS
BitmapCache Sin validación del tamaño de imagen (issue #531319203) Crash de system_server → bucle de arranque
MediaSession Sin validación del tamaño de la carátula Crash de system_server → bucle de arranque

🔴 Diez componentes. Diez modos de fallo diferentes. El mismo patrón subyacente: sin validación de entrada antes del procesamiento, y sin safe fallback cuando se superan los límites.

Esto no es un bug de Minikin. Es un fallo de diseño del framework de Android que ha estado presente durante más de una década, afectando a componentes que van desde la capa de aplicación hasta el servidor central del sistema. Google ha añadido capas de complejidad a Minikin sin abordar la causa raíz, y ha descartado los informes de estos problemas como "fuera de alcance" o "comportamiento intencionado" durante años.

Minikin no es la enfermedad. Es un síntoma.



Manuel Garcia Peña (Lostmon) · lostmon@gmail.com · lostmon.blogspot.com
Whitepaper completo: 30 de julio de 2026

Algorithmic DoS en libminikin.so

Wednesday, June 24, 2026
Algorithmic DoS en libminikin.so – Cómo un texto de 70 KB puede congelar casi cualquier app Android 
 

Un texto de 70.000 caracteres puede congelar casi cualquier app de  Android durante 10–16 segundos. No es corrupción de memoria. Es un ataque algorítmico que explota la complejidad O(n²) del motor de diseño de texto nativo de Android. Y lo peor: cualquier aplicación con un TextView es vulnerable.

A diferencia de muchas vulnerabilidades que requieren aplicaciones de prueba específicas para su reproducción, varios de los vectores documentados pueden activarse simplemente mediante enlaces especialmente construidos que son procesados por componentes estándar del sistema Android. Esto amplía significativamente la superficie de exposición potencial

He documentado 31 vectores en 9 empresas, incluyendo Google, Meta, Microsoft, Mozilla, Opera, DuckDuckGo, Brave, Tor Project y Xiaomi. El fallo reside en libminikin.so, el motor de diseño de texto de Android, y afecta a todos los dispositivos con Android 9 a 16.


1. El problema: Algoritmos que se vuelven en tu contra

libminikin.so implementa el algoritmo de salto de línea óptimo (Knuth-Plass) en la función LineBreakOptimizer::computeBreaks. Este algoritmo tiene complejidad O(n²) en el peor caso: si duplicas la longitud del texto, el tiempo de procesamiento se multiplica por cuatro.

Para un texto normal de 200 caracteres, el tiempo es imperceptible. Pero para uno de 70.000 caracteres con la estructura adecuada, el algoritmo ejecuta millones de operaciones y bloquea el hilo de la interfaz durante más de 10 segundos.

El vector documentado en libminikin no requiere una aplicación de demostración específica. En los escenarios analizados, basta con que un componente del sistema represente una URL especialmente construida en un TextView para activar el cálculo intensivo del algoritmo de composición de texto.

📌 Idea clave: No es la longitud, es la estructura. Una URL con caracteres como #, /, % y multiplica el número de "candidatos a salto de línea", y el algoritmo se dispara.

2. La estructura del ataque: Caracteres que multiplican el caos

Carácter Función en la URL Efecto en libminikin
# Fragmento (separador lógico) Duplica las decisiones de salto de línea
/ Separador de rutas Cada barra es un punto de ruptura → O(n²) con el número de barras
% Codificación porcentual Expansión UTF-8 → UTF-16, fragmenta la cadena
Carácter multibyte Propiedades de salto de línea ambiguas → el algoritmo explora ambas opciones

El patrón óptimo repite miles de veces. Cada 9 bytes generan múltiples puntos de decisión y el algoritmo explota.

Resultado: La UI se congela durante 10–16 segundos. El stack trace nativo confirma que libminikin es el único responsable.

4. Vectores de ataque: Más allá de los navegadores

El fallo no se limita a los navegadores. Cualquier aplicación que muestre texto en un TextView sin truncar es un vector de ataque:

Componente / Aplicación ¿Muestra URL larga? Vulnerabilidad observada
Chrome (menú contextual) ✅ ANR de 5+ segundos
Firefox (diálogo externo) ✅ ANR de 16 segundos (enlace real de Google Drive)
Opera / Opera Mini ✅ Crash con TransactionTooLargeException
DuckDuckGo Sí (historial) ✅ Bucle de cierre permanente
Google drive ⚠️ Potencial
WhatsApp (vista previa de enlace) ⚠️ Potencial
Cualquier app con TextView ✅ Confirmado con PoC

5. El vector crítico: STA-015-DL (SystemUI crash loop)

🔥 CVSS 8.6 (CRÍTICO) – Un solo clic en un enlace de Google Drive puede provocar un bucle de cierre de SystemUI que requiere reinicio forzado.

La cadena de ataque STA-015-DL demuestra que el problema trasciende las aplicaciones y afecta a SystemUI, la interfaz del sistema:

  1. La víctima hace clic en un archivo HTML alojado en Google Drive.
  2. Google Drive lanza un Intent.ACTION_VIEW con una URL malformada.
  3. La URL bypasea todas las validaciones del navegador porque llega a través de callingPackage=com.google.android.apps.docs (certificado por Android).
  4. La URL corrompe el estado de TaskPersister en disco.
  5. SystemUI intenta leer el estado → DeadObjectExceptionbucle de cierre.
  6. Recuperación: reinicio forzado del dispositivo.

Evidencia de campo: Un dispositivo en producción (Xiaomi Redmi Note 14 5G, Android 16) registró 85 SystemUI crashes en 4 días, con un tiempo mínimo de supervivencia de 374ms.

// SystemUI crash en el arranque – bugreport 2026-05-26 // Process-Runtime: 374ms – crashea antes de renderizar cualquier UI android.os.BadParcelableException: Failure retrieving array; only received 1 of 4 at android.content.pm.BaseParceledListSlice.<init>(...) at com.android.wm.shell.sysui.ShellInit.init(...) ← crash en el arranque Caused by: android.os.DeadObjectException: Transaction failed on small parcel

6. Stack traces nativos (evidencia forense)

Chrome – Menú contextual (24 de mayo de 2026)

// ANR — Chrome PID 3533 "main" prio=5 tid=1 Native ← UI THREAD BLOCKED native: minikin::getPrevWordBreakForCache libminikin.so native: minikin::LayoutCacheKey::LayoutCacheKey libminikin.so native: minikin::LayoutCache::getOrCreate libminikin.so native: minikin::StyleRun::getLineMetrics libminikin.so native: minikin::MeasuredText::getLineMetrics libminikin.so native: minikin::LineBreakOptimizer::computeBreaks ← O(n²) en URL larga native: minikin::breakLineOptimal libminikin.so native: android::nComputeLineBreaks libhwui.so at android.widget.TextView.onMeasure (TextView.java:11486) at org.chromium.chrome.browser.contextmenu.ContextMenuListView.onMeasure ↑ MENÚ CONTEXTUAL DE CHROME — long-press en URL sobredimensionada

Firefox – Diálogo de aplicación externa (10 de junio de 2026)

// org.mozilla.firefox PID 3506 — ANR 2026-06-10 — 16.006 ms "main" prio=5 tid=1 Native ← UI THREAD BLOCKED 16.006 ms native: minikin::LayoutCacheKey::LayoutCacheKey+120 libminikin.so native: minikin::LayoutCache::getOrCreate libminikin.so native: minikin::LayoutPieces::getOrCreate libminikin.so native: minikin::StyleRun::getLineMetrics libminikin.so native: minikin::MeasuredText::getLineMetrics libminikin.so native: minikin::LineBreakOptimizer::computeBreaks+1752 ← O(n²) en URL larga native: minikin::breakLineOptimal+476 libminikin.so native: android::nComputeLineBreaks+356 libhwui.so at org.mozilla.fenix.customtabs.ExternalAppBrowserActivity URL rendering // Trigger: Enlace REAL de Google.Drive — NO una prueba controlada

7. Mitigación

7.1 A nivel de framework (Google/AOSP)

Google debería implementar un límite de entrada antes de ejecutar el algoritmo O(n²):

// En LineBreakOptimizer.cpp if (textLength > MAX_SAFE_TEXT_LENGTH_FOR_OPTIMIZER) { return computeBreaksGreedy(measured, start, end, constraints); // O(n) }

7.2 A nivel de aplicación (inmediato)

Los desarrolladores pueden truncar el texto antes de mostrarlo en un TextView:

private static final int MAX_SAFE_LENGTH = 8_192; String safe = text.length() > MAX_SAFE_LENGTH ? text.substring(0, MAX_SAFE_LENGTH) + "…" : text; textView.setText(safe);

8. Conclusión

La complejidad O(n²) en libminikin convierte una URL de 70 KB en un arma de denegación de servicio que puede congelar cualquier app Android que la muestre en un TextView sin truncar. No es corrupción de memoria, es explotación algorítmica — usar la propia complejidad del sistema en su contra.

Google ha empezado a moverse (LargePayloadSupport, savedstate 1.5.0), pero no hay parche público para libminikin a fecha de junio de 2026. La comunidad y los desarrolladores pueden aplicar mitigaciones inmediatas truncando el texto antes de mostrarlo.

Nota sobre el proceso de divulgación Esta vulnerabilidad fue notificada inicialmente al Android Vulnerability Rewards Program (VRP) el 20 de enero de 2026, como parte de una investigación más amplia. El reporte específico sobre la vulnerabilidad de complejidad algorítmica en libminikin.so fue presentado el 15 de junio de 2026. El reporte fue cerrado con la clasificación "Out of Scope". Posteriormente, el 21 de junio de 2026, se presentó una apelación acompañada de evidencia adicional, incluyendo informes ANR, trazas de pila nativas y resultados obtenidos durante la investigación. La apelación fue finalmente cerrada con la clasificación "Not Reproducible". El análisis técnico y la evidencia presentada en este artículo reflejan los resultados obtenidos durante la investigación independiente realizada por el autor y se publican con fines de investigación, documentación y mejora de la seguridad.

Apéndice: Nuevo caso reproducible (28 de junio de 2026) – Google App / Android Translate

Desde la publicación inicial de este análisis, he seguido monitorizando el comportamiento del sistema. El pasado 28 de junio de 2026 apareció un segundo caso, completamente independiente del anterior, que no hace sino reforzar la hipótesis planteada.

Mientras que el primer vector documentado en la sección 4 utilizaba StaticLayout y breakLineOptimal(), este nuevo caso utiliza DynamicLayout y breakLineGreedy() durante el procesamiento de texto iniciado por Android System Intelligence mediante un android.intent.action.TRANSLATE.

Secuencia observada

Usuario selecciona texto
  ▼
Android System Intelligence
  ▼
Google App (ImplicitTranslateSearchEntrypointInternal)
  ▼
SpannableStringBuilder.replace()
  ▼
DynamicLayout.reflow()
  ▼
libminikin::breakLineGreedy()
  ▼
getPrevWordBreakForCache()
  ▼
ANR (Application Not Responding)

Cinco ANR consecutivos

El mismo texto produjo cinco ANR consecutivos en aproximadamente catorce minutos. Todos compartían el mismo patrón:

  • Main thread bloqueado durante más de 6 segundos.
  • Entrada por DynamicLayout.
  • Ejecución dentro de libminikin.
  • Funciones getPrevWordBreakForCache() o getNextWordBreakForCache().

Después de varios ANR, el proceso también comenzó a finalizar con TransactionTooLargeException durante la restauración del estado de la actividad, un efecto secundario posterior al bloqueo del hilo principal que conecta directamente con el fenómeno de amplificación estructural descrito en la sección 5.

Comparación con el caso de Edge

Aspecto Microsoft Edge Google App
Layout StaticLayout DynamicLayout
Algoritmo breakLineOptimal() breakLineGreedy()
Desencadenante Re‑layout tras cambio del sistema Reflow tras modificar texto
Entrada Omnibox Traductor integrado
ANR 1 ANR 5 ANR consecutivos

Aunque las rutas son diferentes, ambas convergen en el mismo componente del framework:

          StaticLayout
              │
              ├──► breakLineOptimal()
              │
          DynamicLayout
              │
              ├──► breakLineGreedy()
              │
              ▼
         libminikin
              │
              ├──► getPrevWordBreakForCache()
              └──► getNextWordBreakForCache()

Implicaciones

Este segundo caso reduce significativamente la probabilidad de que el problema sea específico de una aplicación concreta. Ahora existen al menos dos aplicaciones independientes, con dos flujos de ejecución distintos y dos algoritmos de line breaking diferentes, que terminan bloqueando el hilo principal dentro de libminikin sobre la misma versión de Android 16 (BuildId 4fabe53671b5ead88314c00a1fd6d67d).

En otras palabras, la evidencia ya no apunta únicamente a un problema asociado a Microsoft Edge, sino a un comportamiento común del framework de composición de texto de Android cuando procesa determinadas entradas.

Este segundo caso demuestra que la Amplificación de Texto Estructurado no es un fenómeno limitado a un flujo de ejecución concreto, sino que puede manifestarse en distintos puntos del framework siempre que el texto pase por las capas de composición y medida de libminikin.

— Añadido el 28 de junio de 2026

Análisis interno del pipeline de layout de Minikin

Tras correlacionar los múltiples ANR obtenidos con el código fuente de AOSP, la investigación apunta a que las funciones getPrevWordBreakForCache() y getNextWordBreakForCache() probablemente no constituyen la causa raíz del problema, sino que forman parte de un pipeline de composición de texto mucho más amplio.


DynamicLayout / StaticLayout
            │
            ▼
      MeasuredText
            │
            ▼
     LayoutSplitter
            │
   ┌────────┴────────┐
   ▼                 ▼
getPrevWordBreakForCache()
getNextWordBreakForCache()
            │
            ▼
       LayoutCache
            │
            ▼
       LayoutPiece
            │
            ▼
 WordBreaker / Hyphenation
            │
            ▼
breakLineGreedy() / breakLineOptimal()

El análisis del código fuente de AOSP muestra que LayoutSplitter utiliza getPrevWordBreakForCache() y getNextWordBreakForCache() para determinar los límites del fragmento de texto que será almacenado dentro de LayoutCache.

Además, la implementación de LayoutCache indica que cuando el texto supera un determinado tamaño (CHAR_LIMIT_FOR_CACHE), la caché deja de reutilizarse y el sistema debe reconstruir un nuevo LayoutPiece, recalculando el layout completo.


LayoutCache::getOrCreate()

        │

        ├── Cache hit
        │       │
        │       ▼
        │  Reutiliza LayoutPiece
        │
        └── Cache miss
                │
                ▼
      Construcción de LayoutPiece
                │
                ▼
        Glyph shaping
                │
                ▼
         Word breaking
                │
                ▼
      Cálculo del layout

Esto conduce a una nueva hipótesis de trabajo. Más que encontrarnos ante un fallo localizado en getPrevWordBreakForCache(), es posible que los ANR sean consecuencia de la reconstrucción repetitiva del pipeline completo de composición de texto cuando la reutilización de la caché deja de ser efectiva.

Esta hipótesis encaja con los casos observados durante la investigación:

  • Microsoft Edge alcanza este pipeline mediante StaticLayout y breakLineOptimal().
  • Google Translate lo hace mediante DynamicLayout y breakLineGreedy().
  • Ambas rutas convergen finalmente en la misma infraestructura interna de Minikin encargada de la caché y del cálculo de límites de palabra.

Un aspecto especialmente relevante es que Google ha realizado múltiples modificaciones en esta parte del código fuente de Minikin a lo largo de los últimos años, incluyendo optimizaciones específicas del sistema de caché y del rendimiento del motor de layout. Esto sugiere que se trata de una zona considerada crítica desde el punto de vista del rendimiento.

En el momento de redactar este artículo, esta explicación constituye una hipótesis fundamentada en la correlación entre los stack traces de los ANR y el análisis del código fuente de AOSP. Será necesario un análisis más profundo —mediante perfilado o reproducción controlada— para determinar si la causa raíz corresponde a una invalidación de la caché, un problema de complejidad algorítmica o cualquier otro cuello de botella interno del motor de composición de texto de Minikin.


🔴 Conclusión: Esta evidencia independiente refuerza el argumento de que la vulnerabilidad en libminikin no es un problema específico de navegadores, sino un fallo fundamental del framework de layout de texto de Android que afecta a todo el ecosistema de aplicaciones Android.

El 30 de julio de 2026 publicaré el whitepaper completo con los 31 vectores documentados y la evidencia forense completa. Si tu aplicación usa TextView, ya estás avisado.

Manuel Garcia Peña (Lostmon)
Investigador independiente · lostmon@gmail.com
Blog: lostmon.blogspot.com

 

Browse

About:Me

My blog:http://lostmon.blogspot.com
Mail:Lostmon@gmail.com
Lostmon Google group
Lostmon@googlegroups.com

La curiosidad es lo que hace
mover la mente...

Friends