<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>tee on SecMate Blog</title><link>https://blog.secmate.dev/tags/tee/</link><description>What we find. What we share.</description><generator>Hugo</generator><language>en-us</language><managingEditor>noreply@blog.secmate.dev//blog.secmate.dev/ (SecMate Team)</managingEditor><lastBuildDate>Sat, 25 Jul 2026 09:06:45 +0000</lastBuildDate><atom:link href="https://blog.secmate.dev/tags/tee/index.xml" rel="self" type="application/rss+xml"/><item><title>Corrupting the Secure World: Three Memory-Safety Flaws in OP-TEE</title><link>https://blog.secmate.dev/posts/optee-vulnerabilities-disclosure/</link><pubDate>Wed, 22 Jul 2026 05:00:00 +0000</pubDate><dc:creator>Maxime Rossi Bellom</dc:creator><dc:creator>Ramtine Tofighi Shirazi</dc:creator><category>Security</category><category>Vulnerability Research</category><category>Embedded</category><guid>https://blog.secmate.dev/posts/optee-vulnerabilities-disclosure/</guid><description>Technical disclosure of three OP-TEE memory-safety vulnerabilities: a SHA-3 accelerated-finalize heap overflow (CVE-2026-40257), an RSASSA PKCS#1 v1.5 underflow (CVE-2026-33662), and an SE050 RSA NOPAD heap underflow. We cover the Arm Trusted Firmware architecture, attack chains across the S-EL0/S-EL1 boundary, OP-TEE mitigations, and the upstream response.</description><content:encoded><![CDATA[<h1 id="corrupting-the-secure-world-three-memory-safety-flaws-in-op-tee">Corrupting the Secure World: Three Memory-Safety Flaws in OP-TEE</h1>
<p>SecMate&rsquo;s automated analysis found three security issues in <a href="https://github.com/OP-TEE/optee_os" rel="noopener noreferrer" target="_blank">OP-TEE</a>
 <a href="#ref1">[1]</a>
, the open-source Trusted OS for Arm TrustZone that is commonly deployed with Arm Trusted Firmware. All three corrupt memory in the privileged OP-TEE core. We reported them against commit <code>f2a7ad0638aeff5243593b33cc56ad064cae7615</code>.</p>
<p>Two received CVEs. The affected SHA-3 path shipped in 13 public OP-TEE releases, the PKCS#1 v1.5 path in 25, and the SE050 RSA NOPAD path in at least 18. The oldest issue had been present for more than six years. SecMate found all three automatically using locally deployable gpt-oss-20b and gpt-oss-120b models.</p>
<p>OP-TEE treated the third finding as a bug because our report did not demonstrate the initial compromise needed to establish a complete compromised-TA attack chain.</p>
<p>Core memory corruption matters because OP-TEE is responsible for keeping secrets isolated from Linux or Android.</p>
<h2 id="why-op-tee-matters">Why OP-TEE matters</h2>
<p>OP-TEE stands for <strong>Open Portable Trusted Execution Environment</strong>. On Arm TrustZone systems, it runs security-sensitive code and data separately from the normal operating system, usually Linux or Android <a href="#ref1">[1]</a>
.</p>
<p>That normal operating system is called the <strong>Rich Execution Environment (REE)</strong>. OP-TEE runs on the protected side of the TrustZone boundary. The separation is meant to keep sensitive data, code, and execution isolated even if software in the normal world is compromised. The GlobalPlatform TEE architecture defines integrity and data confidentiality as core security properties. It also defines mechanisms for Trusted Application code confidentiality and isolation from the REE <a href="#ref2">[2]</a>
. The protected properties include:</p>
<ul>
<li>confidentiality of sensitive data and keys</li>
<li>integrity of code and data during execution</li>
<li>confidentiality of Trusted Application code when provided by the implementation</li>
<li>isolation from the normal operating system</li>
</ul>
<p>OP-TEE is not limited to research platforms. TrustedFirmware.org describes it as widely used and deployed, and its official platform list includes maintained targets from STMicroelectronics, NXP, Texas Instruments, Renesas, Rockchip, and AMD/Xilinx <a href="#ref3">[3]</a>
 <a href="#ref4">[4]</a>
. Other vendors use different secure environments, including Qualcomm TEE, Samsung TEEGRIS, and Apple&rsquo;s Secure Enclave with <code>sepOS</code> <a href="#ref5">[5]</a>
 <a href="#ref6">[6]</a>
 <a href="#ref7">[7]</a>
. A device with a TEE therefore does not necessarily run OP-TEE.</p>
<h2 id="what-arm-trusted-firmware-and-op-tee-actually-are">What Arm Trusted Firmware and OP-TEE actually are</h2>
<p>Arm application processors implement two security states, the <strong>Normal World</strong> and the <strong>Secure World</strong>, selected by the <code>NS</code> bit and enforced in hardware by TrustZone. The idea is to keep a small, high-assurance environment isolated from a large, complex Rich OS such as Linux. Inside each world, the processor still has privilege levels, expressed as exception levels.</p>
<p><a href="https://www.trustedfirmware.org/projects/tf-a/" rel="noopener noreferrer" target="_blank">Arm Trusted Firmware (TF-A)</a>
 <a href="#ref9">[9]</a>
 is a reference implementation of the firmware that commonly runs at the most privileged level, <strong>EL3</strong>. Its <code>BL31</code> runtime acts as the <strong>Secure Monitor</strong> in this deployment model. It handles <code>SMC</code> (Secure Monitor Call) instructions, PSCI power management, world switches, and dispatch to a Trusted OS.</p>
