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) |
S铆 |
✅ ANR de 5+ segundos |
| Firefox (di谩logo externo) |
S铆 |
✅ ANR de 16 segundos (enlace real de Google Drive) |
| Opera / Opera Mini |
S铆 |
✅ Crash con TransactionTooLargeException |
| DuckDuckGo |
S铆 (historial) |
✅ Bucle de cierre permanente |
| Google drive |
S铆 |
⚠️ Potencial |
| WhatsApp (vista previa de enlace) |
S铆 |
⚠️ Potencial |
| Cualquier app con TextView |
S铆 |
✅ 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:
- La v铆ctima hace clic en un archivo HTML alojado en Google Drive.
- Google Drive lanza un
Intent.ACTION_VIEW con una URL malformada.
- La URL bypasea todas las validaciones del navegador porque llega a trav茅s de
callingPackage=com.google.android.apps.docs (certificado por Android).
- La URL corrompe el estado de
TaskPersister en disco.
- SystemUI intenta leer el estado →
DeadObjectException → bucle de cierre.
- 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.
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(...)
Caused by: android.os.DeadObjectException: Transaction failed on small parcel
6. Stack traces nativos (evidencia forense)
Chrome – Men煤 contextual (24 de mayo de 2026)
"main" prio=5 tid=1 Native
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
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
Firefox – Di谩logo de aplicaci贸n externa (10 de junio de 2026)
"main" prio=5 tid=1 Native
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
native: minikin::breakLineOptimal+476 libminikin.so
native: android::nComputeLineBreaks+356 libhwui.so
at org.mozilla.fenix.customtabs.ExternalAppBrowserActivity URL rendering
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);
}
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.
#Lostmon
#Android
#AOSP
#Ciberseguridad
#MobileSecurity
#SystemUI
#libminikin
#STA
#StructuredTextAmplification
#BugBounty
#GoogleVRP
#Xiaomi
#HackerOne
#SaludMental
#Investigaci贸nIndependiente
#BojosXtu