Dieser Beitrag wurde von einem LLM aus dem Englischen ins Deutsche übersetzt. Zum englischen Original
TL;DR: Wenn Sie kein Upstream-OP-TEE neuer als v4.10.0 mit CFG_TZASC_REGION0_SECURE=y oder i.MX-Downstream-OP-TEE neuer als lf-6.12.49_2.2.0 verwenden, ist der Speicher von OP-TEE nicht ordentlich geschützt und kann aus der Normal World kompromittiert werden.
Vor Kurzem erregte ein Commit aus dem Jahr 2024 im OP-TEE-OS-Git-Repository unsere Aufmerksamkeit. Er stellt sicher, dass der TrustZone Address Space Controller (TZASC) auf allen i.MX-8M-SoCs aktiviert ist. Unsere erste Reaktion war gemischt. Kann es wirklich sein, dass OP-TEE vor diesem Commit vergessen hat, seine eigene Speicherregion zu schützen? Oder fügt der Commit nur eine weitere Absicherung gegen bestimmte Fehlkonfigurationen hinzu? Und falls ja, warum wurde kein CVE eingereicht?
Erinnern Sie sich daran, wie die beiden Welten aufgeteilt sind. OP-TEE läuft in der sogenannten Secure World des SoC, während ein Betriebssystem wie Linux in der Normal World läuft (auch Non-Secure World genannt). Die Arm TrustZone erzwingt diese Trennung mithilfe des TZASC.
Es ist also entscheidend, dass die Normal World nicht direkt auf den Speicher der Secure World zugreifen kann. OP-TEE implementiert oft ein Software-TPM oder verwahrt andere geheime Schlüssel. Linux darf nicht darauf zugreifen, weder der Kernel noch root im Userspace.
Dieser Commit ließ uns nicht los, also gruben wir tiefer und probierten es aus. Viele unserer Kunden betreiben Systeme auf i.MX-8M-Basis und legen großen Wert auf Sicherheit. Wir hatten dabei viel Spaß, und so dokumentiert dieser Blogpost unsere Reise.
Wir booteten eines unserer i.MX-8M-Boards, genauer gesagt das NXP i.MX 8M Mini EVK, mit OP-TEE Version 4.1.0, die diesen Commit nicht enthält.
Auf diesem System legt OP-TEE seinen Hauptspeicher an die Adresse 0xbe000000, also versuchten wir, diese Region aus Linux auszulesen.
Der Versuch sollte fehlschlagen, weil Linux in der Normal World läuft und OP-TEE in der Secure World.
Unter Linux erreicht root den gesamten Systemspeicher über /dev/mem, also versuchten wir, auf diese Weise von 0xbe000000 zu lesen.
Es gibt Werkzeuge wie devmem2, aber wir schrieben ein paar Zeilen Python, um flexibel zu bleiben.
Der Zugriff schlug sofort fehl. Gut. Oder?
Nicht so schnell. Der Kernel auf diesem System war mit CONFIG_STRICT_DEVMEM gebaut.
Diese Option beschränkt /dev/mem auf Gerätespeicher, also blockierte sie unseren Lesezugriff, noch bevor die TrustZone überhaupt ins Spiel kam.
Zeit, den Kernel mit deaktiviertem CONFIG_STRICT_DEVMEM neu zu bauen.
Auf dem frisch gebauten Kernel schlug der Lesezugriff weiterhin fehl. Gut, aber waren wir sicher? Als wir genauer nachdachten, wandten wir uns dem Kernel-Log zu und fanden diese Zeile:
OF: reserved mem: 0x00000000be000000..0x00000000bfbfffff (28672 KiB) nomap non-reusable optee_core@be000000
OP-TEE fügt dem an Linux übergebenen Device Tree Reserved-Memory-Knoten hinzu, sodass Linux diese Speicherregion vollständig ignoriert.
Besonders die nomap-Eigenschaft stellt sicher, dass die Region nie abgebildet wird.
Unser Test konnte also nicht funktionieren: Linux selbst hält die OP-TEE-Region außer Reichweite.
Aber das war nicht das, was wir testen wollten. Wir wollten wissen, ob der Zugriff überhaupt möglich ist.
Linux läuft in der Normal World und darf diesen Speicher nicht erreichen, auch dann nicht, wenn es das unbedingt will.
Nach ein paar geänderten Codezeilen hatten wir einen Kernel, der den nomap-Hinweis von OP-TEE ignorierte.
In einem realen Szenario kann ein Angreifer, der den Kernel übernommen hat, diese Einschränkung ebenfalls umgehen, am einfachsten durch das Laden eines eigenen Kernelmoduls.
Zeit, es erneut zu prüfen.
# dump memory
$ python3 devmem.py 0xbe000000 0x100000 > optee_core.bin
# check for a well known string
$ strings optee_core.bin | grep "OP-TEE version"
OP-TEE version: %s
Tatsächlich konnten wir den Speicher von OP-TEE aus Linux lesen! Der Bug ist echt und ernst. Er macht den gesamten Sicherheitsnutzen von OP-TEE zunichte. Ein kompromittiertes Linux-System kann die Secure World erreichen und Schlüsselmaterial extrahieren oder sogar verändern.
Diese Geschichte hätte hier enden können. OP-TEE hat vergessen, die Speicherbeschränkung auf einer bestimmten Plattform zu erzwingen, ein einzelner Commit hat es behoben, und alles ist gut. Nicht schön, aber Bugs passieren. So ist das Leben.
Bei der weiteren Untersuchung fanden wir noch andere Commits rund um den TZASC:
Diese Commit-Nachrichten deuteten auf ein tieferes Problem hin. Die Secure World vor der Normal World zu schützen, erfordert mehr als das korrekte Aktivieren des TZASC. Die Ursache ist, dass der TZASC keine Speicher-Aliase berücksichtigt.
Mit dem passenden Fix stellt OP-TEE sicher, dass seine Speicherregion nicht aus der Normal World erreicht werden kann.
Aber dieser Schutz stützt sich allein auf die Adresse, in unserem Fall 0xbe000000.
Weil der Controller keine Speicher-Aliase berücksichtigt, kann eine andere Adresse auf genau denselben Speicherort zeigen und an der Zugriffsprüfung vorbeischlüpfen.
Hier wird region0 relevant. Der TZASC erlaubt es, Zugriffsberechtigungen für bestimmte Speicheradressbereiche zu konfigurieren. Hat eine angeforderte Adresse keinen konfigurierten Bereich im TZASC, fällt sie auf region0 zurück.
region0 ist eine Speicherbereichskonfiguration, die den gesamten dem SoC bekannten Adressraum abdeckt. Stellen Sie es sich als Platzhalter vor.
Im Reset-Zustand ist region0 nur aus der Secure World zugänglich, sodass der Fallback sicher ist.
Also alles gut? Leider nicht, und genau das behebt der TF-A-Commit.
Wenn auf Ihrem System Speicher-Aliase auftreten können und irgendeine Komponente region0 während des Bootens umkonfiguriert, um Zugriff aus der Normal World zu erlauben, stecken Sie tief in Schwierigkeiten.
Der Konfiguration sieht man die Gefahr nicht an.
Nach dem Upgrade von OP-TEE und TF-A schien alles in Ordnung zu sein.
Der Zugriff auf 0xbe000000 war nicht mehr möglich.
Alles gut, bis uns auffiel, dass die OP-TEE-Konfigurationsoption CFG_TZASC_REGION0_SECURE in unserem Testaufbau deaktiviert war.
CFG_TZASC_REGION0_SECURE prüft, ob region0 sicher konfiguriert ist, was bedeutet, dass nur Zugriff aus der Secure World erlaubt ist.
Sie zu aktivieren, offenbarte die harte Wahrheit.
OP-TEE löste sehr früh beim Start eine Panic aus:
E/TC:0 0 Panic 'region0 is not secure configured, non-secure memory alias access possible!' at core/arch/arm/plat-imx/tzc380.c:217 <imx_configure_tzasc>
Irgendetwas auf unserem System fasste region0 also immer noch an.
Einer Ahnung folgend, schauten wir uns an, was unser Bootloader, U-Boot, tut.
In arch/arm/mach-imx/imx8m/soc.c erlaubt er nicht-sicheren Zugriff auf region0.
void enable_tzc380(void)
{
[...]
/*
* set Region 0 attribute to allow secure and non-secure
* read/write permission. Found some masters like usb dwc3
* controllers can't work with secure memory.
*/
writel(0xf0000000, TZASC_BASE_ADDR + 0x108);
}
Das Entfernen dieses writel() ließ die OP-TEE-Panic verschwinden und bestätigte, dass niemand sonst region0 mehr anfasst.
Dennoch ließ uns die Alias-Frage nicht los. War das ein echtes Problem oder nur ein theoretisches?
Das i.MX 8M Mini Reference Manual zeigte keinen Speicher-Alias für den Bereich, den unser OP-TEE nutzt.
Vielleicht waren wir auch ohne die region0-Fixes sicher?
Deshalb schrieben wir ein winziges Programm, das /dev/mem und /proc/self/pagemap nutzt, um Adressen zu finden, die auf denselben physischen Speicher abgebildet werden.
Es wählt eine Seite, liest deren Inhalt, kippt ein paar hohe Bits der Adresse und prüft, ob die neue Adresse dieselben Bytes zurückgibt.
Der Ansatz erwies sich als weit komplizierter als nötig, sobald er die ersten Aliase gefunden hatte.
Beim Testen des OP-TEE-Bereichs mit deaktiviertem Schutz kam als ein Alias für 0xbe000000 die Adresse 0x1be000000 heraus.
Der DDR-Controller der i.MX-8M-Serie ignoriert also die oberen 32 Bit einer Speicheradresse.
TZASC und DDR-Controller sind sich daher uneinig: Der TZASC sieht zwei nicht zusammenhängende Adressen, während der DDR-Controller sie auf denselben Ort abbildet.
Die TZASC-Einstellungen gelten nicht für den Alias, also fällt der Zugriff auf region0 durch.
Nicht alle oberen Bits sind unter Linux nutzbar, weil die Adresse weiterhin in den konfigurierten Adressraum passen muss. Aber Bit 32 zu setzen, schien einwandfrei zu funktionieren.
Als letzten Test wendeten wir alle Fixes erneut an, ließen aber U-Boot unberührt, sodass es region0 umkonfigurierte, und versuchten, den Speicher von OP-TEE auszulesen:
$ python3 devmem.py 0xbe000000 0x100000 > optee_core.bin
Bus error
$ python3 devmem.py 0x1be000000 0x100000 > optee_core.bin
$ strings optee_core.bin | grep "OP-TEE version"
OP-TEE version: %s
Es funktionierte. Die direkte Adresse war blockiert, aber der Alias glitt glatt durch, und der gesamte Schutz erwies sich als vergeblich.
Sie sind potenziell betroffen, wenn Sie OP-TEE auf einem i.MX-8M-SoC betreiben. Zwei separate Probleme machen die Umgehung möglich, und jedes einzelne reicht aus, um die Isolation zu brechen:
region0 um. Sobald ein Speicher-Alias auf ein offenes region0 durchfällt, erreicht die Normal World den sicheren Speicher. Ab OP-TEE 4.10.0 lässt sich das erkennen.Um Ihr eigenes System zu prüfen, betreiben Sie ein aktuelles OP-TEE mit aktiviertem CFG_TZASC_REGION0_SECURE. Mit dieser Option verifiziert OP-TEE region0 beim Start und löst eine Panic aus, wenn es nicht sicher ist, sodass es auch den Fall abfängt, in dem eine andere Komponente region0 hinter seinem Rücken umkonfiguriert. Die Option ist erst ab OP-TEE 4.10.0 verfügbar.
Wenn Sie U-Boot als Bootloader verwenden, machen Sie in der Zwischenzeit den Commit imx8m: Configure trustzone region 0 for non-secure access rückgängig. Wir haben den Entwicklern einen Hinweis gegeben. Sobald eine bessere Lösung verfügbar ist, vermerken wir sie hier.
Für das i.MX-Downstream-OP-TEE gibt es einen Sonderfall. Wenn Sie es betreiben, verwenden Sie mindestens die Version, die den Commit LFOPTEE-468 core: plat-imx: tzc380: update TZASC configuration enthält.
Leider hat Downstream einen anderen Weg gewählt. Statt darauf zu bestehen, die anderen Komponenten zu reparieren, konfigurieren sie region0 um, um den nicht-sicheren Zugriff zu deaktivieren.
Das überdeckt nur das eigentliche Problem: Andere Softwarekomponenten ändern region0 aus den falschen Gründen.
Unser Hinweis auf der U-Boot-Mailingliste löste eine hitzige Diskussion auf dem OP-TEE-GitHub darüber aus, wie damit umzugehen sei.
Unser wichtigster Rat ist einfach: Aktualisieren Sie OP-TEE, TF-A und Ihren Bootloader regelmäßig, denn sie enthalten wichtige Bugfixes. Leider wurden für diese Probleme keine CVE-Nummern eingereicht, sodass die Chance groß ist, dass viele Nutzer sie übersehen haben. Obwohl wir spät dran sind, haben wir CVE-Nummern beantragt und werden diesen Blogpost aktualisieren, sobald die CVEs zugewiesen sind.
Moderne Computersysteme sind endlos komplex, und selbst gut etablierte Komponenten können seltsame Probleme verbergen. Wir mussten mehrere Schichten abtragen, um das eigentliche Problem vollständig zu bestätigen:
CONFIG_STRICT_DEVMEM blockierte den ersten Test.nomap-Eigenschaft im Device Tree hielt Linux davon ab, die Region überhaupt abzubilden.Aus Sicht von Sicherheit und Code-Audit gilt einmal mehr ein Grundsatz: Misstraue allem. Sogar der Speicherschutz von OP-TEE braucht rigoroses Testen, und wie wir gezeigt haben, müssen Sie sicher sein, dass Sie das Richtige testen. Ein weniger erfahrener Ingenieur hätte womöglich geschlossen, dass alles in Ordnung ist, weil Linux selbst die ersten Zugriffsversuche verweigerte.
Zum Schluss: Danke an Pengutronix für die Behebung des Problems in TF-A und OP-TEE!
Veröffentlicht am
11.06.2026
Kategorie
security
Autoren
Richard Weinberger
sigma star gmbh
Eduard-Bodem-Gasse 6, 1. Stock
6020 Innsbruck | Österreich