<p><strong>OP-TEE</strong> is the Trusted OS in the deployment shown below. It commonly runs as the <code>BL32</code> secure payload at <strong>S-EL1</strong>, with Trusted Applications (TAs) at <strong>S-EL0</strong>. OP-TEE and TF-A are sibling <a href="https://www.trustedfirmware.org/" rel="noopener noreferrer" target="_blank">TrustedFirmware.org</a>
 projects. When deployed together, TF-A provides the monitor and dispatches calls to OP-TEE. From the Normal World, an application uses the GlobalPlatform TEE Client API. The call crosses into EL3 via <code>SMC</code>, and the monitor hands it to OP-TEE.</p>
<figure class="optee-architecture" role="img" aria-label="Arm TrustZone architecture showing the Normal World, Secure World, TF-A at EL3, OP-TEE core at S-EL1, and Trusted Applications at S-EL0"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 680 540" class="optee-diagram" aria-hidden="true" focusable="false">
  <defs>
    <style>
      .opt-canvas { fill: var(--color-bg, #111827); }
      .opt-txt { fill: var(--color-text, #e5e7eb); font-family: system-ui, sans-serif; }
      .opt-muted { fill: var(--color-text-secondary, #9ca3af); font-family: system-ui, sans-serif; }
      .opt-acc { fill: var(--color-accent, #84cc16); font-family: system-ui, sans-serif; }
      .opt-red { fill: #ef4444; font-family: system-ui, sans-serif; }
      .opt-box { fill: var(--color-bg-card, #1f2937); stroke: var(--color-accent, #84cc16); stroke-width: 2; }
      .opt-monitor { fill: var(--color-bg-card, #1f2937); fill: color-mix(in srgb, var(--color-accent, #84cc16) 12%, var(--color-bg-card, #1f2937)); stroke: var(--color-accent, #84cc16); stroke-width: 2; }
      .opt-vuln { fill: var(--color-bg-card, #1f2937); fill: color-mix(in srgb, #ef4444 12%, var(--color-bg-card, #1f2937)); stroke: #ef4444; stroke-width: 2; }
      .opt-entry { fill: var(--color-bg-card, #1f2937); stroke: #f59e0b; stroke-width: 2; }
      .opt-dashed { fill: none; stroke: var(--color-text-secondary, #9ca3af); stroke-width: 1.5; stroke-dasharray: 5,3; }
      .opt-hw { fill: none; stroke: var(--color-accent, #84cc16); stroke-width: 1.5; stroke-dasharray: 6,3; }
      .opt-line { stroke: var(--color-accent, #84cc16); stroke-width: 1.5; }
      .opt-line-red { stroke: #ef4444; stroke-width: 2; }
      .opt-tz { stroke: var(--color-text-secondary, #9ca3af); stroke-width: 1.5; stroke-dasharray: 6,4; }
      .opt-arrow { fill: var(--color-accent, #84cc16); }
      .opt-arrow-red { fill: #ef4444; }
      .opt-red-ring { stroke: #ef4444; }
    </style>
    <marker id="opt-arrow" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" class="opt-arrow"/>
    </marker>
    <marker id="opt-arrow-red" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" class="opt-arrow-red"/>
    </marker>
  </defs>

  <rect width="680" height="540" rx="6" class="opt-canvas"/>

  <!-- World labels -->
  <text x="182" y="20" text-anchor="middle" font-size="13" font-weight="700" class="opt-acc" letter-spacing="1">NORMAL WORLD (NS=1)</text>
  <text x="497" y="20" text-anchor="middle" font-size="13" font-weight="700" class="opt-red" letter-spacing="1">SECURE WORLD (NS=0)</text>

  <!-- TrustZone boundary -->
  <line x1="340" y1="28" x2="340" y2="262" class="opt-tz"/>

  <!-- EL0 -->
  <text x="14" y="62" font-size="11" font-weight="600" class="opt-muted">EL0</text>
  <rect x="40" y="40" width="285" height="56" rx="4" class="opt-box"/>
  <text x="182" y="62" text-anchor="middle" font-size="14" font-weight="500" class="opt-txt">Client Applications</text>
  <text x="182" y="80" text-anchor="middle" font-size="10" class="opt-muted">GlobalPlatform TEE Client API (libteec)</text>

  <rect x="355" y="40" width="285" height="56" rx="4" class="opt-entry"/>
  <text x="497" y="62" text-anchor="middle" font-size="14" font-weight="500" class="opt-txt">Trusted Applications (TAs)</text>
  <text x="497" y="80" text-anchor="middle" font-size="10" class="opt-muted">User-mode applications signed for the target platform</text>
  <text x="674" y="62" font-size="11" font-weight="600" class="opt-muted" text-anchor="end">S-EL0</text>

  <!-- EL1 -->
  <text x="14" y="142" font-size="11" font-weight="600" class="opt-muted">EL1</text>
  <rect x="40" y="116" width="285" height="62" rx="4" class="opt-box"/>
  <text x="182" y="140" text-anchor="middle" font-size="14" font-weight="500" class="opt-txt">Rich OS (Linux)</text>
  <text x="182" y="158" text-anchor="middle" font-size="10" class="opt-muted">OP-TEE driver, tee-supplicant</text>

  <rect x="355" y="116" width="285" height="62" rx="4" class="opt-vuln"/>
  <text x="497" y="138" text-anchor="middle" font-size="14" font-weight="600" class="opt-txt">OP-TEE OS (Trusted OS)</text>
  <text x="497" y="156" text-anchor="middle" font-size="10" class="opt-muted">core, GP syscalls, libtomcrypt, crypto drivers</text>
  <text x="497" y="170" text-anchor="middle" font-size="10" class="opt-muted">SE050 / CAAM / accelerated paths, S-EL1 heap</text>
  <circle cx="620" cy="138" r="9" fill="none" stroke-width="1.5" class="opt-red-ring"/>
  <text x="620" y="142" text-anchor="middle" font-size="12" font-weight="700" class="opt-red">!</text>
  <text x="674" y="142" font-size="11" font-weight="600" class="opt-muted" text-anchor="end">S-EL1</text>

  <!-- Secure-world attack arrow S-EL0 -> S-EL1 -->
  <line x1="497" y1="96" x2="497" y2="114" class="opt-line-red" marker-end="url(#opt-arrow-red)"/>
  <text x="505" y="110" font-size="9" class="opt-red">GP TEE syscalls</text>

  <!-- EL2 -->
  <text x="14" y="222" font-size="11" font-weight="600" class="opt-muted">EL2</text>
  <rect x="40" y="198" width="285" height="44" rx="4" class="opt-dashed"/>
  <text x="182" y="224" text-anchor="middle" font-size="13" class="opt-muted">Hypervisor (optional)</text>

  <rect x="355" y="198" width="285" height="44" rx="4" class="opt-dashed"/>
  <text x="497" y="224" text-anchor="middle" font-size="13" class="opt-muted">S-EL2 (optional)</text>

  <!-- SMC arrows into monitor -->
  <line x1="182" y1="242" x2="182" y2="277" class="opt-line" marker-end="url(#opt-arrow)"/>
  <line x1="497" y1="242" x2="497" y2="277" class="opt-line" marker-end="url(#opt-arrow)"/>
  <text x="340" y="268" text-anchor="middle" font-size="10" class="opt-muted">SMC / world switch</text>

  <!-- EL3 monitor (spans both worlds) -->
  <text x="14" y="306" font-size="11" font-weight="600" class="opt-muted">EL3</text>
  <rect x="40" y="282" width="600" height="58" rx="4" class="opt-monitor"/>
  <text x="340" y="306" text-anchor="middle" font-size="14" font-weight="600" class="opt-txt">Arm Trusted Firmware (TF-A), Secure Monitor / BL31</text>
  <text x="340" y="324" text-anchor="middle" font-size="10" class="opt-muted">SMC dispatch, TrustZone context switch, OP-TEE SPD, PSCI</text>

  <!-- Hardware -->
  <line x1="40" y1="360" x2="640" y2="360" class="opt-line" stroke-dasharray="10,4"/>
  <rect x="40" y="372" width="600" height="58" rx="4" class="opt-hw"/>
  <text x="340" y="396" text-anchor="middle" font-size="14" font-weight="500" class="opt-txt">Arm Cortex-A SoC, TrustZone (NS bit), MMU, GIC, secure storage</text>
  <text x="340" y="414" text-anchor="middle" font-size="10" class="opt-muted">Boot chain: BL1, BL2, BL31 (TF-A), BL32 (OP-TEE), BL33 (Rich OS)</text>

  <!-- Legend -->
  <rect x="40" y="468" width="14" height="14" rx="2" class="opt-vuln"/>
  <text x="62" y="479" font-size="11" class="opt-muted">OP-TEE core code containing the findings</text>
  <rect x="40" y="490" width="14" height="14" rx="2" class="opt-entry"/>
  <text x="62" y="501" font-size="11" class="opt-muted">Trusted Application caller at S-EL0</text>
  <rect x="400" y="468" width="14" height="14" rx="2" class="opt-monitor"/>
  <text x="422" y="479" font-size="11" class="opt-muted">Arm Trusted Firmware (EL3 monitor)</text>
  <text x="400" y="501" font-size="11" class="opt-muted">Red arrow: syscall path into OP-TEE core</text>
</svg>
</figure>

<p>The boundary that matters here is inside the secure world. Trusted Applications run at S-EL0. The OP-TEE core runs above them at S-EL1 and handles privileged operations such as key storage, cryptographic services, and secure peripherals. A TA can call the core through syscalls, but it must never be able to write core memory.</p>
<p>These three issues break that boundary. The SHA-3 issue starts with an off-by-one. The other two start with unchecked size relationships. In all three, an unsigned subtraction wraps. The resulting size or offset makes <code>memset</code> or <code>memcpy</code> write outside its buffer.</p>
<h2 id="the-three-findings">The three findings</h2>
<table>
<thead>
<tr>
<th>#</th>
<th>Vulnerability</th>
<th>Component</th>
<th>Identifier</th>
<th>OP-TEE severity</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>SHA-3 accelerated-finalize heap overflow</td>
<td><code>core/lib/libtomcrypt/sha3_accel.c</code></td>
<td><a href="https://github.com/OP-TEE/optee_os/security/advisories/GHSA-75x4-j8p9-55qv" rel="noopener noreferrer" target="_blank">CVE-2026-40257</a>
 <a href="#ref10">[10]</a>
</td>
<td>Moderate (5.5)</td>
<td>Fixed in 4.11.0</td>
</tr>
<tr>
<td>2</td>
<td>RSASSA PKCS#1 v1.5 encode underflow</td>
<td><code>core/drivers/crypto/crypto_api/acipher/rsassa.c</code></td>
<td><a href="https://github.com/OP-TEE/optee_os/security/advisories/GHSA-4cf8-v5g3-73gr" rel="noopener noreferrer" target="_blank">CVE-2026-33662</a>
 <a href="#ref11">[11]</a>
</td>
<td>High (7.5)</td>
<td>Fixed in 4.11 (duplicate of an internal finding)</td>
</tr>
<tr>
<td>3</td>
<td>SE050 RSA NOPAD heap underflow</td>
<td><code>core/drivers/crypto/se050/core/rsa.c</code></td>
<td>None (treated as a bug)</td>
<td>n/a</td>
<td>Still present upstream on July 13, 2026</td>
</tr>
</tbody>
</table>
<p>CVE-2026-40257 requires the Arm v8.2 SHA-3 acceleration path. CVE-2026-33662 affects builds registering RSA acceleration through CAAM, HiSilicon, Versal, or SE050, as listed in the OP-TEE advisory. The third issue requires the NXP SE050 RSA driver. These paths are platform-specific, but they ship on production hardware.</p>
<h2 id="scope-and-impact">Scope and impact</h2>
<p>OP-TEE&rsquo;s published security scope excludes a deliberately malicious TA signed with a trusted TA key because the key holder is already part of the trusted computing base <a href="#ref8">[8]</a>
.</p>
<p>An attacker may first compromise a legitimate, signed TA through one or more separate vulnerabilities and then invoke the SE050 bug through that TA. In this chain, the attacker does not need the TA&rsquo;s signing key, and the TA retains its valid signature. Our report demonstrated the resulting controlled write from S-EL0 into S-EL1 core memory, but it did not demonstrate the initial TA compromise. OP-TEE therefore treated the finding as a bug rather than a complete vulnerability report.</p>
<p>Findings 1 and 2 do not have that prerequisite. Valid hash input triggers the first. A signing operation with a sufficiently small RSA modulus reaches the second.</p>
<p>All three are heap writes. Stack protection does not help. ASLR and non-executable data make targeted exploitation harder, but the minimum demonstrated impact for findings 1 and 2 is a secure-world crash. Finding 3 gives the attacker control over both the bytes written and their position relative to the allocation.</p>
<h2 id="finding-1-sha-3-accelerated-finalize-heap-overflow-cve-2026-40257">Finding 1: SHA-3 accelerated finalize heap overflow (CVE-2026-40257)</h2>
<p>OP-TEE has two SHA-3 implementations. The default software implementation in <code>sha3.c</code> accumulates input word by word and is not affected by this issue. The other, <code>sha3_accel.c</code>, is compiled in when a platform enables the Arm v8.2 SHA-3 crypto extensions (<code>CFG_CRYPTO_WITH_CE82=y</code>) and buffers input in blocks. It is the accelerated path that is broken.</p>
<p>The Keccak sponge processes input in blocks of <code>block_size</code> bytes (136 for SHA3-256). <code>sha3_process()</code> is supposed to: finish any partial block, run full blocks through the hardware, then buffer the remainder. The bug is an off-by-one in step two:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cm">/* sha3_accel.c:151 - strict greater-than is the bug */</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">inlen</span> <span class="o">&gt;</span> <span class="n">block_size</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="n">block_count</span> <span class="o">=</span> <span class="n">inlen</span> <span class="o">/</span> <span class="n">block_size</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="nf">crypto_accel_sha3_compress</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="n">in</span><span class="p">,</span> <span class="n">block_count</span><span class="p">,</span> <span class="n">digest_size</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="n">in</span> <span class="o">+=</span> <span class="n">block_count</span> <span class="o">*</span> <span class="n">block_size</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="n">inlen</span> <span class="o">-=</span> <span class="n">block_count</span> <span class="o">*</span> <span class="n">block_size</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="cm">/* lines 159-160 - reached when inlen == block_size */</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nf">memcpy</span><span class="p">(</span><span class="n">md</span><span class="o">-&gt;</span><span class="n">sha3</span><span class="p">.</span><span class="n">sb</span> <span class="o">+</span> <span class="n">md</span><span class="o">-&gt;</span><span class="n">sha3</span><span class="p">.</span><span class="n">byte_index</span><span class="p">,</span> <span class="n">in</span><span class="p">,</span> <span class="n">inlen</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="n">md</span><span class="o">-&gt;</span><span class="n">sha3</span><span class="p">.</span><span class="n">byte_index</span> <span class="o">+=</span> <span class="n">inlen</span><span class="p">;</span>            <span class="cm">/* byte_index becomes block_size */</span>
</span></span></code></pre></div><p>When a TA feeds <em>exactly</em> one block (136 bytes) with an empty buffer, <code>inlen &gt; block_size</code> is false, the hardware compression is skipped, and the whole block is copied into <code>sb</code> with <code>byte_index</code> left equal to <code>block_size</code>. The finalize functions assume the invariant <code>byte_index &lt; block_size</code>. It no longer holds:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="cm">/* sha3_done(), lines 194-195 */</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">buf</span><span class="p">[</span><span class="n">md</span><span class="o">-&gt;</span><span class="n">sha3</span><span class="p">.</span><span class="n">byte_index</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x06</span><span class="p">;</span>                              <span class="cm">/* sb[136], index -&gt; 137 */</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nf">memset</span><span class="p">(</span><span class="n">buf</span> <span class="o">+</span> <span class="n">md</span><span class="o">-&gt;</span><span class="n">sha3</span><span class="p">.</span><span class="n">byte_index</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">block_size</span> <span class="o">-</span> <span class="n">md</span><span class="o">-&gt;</span><span class="n">sha3</span><span class="p">.</span><span class="n">byte_index</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="cm">/* block_size(136) - byte_index(137) underflows to 0xFFFFFFFF */</span>
</span></span></code></pre></div><p><code>block_size - byte_index</code> is <code>136 - 137</code> in unsigned arithmetic, i.e. <code>0xFFFFFFFF</code>. The <code>memset</code> then writes roughly <strong>4 GB of zeroes</strong> starting inside the hash state on the S-EL1 heap. <code>sha3_shake_done()</code> has the identical pattern for SHAKE-128/256.</p>
<p><strong>Attack chain and impact.</strong> The trigger is two ordinary GlobalPlatform calls: <code>TEE_DigestUpdate(op, data, 136)</code> then <code>TEE_DigestDoFinal(...)</code>. No special key material, permissions, or object types are required. The input is valid because hashing a block-sized buffer is normal usage. A perfectly honest TA can therefore crash the secure world without intending to. The write consists of zeroes and spans roughly 4 GB. In practice, it crashes the OP-TEE core and takes down its TAs, secure services, and Normal-World clients. The write offers too little control for a realistic code-execution primitive. The advisory rates it Moderate, CVSS 5.5 (<code>AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H</code>).</p>
<p>We reproduced it with an ASAN harness that <code>#include</code>s the real <code>sha3_accel.c</code>, stubs the hardware compress function, and runs the two-call sequence. ASAN reports a heap-buffer-overflow with the stack pointing straight at <code>sha3_accel.c:195</code>.</p>
<p>Introduced by commit <a href="https://github.com/OP-TEE/optee_os/commit/1478437e65c44163f7c96f8a4c5d1532a9312bc3" rel="noopener noreferrer" target="_blank"><code>1478437e65c4</code></a>
 in March 2023, this vulnerability shipped in 13 public OP-TEE releases, from 3.21.0 through 4.10.0. It remained in the secure-world core for nearly three years before SecMate&rsquo;s automated analysis identified it, and for more than three years before the fix was merged.</p>
<p><strong>The fix.</strong> Change the comparison so a full block is actually compressed, and add a defensive assertion to the finalize paths <a href="#ref10">[10]</a>
:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="ln">1</span><span class="cl"><span class="gd">-	if (inlen &gt; block_size) {
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gd"></span><span class="gi">+	if (inlen &gt;= block_size) {
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="gi"></span> 		block_count = inlen / block_size;
</span></span><span class="line"><span class="ln">4</span><span class="cl"> 		crypto_accel_sha3_compress(state, in, block_count, digest_size);
</span></span><span class="line"><span class="ln">5</span><span class="cl">...
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="gi">+	LTC_ARGCHK(md-&gt;sha3.byte_index &lt; block_size);
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="gi"></span> 	buf[md-&gt;sha3.byte_index++] = 0x06;
</span></span><span class="line"><span class="ln">9</span><span class="cl"> 	memset(buf + md-&gt;sha3.byte_index, 0, block_size - md-&gt;sha3.byte_index);
</span></span></code></pre></div><p>With <code>&gt;=</code>, an exact-block input is consumed by the hardware compress, <code>inlen</code> drops to zero, and <code>byte_index</code> stays at zero. The added <code>LTC_ARGCHK</code> returns <code>CRYPT_INVALID_ARG</code> if the invariant is violated again. The advisory identifies OP-TEE 4.11.0 and later as patched. The workaround is <code>CFG_CRYPTO_WITH_CE82=n</code>.</p>
<h2 id="finding-2-rsassa-pkcs1-v15-encode-underflow-cve-2026-33662">Finding 2: RSASSA PKCS#1 v1.5 encode underflow (CVE-2026-33662)</h2>
<p>This one lives in the generic crypto-driver framework (<code>CFG_CRYPTO_DRIVER=y</code>), which platforms with RSA acceleration use to dispatch signature operations: NXP CAAM (<code>plat-imx</code>, <code>plat-ls</code>), Xilinx Versal, HiSilicon, and SE050 builds.</p>
<p>PKCS#1 v1.5 signature encoding pads the DigestInfo structure up to the modulus size with <code>0xFF</code> bytes. <code>emsa_pkcs1_v1_5_encode()</code> computes how many padding bytes it needs by subtracting from the modulus size, with no lower-bound check:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="cm">/* rsassa.c:48-50 */</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">ps_size</span>  <span class="o">=</span> <span class="n">ssa_data</span><span class="o">-&gt;</span><span class="n">key</span><span class="p">.</span><span class="n">n_size</span> <span class="o">-</span> <span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">ps_size</span> <span class="o">-=</span> <span class="n">ssa_data</span><span class="o">-&gt;</span><span class="n">digest_size</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">ps_size</span> <span class="o">-=</span> <span class="mi">10</span> <span class="o">+</span> <span class="n">hash_oid</span><span class="o">-&gt;</span><span class="n">asn1_length</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">...</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nf">memset</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">UINT8_MAX</span><span class="p">,</span> <span class="n">ps_size</span><span class="p">);</span>          <span class="cm">/* line 75 */</span>
</span></span></code></pre></div><p>With a 256-bit modulus (32 bytes) and SHA-256 (32-byte digest, 9-byte OID), the arithmetic is <code>32 - 3 - 32 - (10 + 9)</code>, which underflows <code>size_t</code> to roughly <code>SIZE_MAX</code>. The <code>memset</code> then writes <code>0xFF</code> across the entire S-EL1 heap until OP-TEE faults. RFC 3447 Section 9.2 mandates the missing size check <a href="#ref12">[12]</a>
. The same file&rsquo;s <code>noasn1</code> and PSS paths already perform their own size validation. This path did not.</p>
<p><strong>Attack chain and impact.</strong> A TA uses a sufficiently small RSA modulus and calls <code>TEE_AsymmetricSignDigest(TEE_ALG_RSASSA_PKCS1_V1_5_SHA256, ...)</code>. The vulnerable core path is reached through that signing operation. Whether an external attacker can reach it depends on how the product exposes the TA to Normal-World clients. Like finding 1, this is a blunt overwrite. It gives an attacker a reliable TEE-wide denial of service, not controlled execution. The OP-TEE advisory rates the issue High, CVSS 7.5 (<code>AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H</code>).</p>
<p>SecMate identified this vulnerability independently before its public disclosure. OP-TEE had already found the issue internally, so the project handled our report as a duplicate and the CVE process was already underway.</p>
<p>This vulnerability was introduced by commit <a href="https://github.com/OP-TEE/optee_os/commit/f5a70e3efb80be4b9bff2c9c811ddc139058e05a" rel="noopener noreferrer" target="_blank"><code>f5a70e3efb80</code></a>
 in February 2020. It remained in OP-TEE for more than six years and shipped in 25 public releases before the fix was merged in April 2026. Public Git history places the first affected release at 3.9.0, although the GitHub advisory lists versions from 3.8.0.</p>
<p><strong>The fix.</strong> The maintainers replaced the chained subtraction with checked subtraction macros that bail out on underflow <a href="#ref11">[11]</a>
:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="ln">1</span><span class="cl"><span class="gd">-	ps_size = ssa_data-&gt;key.n_size - 3;
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gd">-	ps_size -= ssa_data-&gt;digest_size;
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="gd">-	ps_size -= 10 + hash_oid-&gt;asn1_length;
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="gd"></span><span class="gi">+	if (SUB_OVERFLOW(ssa_data-&gt;key.n_size, 3, &amp;ps_size) ||
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="gi">+	    SUB_OVERFLOW(ps_size, ssa_data-&gt;digest_size, &amp;ps_size) ||
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="gi">+	    SUB_OVERFLOW(ps_size, 10 + hash_oid-&gt;asn1_length, &amp;ps_size))
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="gi">+		return TEE_ERROR_BAD_PARAMETERS;
</span></span></span></code></pre></div><p>An encoding size that would make one of the subtractions wrap now returns <code>TEE_ERROR_BAD_PARAMETERS</code>. The patch prevents the memory corruption. It does not enforce the separate PKCS#1 v1.5 requirement that the padding string contain at least eight bytes. The advisory identifies OP-TEE 4.11 and later as patched. The workaround is to disable RSA acceleration.</p>
<h2 id="finding-3-se050-rsa-nopad-controlled-heap-underflow">Finding 3: SE050 RSA NOPAD controlled heap underflow</h2>
<p>The third finding gives an attacker more control than the two CVEs, but OP-TEE treated it as a bug. It is in the NXP SE050 secure-element driver (<code>CFG_NXP_SE05X=y</code>), used on i.MX6/7/8 platforms that pair the SoC with an SE050 element over I²C.</p>
<p><code>decrypt_nopad()</code> and <code>encrypt_nopad()</code> left-pad the caller&rsquo;s input to the modulus size by computing a destination offset with, again, an unchecked unsigned subtraction:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="cm">/* se050/core/rsa.c:356 (and :418) */</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">rsa_len</span> <span class="o">=</span> <span class="nf">crypto_bignum_num_bytes</span><span class="p">(</span><span class="n">key</span><span class="o">-&gt;</span><span class="n">n</span><span class="p">);</span>     <span class="cm">/* e.g. 256 for a 2048-bit key */</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nf">memcpy</span><span class="p">(</span><span class="n">buf</span> <span class="o">+</span> <span class="n">rsa_len</span> <span class="o">-</span> <span class="n">src_len</span><span class="p">,</span> <span class="n">src</span><span class="p">,</span> <span class="n">src_len</span><span class="p">);</span>
</span></span></code></pre></div><p>Neither the generic dispatch layer (<code>crypto_acipher_rsanopad_decrypt</code>, which passes <code>cipher_len</code> straight through) nor the SE050 driver checks that <code>src_len &lt;= rsa_len</code>. If a TA supplies input larger than the modulus, the subtraction computes a destination before the allocation. Forming that pointer is undefined behavior in C. On the affected targets, it produces the following relative out-of-bounds write:</p>
<pre tabindex="0"><code>rsa_len = 256, src_len = 300  -&gt;  256 - 300 = (size_t)-44
memcpy(buf - 44, src, 300)    -&gt;  44 attacker-chosen bytes written BEFORE buf
</code></pre><p><code>buf</code> comes from OP-TEE&rsquo;s BGET-backed mempool. An allocated block&rsquo;s <code>prevfree</code> and <code>bsize</code> metadata sit immediately before its payload, so even a short underflow can overwrite allocator state that <code>mempool_free()</code> later consumes.</p>
<p><strong>Why this one is different.</strong> Findings 1 and 2 overwrite with a fixed byte over an unbounded range, which is useful for a crash but offers little control. This is a <strong>controlled heap-underflow write primitive</strong>: the attacker chooses the underflow distance (<code>src_len - rsa_len</code>) and the bytes copied from <code>src</code>. The primitive can corrupt allocator metadata or an adjacent core object before the affected allocation is freed. We did not build a complete S-EL0-to-S-EL1 exploit, but this gives an attacker far more control than findings 1 and 2, especially on constrained 32-bit targets. We confirmed the out-of-bounds write with an ASAN harness around the real <code>rsa.c</code>.</p>
<p>The vulnerable RSA NOPAD implementation entered the SE050 driver in commit <a href="https://github.com/OP-TEE/optee_os/commit/462192731c921460f35043e04a1aec6074682d6b" rel="noopener noreferrer" target="_blank"><code>462192731c92</code></a>
 in January 2022. It shipped in at least 18 public releases and was still present in upstream commit <a href="https://github.com/OP-TEE/optee_os/commit/611fbaf3415082fe6ae278e5614e0c9608f5ce32" rel="noopener noreferrer" target="_blank"><code>611fbaf34150</code></a>
 on July 13, 2026, four and a half years later.</p>
<p><strong>Why it was treated as a bug.</strong> Reaching this code requires control of a TA that calls <code>TEE_AsymmetricDecrypt(TEE_ALG_RSA_NOPAD, ...)</code> with oversized input. That control could come from a deliberately malicious TA or from compromising a legitimate, signed TA through another vulnerability. The former is outside OP-TEE&rsquo;s threat model; the latter does not require the attacker to possess a TA signing key and can form the first stage of an exploit chain. Our report demonstrated the controlled S-EL1 write but not the initial TA compromise, so OP-TEE treated the finding as a bug.</p>
<p>The fix is to validate <code>src_len &lt;= rsa_len</code> and <code>msg_len &lt;= n_size</code> in the generic dispatch layer, then repeat the check in the driver before <code>memcpy</code>. The HiSilicon HPRE driver already validates input sizes independently and is not affected.</p>
<h2 id="three-old-flaws-found-automatically">Three old flaws, found automatically</h2>
<p>These issues entered OP-TEE in 2020, 2022, and 2023. They survived across separate accelerated implementations and hardware drivers until SecMate&rsquo;s automated analysis found them.</p>
<p>Every issue in our public <a href="https://secmate.dev/disclosures?utm_source=blog&amp;utm_medium=body&amp;utm_campaign=optee-vulnerabilities-disclosure&amp;utm_content=security" rel="noopener noreferrer" target="_blank" data-cta-type="body_disclosures" data-post-slug="optee-vulnerabilities-disclosure" data-post-category="security">disclosure record</a>
 <a href="#ref13">[13]</a>
 was found by the same engine. SecMate runs with locally deployable models, so vulnerability research can stay inside the operator&rsquo;s environment instead of sending source code to an external model service.</p>
<h2 id="disclosure-timeline">Disclosure timeline</h2>
<div style="position: relative; padding-left: 1.5rem; margin: 1.5rem 0;">
  <div style="position: absolute; left: 0.35rem; top: 0.5rem; bottom: 0.5rem; width: 2px; background: var(--color-border);"></div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: var(--color-accent); border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">March 5, 2026</div>
    <div style="color: var(--color-text);">SecMate sent the three issues to the TrustedFirmware security team, with advisories, proof-of-concepts, and patch suggestions. We stated our 90-day coordinated-disclosure policy and offered to coordinate additional time if needed. The public SHA-3 advisory records March 13 as its received-report date.</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: var(--color-text-secondary); border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">March 16, 2026</div>
    <div style="color: var(--color-text);">The OP-TEE security team acknowledged the reports and began analysis.</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: var(--color-text-secondary); border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">March 20, 2026</div>
    <div style="color: var(--color-text);">The PKCS#1 v1.5 underflow was confirmed as a duplicate of an internal finding. The SHA-3 off-by-one was accepted as a security issue. The SE050 RSA NOPAD underflow was classified as a bug because the report did not include the initial compromise needed to demonstrate a complete compromised-TA attack chain.</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: var(--color-text-secondary); border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">March 23, 2026</div>
    <div style="color: var(--color-text);">OP-TEE shared <code>CVE-2026-33662</code> and confirmed the requested credit.</div>
  </div>
  <div style="position: relative; padding-bottom: 1.25rem;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: #22c55e; border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">April 24, 2026</div>
    <div style="color: var(--color-text);">OP-TEE published the fix and advisory for <code>CVE-2026-33662</code> (GHSA-4cf8-v5g3-73gr), identifying version 4.11 and later as patched.</div>
  </div>
  <div style="position: relative;">
    <div style="position: absolute; left: -1.15rem; top: 0.35rem; width: 10px; height: 10px; border-radius: 50%; background: #22c55e; border: 2px solid var(--color-bg);"></div>
    <div style="font-size: 0.75rem; color: var(--color-text-secondary); font-weight: 600;">June 15, 2026</div>
    <div style="color: var(--color-text);">OP-TEE published the fix and advisory for <code>CVE-2026-40257</code> (GHSA-75x4-j8p9-55qv), identifying version 4.11.0 and later as patched and crediting SecMate's automated analyzer.</div>
  </div>
</div>
<h2 id="references">References</h2>
<ul>
<li>
<p><a id="ref1"></a>[1] OP-TEE. &ldquo;About OP-TEE.&rdquo; <em>OP-TEE documentation</em>. <a href="https://optee.readthedocs.io/en/latest/general/about.html" rel="noopener noreferrer" target="_blank">Documentation</a>
</p>
</li>
<li>
<p><a id="ref2"></a>[2] GlobalPlatform. &ldquo;TEE System Architecture v1.3.&rdquo; <a href="https://globalplatform.org/wp-content/uploads/2022/05/GPD_SPE_009-GPD_TEE_SystemArchitecture_v1.3_PublicRelease_signed.pdf" rel="noopener noreferrer" target="_blank">Specification</a>
</p>
</li>
<li>
<p><a id="ref3"></a>[3] OP-TEE. &ldquo;Platforms supported.&rdquo; <em>OP-TEE documentation</em>. <a href="https://optee.readthedocs.io/en/latest/general/platforms.html" rel="noopener noreferrer" target="_blank">Documentation</a>
</p>
</li>
<li>
<p><a id="ref4"></a>[4] TrustedFirmware.org. &ldquo;OP-TEE moving into Trusted Firmware.&rdquo; <a href="https://www.trustedfirmware.org/blog/op-tee-moving-into-trusted-firmware/" rel="noopener noreferrer" target="_blank">Project announcement</a>
</p>
</li>
<li>
<p><a id="ref5"></a>[5] Qualcomm. &ldquo;Qualcomm Linux System Software Architecture.&rdquo; <a href="https://docs.qualcomm.com/bundle/publicresource/topics/80-80022-252/qualcomm-linux-sw-overview.html" rel="noopener noreferrer" target="_blank">Documentation</a>
</p>
</li>
<li>
<p><a id="ref6"></a>[6] Samsung. &ldquo;Samsung TEEGRIS.&rdquo; <em>Samsung Developer</em>. <a href="https://developer.samsung.com/teegris/overview.html" rel="noopener noreferrer" target="_blank">Documentation</a>
</p>
</li>
<li>
<p><a id="ref7"></a>[7] Apple. &ldquo;The Secure Enclave.&rdquo; <em>Apple Platform Security</em>. <a href="https://support.apple.com/guide/security-pdf/the-secure-enclave-sec59b0b31ff/web" rel="noopener noreferrer" target="_blank">Documentation</a>
</p>
</li>
<li>
<p><a id="ref8"></a>[8] OP-TEE. &ldquo;Security scope.&rdquo; <em>OP-TEE documentation</em>. <a href="https://optee.readthedocs.io/en/latest/general/security_scope.html" rel="noopener noreferrer" target="_blank">Documentation</a>
</p>
</li>
<li>
<p><a id="ref9"></a>[9] TrustedFirmware.org. &ldquo;Firmware Design.&rdquo; <em>Trusted Firmware-A documentation</em>. <a href="https://trustedfirmware-a.readthedocs.io/en/stable/design/firmware-design.html" rel="noopener noreferrer" target="_blank">Documentation</a>
</p>
</li>
<li>
<p><a id="ref10"></a>[10] OP-TEE. &ldquo;SHA-3 accelerated finalize heap overflow.&rdquo; <em>GHSA-75x4-j8p9-55qv / CVE-2026-40257</em>. <a href="https://github.com/OP-TEE/optee_os/security/advisories/GHSA-75x4-j8p9-55qv" rel="noopener noreferrer" target="_blank">Advisory</a>
</p>
</li>
<li>
<p><a id="ref11"></a>[11] OP-TEE. &ldquo;RSASSA EMSA-PKCS1-v1_5 underflow in emsa_pkcs1_v1_5_encode().&rdquo; <em>GHSA-4cf8-v5g3-73gr / CVE-2026-33662</em>. <a href="https://github.com/OP-TEE/optee_os/security/advisories/GHSA-4cf8-v5g3-73gr" rel="noopener noreferrer" target="_blank">Advisory</a>
</p>
</li>
<li>
<p><a id="ref12"></a>[12] IETF. &ldquo;PKCS #1: RSA Cryptography Specifications Version 2.1.&rdquo; <em>RFC 3447</em>. <a href="https://www.rfc-editor.org/rfc/rfc3447#section-9.2" rel="noopener noreferrer" target="_blank">Specification</a>
</p>
</li>
<li>
<p><a id="ref13"></a>[13] SecMate. &ldquo;Disclosures.&rdquo; <a href="https://secmate.dev/disclosures?utm_source=blog&amp;utm_medium=body&amp;utm_campaign=optee-vulnerabilities-disclosure&amp;utm_content=security" rel="noopener noreferrer" target="_blank" data-cta-type="body_disclosures" data-post-slug="optee-vulnerabilities-disclosure" data-post-category="security">Disclosure page</a>
</p>
</li>
</ul>
<hr>
<p><em>The SecMate Team</em></p>
]]></content:encoded><media:content url="https://blog.secmate.dev/images/og_image.jpg" medium="image"/></item></channel></rss>