📌 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. 6 ARCHIVOS. 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.
- 6 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
- Resumen ejecutivo
- Issues públicos de Google (2020–2026)
- Análisis del código fuente: 6 archivos vulnerables
- Funciones vulnerables y código exacto
- El pipeline completo: 5 capas de fragilidad
- FontFamily.cpp: el origen del SIGSEGV en Samsung
- LayoutCache.h: el amplificador silencioso
- Evidencia forense: los números no mienten
- La cronología de la negligencia (2013–2026)
- La contradicción del VRP: $250 y "out of scope"
- Conclusión: 10 años de código vulnerable
- Referencias y enlaces
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
}
🔴 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.cpp | 2018 | breakIntoLines() | O(1) | No valida longitud de entrada |
| OptimalLineBreaker.cpp | 2015 | computeBreaks() | O(n²) | Knuth-Plass sin límites |
| GreedyLineBreaker.cpp | 2017 | processLineBreak() | O(n²) | Bucles anidados sin límites |
| Layout.cpp | 2013 | doLayoutWord() | O(n) | Llama a LayoutCache sin verificar tamaño |
| LayoutCache.h | 2018 | getOrCreate() | O(n) | CHAR_LIMIT_FOR_CACHE → procesa directamente |
| LayoutCore.cpp | 2018 | LayoutPiece() | O(n²) | Llama a hb_shape (HarfBuzz) sin límites |
| Hyphenator.cpp | 2015 | hyphenateFromCodes() | O(n²) | Bucles anidados sin límites |
| Measurement.cpp | 2015 | getOffsetForAdvance() | O(n²) | Bucles anidados sin límites |
| Measurement.cpp | 2015 | distributeAdvances() | O(n²) | Bucles anidados sin límites |
| WordBreaker.cpp | 2015 | detectEmailOrUrl() | O(n) | Recorre texto sin límites |
| WordBreaker.cpp | 2015 | findNextBreakInEmailOrUrl() | O(n) | Recorre texto sin límites |
| FontCollection.cpp | 2013 | getGlyphScore() | O(n²) | Llama a hb_shape sin límites |
| FontCollection.cpp | 2013 | itemize() | O(n) | Recorre texto sin límites |
| FontFamily.cpp | 2013 | getClosestMatch() | 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 |
|---|---|---|
| 2013 | FontCollection.cpp, FontFamily.cpp, Layout.cpp creados | El código vulnerable existe desde hace 13 años |
| 2015 | OptimalLineBreaker.cpp, Hyphenator.cpp, Measurement.cpp, WordBreaker.cpp creados | El pipeline de amplificación se completa |
| 2016 | CVE-2016-2414 — DoS en Minikin | Google sabía que Minikin era vulnerable y lo arregló |
| 2017 | GreedyLineBreaker.cpp creado | Se añade una capa más de amplificación |
| 2018 | LineBreaker.cpp, LayoutCore.cpp, LayoutCache.h creados | El pipeline alcanza 10 capas |
| 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 — 31 vectores documentados | Google lo cerró "out of scope" |
| 2026 | Boletín de julio — sin parche | Google 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:
- Los textos largos no se almacenan en caché.
- Cada vez que se renderizan, se procesan desde cero.
- 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).
- 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?
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
- "Algorithmic DoS en libminikin.so – Cómo un texto de 70 KB puede congelar casi cualquier app Android" (24 de junio de 2026)
- Blog de Lostmon
Issues públicos de Google
- #161830416 — crash en LayoutCache (2020)
- #167014931 — crash por Float.POSITIVE_INFINITY (2020)
- #188985643 — SIGSEGV en Samsung (2021)
- #40268980 — ANR en Chrome reportado por Samsung (2023)
- #477202817 — Reporte Chrome VRP (2026)
- #531319203 — system_server boot loop (julio 2026)
Código fuente de AOSP
- frameworks/minikin/ — Repositorio de Minikin
- LineBreaker.cpp
- OptimalLineBreaker.cpp
- GreedyLineBreaker.cpp
- Layout.cpp
- LayoutCache.h
- FontFamily.cpp
Whitepaper completo: 30 de julio de 2026