<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="feed.xslt.xml"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Blargh</title>
    <description>The blog where I write down random techy things I've found or done.
</description>
    <link>https://blog.habets.se/</link>
    <atom:link href="https://blog.habets.se/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Thu, 18 Jun 2026 13:57:18 +0000</pubDate>
    <lastBuildDate>Thu, 18 Jun 2026 13:57:18 +0000</lastBuildDate>
    <generator>Jekyll v3.2.1</generator>
    
      <item>
        <title>I fixed shell pipes</title>
        <description>&lt;p&gt;In &lt;a href=&quot;/2022/12/Better-pipes.html&quot;&gt;a previous post&lt;/a&gt; I made pipes in unix shells more reliable. Well, it
had some drawbacks. I’ll summarize the problem, the failed previous version,
and then show the new and improved one.&lt;/p&gt;

&lt;h2 id=&quot;problem-summary&quot;&gt;Problem summary&lt;/h2&gt;

&lt;p&gt;Downstream processes in a unix shell pipe cannot know if the upstream finished
successfully, or exited with an error. This means that it can’t know if it
should “commit” the data it received.&lt;/p&gt;

&lt;p&gt;Example uses:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;pg_dumpall | xz -9 | google_cloud_storage_upload gs://bucket/path/postgres.dump
&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;generate_data | psql --single-transaction
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;In both of these cases you want the right hand side to STOP, and not finalize
the upload or commit the transaction.&lt;/p&gt;

&lt;h2 id=&quot;the-previous-version&quot;&gt;The previous version&lt;/h2&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;goodpipe &lt;span class=&quot;sh&quot;&gt;&amp;lt;&amp;lt;EOF
[
  [&quot;gsutil&quot;, &quot;cat&quot;, &quot;gs://example/input-unsorted.txt&quot;],
  [&quot;sort&quot;, &quot;-S300M&quot;, &quot;-n&quot;],
  [&quot;gzip&quot;, &quot;-9&quot;],
  [&quot;gsutil&quot;, &quot;cp&quot;, &quot;-&quot;, &quot;gs://example/input-sorted-numerically.txt.gz&quot;]
]
EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;This works fine for simple cases, but doesn’t support &lt;code class=&quot;highlighter-rouge&quot;&gt;tee&lt;/code&gt; or per-command
environment variables very well.&lt;/p&gt;

&lt;p&gt;And I don’t want to invent a complex language, so my replacement took a
different path.&lt;/p&gt;

&lt;h2 id=&quot;wp--wrap-pipe&quot;&gt;wp — Wrap Pipe&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/ThomasHabets/wp&quot;&gt;wp on github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;wp&lt;/code&gt; instead wraps the input and/or output with a very minimal encapsulating
protocol. This allows normal data to pass through, but still allows the
downstream to get &lt;code class=&quot;highlighter-rouge&quot;&gt;EOF&lt;/code&gt; as metadata.&lt;/p&gt;

&lt;p&gt;If the data stream ends before receiving the &lt;code class=&quot;highlighter-rouge&quot;&gt;EOF&lt;/code&gt; marker, then &lt;em&gt;do not
commit&lt;/em&gt;. The wrapped downstream child process sees this as &lt;code class=&quot;highlighter-rouge&quot;&gt;stdin&lt;/code&gt; remaining
open, and instead it’s getting terminated with a signal.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;wp&lt;/code&gt; can either encapsulate when it wraps something that &lt;strong&gt;o&lt;/strong&gt;utputs data, with
&lt;code class=&quot;highlighter-rouge&quot;&gt;wp -o&lt;/code&gt;, or decapsulate and receive the EOF marker when it’s handling &lt;strong&gt;i&lt;/strong&gt;nput
data, or both.&lt;/p&gt;

&lt;h3 id=&quot;examples&quot;&gt;Examples&lt;/h3&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;wp -o pg_dumpall | wp -io xz -9 | wp -i google_cloud_storage_upload gs://bucket/path-postgres.dump
&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;wp -o generate_data | wp -i psql --single-transaction
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h3 id=&quot;quick-install-if-you-have-cargo&quot;&gt;Quick install, if you have &lt;code class=&quot;highlighter-rouge&quot;&gt;cargo&lt;/code&gt;&lt;/h3&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cargo install --locked wp-cli
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

</description>
        <pubDate>Thu, 18 Jun 2026 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2026/06/I-fixed-shell-pipes.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2026/06/I-fixed-shell-pipes.html</guid>
        
        
        <category>unix</category>
        
      </item>
    
      <item>
        <title>Quake demos raytraced again</title>
        <description>&lt;p&gt;This is a follow-up to &lt;a href=&quot;https://blog.habets.se/2015/03/Raytracing-Quake-demos.html&quot;&gt;a previous post about raytracing Quake demos&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But first, the money shot:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/2026-06-frame-738-4k.png&quot;&gt;&lt;img src=&quot;/static/2026-06-frame-738.png&quot; alt=&quot;e1m1 flat shaded&quot; /&gt;&lt;/a&gt;
&lt;a href=&quot;/static/2026-06-frame-738-4k-textures.png&quot;&gt;&lt;img src=&quot;/static/2026-06-frame-738-textures.png&quot; alt=&quot;e1m1 with textures&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And &lt;a href=&quot;https://www.youtube.com/watch?v=RoRzPcjjnag&quot;&gt;flat shaded&lt;/a&gt; and
&lt;a href=&quot;https://youtu.be/q9JJL1K9kao&quot;&gt;textured&lt;/a&gt; videos. Youtube is Very Aggressive™
with its compression, so the quality there is not good. For pixel quality the
above images showcase it better.&lt;/p&gt;

&lt;h2 id=&quot;a-new-raytracer&quot;&gt;A new raytracer&lt;/h2&gt;

&lt;p&gt;One of my original reasons for creating the quake demo povray files is that it
was a good source of data for 3D experiments. POV-Ray is a great raytracer,
though entirely CPU (no GPU) and no longer state of the art.&lt;/p&gt;

&lt;p&gt;POV-Ray has plenty of built in options, but takes forever to render the
30-60fps demos I want to play with.&lt;/p&gt;

&lt;p&gt;Also POV-Ray is AGPL now, so nope nope nope nope nope. That’s a dead end.&lt;/p&gt;

&lt;h2 id=&quot;another-ai-detour&quot;&gt;Another AI detour&lt;/h2&gt;

&lt;p&gt;We live in interesting times. We could be living in a time when no two people
are running the same email client, or music player, or shell. There used to be a
barrier to writing these things custom. I know people who &lt;a href=&quot;https://github.com/elves/elvish&quot;&gt;wrote their own
shell&lt;/a&gt; and use it as a daily driver. I wrote &lt;a href=&quot;https://github.com/ThomasHabets/cmdg&quot;&gt;my own email
client&lt;/a&gt;, and use that.&lt;/p&gt;

&lt;p&gt;There are many people out there, me included, who are perfectly able to write
their own shell, but don’t. For the shell, my need is not above my threshold of
putting in the effort. But now? I could, if I ran into more annoyances with
Bash.&lt;/p&gt;

&lt;p&gt;But do &lt;em&gt;you&lt;/em&gt; not like &lt;a href=&quot;https://en.wikipedia.org/wiki/Bash_(Unix_shell)&quot;&gt;Bash&lt;/a&gt;? Just ask the AI to write one &lt;em&gt;exactly&lt;/em&gt; the
way you want it. If it breaks, well you’re the only user and your fingers are
trusted input. “It should be fine” (famous last words)&lt;/p&gt;

&lt;p&gt;If the static site generator I use for this blog (Jekyll) gives me any trouble,
like some Ruby dependency troubleshooting, I’ll replace it with a custom one in
a heartbeat.&lt;/p&gt;

&lt;h2 id=&quot;so-lets-write-a-new-raytracer&quot;&gt;So let’s write a new raytracer&lt;/h2&gt;

&lt;p&gt;I don’t have to “find” the best renderer, anymore. I can just have AI write a
custom one. Oh, but do I have to modify &lt;a href=&quot;https://github.com/ThomasHabets/qpov&quot;&gt;QPov (the demo-to-pov
converter)&lt;/a&gt; to write a new file format? I could ask AI to add other
output format. Or I could have AI write a converter.&lt;/p&gt;

&lt;p&gt;Nah, I’ll just have AI load the existing POV-Ray files full of
includes and macros. Remember, I’m not writing “the perfect general purpose
raytracer”. This is not reusable code. I’m just turning my data into raytraced
files.&lt;/p&gt;

&lt;h2 id=&quot;the-process&quot;&gt;The process&lt;/h2&gt;

&lt;p&gt;I got my initial result in under half an hour. I didn’t save the exact prompts,
but they were as short and vague as this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Write a raytracer in rust for the files in this directory.&lt;/li&gt;
  &lt;li&gt;The output is a garbage image. Fix it.&lt;/li&gt;
  &lt;li&gt;Make it parallel using the &lt;code class=&quot;highlighter-rouge&quot;&gt;rayon&lt;/code&gt; crate.&lt;/li&gt;
  &lt;li&gt;Add optional textures as specified in the input files.&lt;/li&gt;
  &lt;li&gt;I think you got the texture coordinates upside down.&lt;/li&gt;
  &lt;li&gt;Frame 302 has rendering errors. Fix the renderer.&lt;/li&gt;
  &lt;li&gt;Add adaptive antialiasing.&lt;/li&gt;
  &lt;li&gt;It’s a bit slow. Optimize it.&lt;/li&gt;
  &lt;li&gt;Switch to outputting PNG files using the &lt;code class=&quot;highlighter-rouge&quot;&gt;png&lt;/code&gt; crate. Maximum lossless compression.&lt;/li&gt;
  &lt;li&gt;Add some rendering metadata to the output PNG.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Some notable impressive feats:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It fixed the initial garbage by “realizing” that it could render with
POV-Ray, and compare the output. After the initial working version it no
longer needed to do that, and didn’t.&lt;/li&gt;
  &lt;li&gt;When the output was no longer garbage it said that it could “see” a hallway
now, which made it realize it was done.&lt;/li&gt;
  &lt;li&gt;For the frame 302 rendering bug, it rendered 301 and 303 to compare, and
could see a wall disappearing from the rendered output.&lt;/li&gt;
  &lt;li&gt;When working on the problem, it would render an image, and then do image
recognition. Yeah, that’s what I’d do too.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-result&quot;&gt;The result&lt;/h2&gt;

&lt;p&gt;It’s not povray, but it’s fast. The example 4K frame with antiaalias from
earlier took 30 seconds on my laptop (5m39s CPU time). POV-Ray (though
admittedly with much more advanced effects) would probably take &lt;em&gt;days&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I can now iterate on other things, such as a better way to render the sky and
water/lava/slime, and add special effects.&lt;/p&gt;

&lt;p&gt;I can… but it depends on when I have an itch to continue on this project.&lt;/p&gt;

</description>
        <pubDate>Sun, 14 Jun 2026 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2026/06/Quake-demos-raytraced-again.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2026/06/Quake-demos-raytraced-again.html</guid>
        
        
        <category>programming</category>
        
        <category>ai</category>
        
      </item>
    
      <item>
        <title>AI solving problems</title>
        <description>&lt;p&gt;I’ve been able to find some time, lately, to work on my project backlog. And
because it’s 2026, I’ve been using AI as a diligent intern.&lt;/p&gt;

&lt;p&gt;I’ve &lt;a href=&quot;https://blog.habets.se/2022/03/seccomp-unsafe-at-any-speed.html&quot;&gt;ranted before about
seccomp&lt;/a&gt;, but
still used it for a project or two. But then, rarely, it triggered an
unexpected &lt;code class=&quot;highlighter-rouge&quot;&gt;openat&lt;/code&gt;. That’s exactly the kind of I &lt;em&gt;do&lt;/em&gt; want to detect and kill
the binary for, so I don’t just want to allow it. I want to know where it’s
coming from.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;strace&lt;/code&gt; showed it’s trying to read &lt;code class=&quot;highlighter-rouge&quot;&gt;/proc/sys/vm/overcommit_memory&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It’s certainly not my code. But just the Rust transitive dependency tree is
quite a few crates:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cargo tree | sed -r 's/^[^a-z]+//;s/ .*//' | sort -u | wc -l
236
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Step 1 was to run it in &lt;code class=&quot;highlighter-rouge&quot;&gt;gdb&lt;/code&gt;, and reproduce the problem. But it’s a bit
trickier than that, because seccomp fully kills the process, so no backtrace.
And setting breakpoints requires a few more syscalls to work, just for the
process to work under gdb (e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;sigaltstack&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;And turns out some calls fail with &lt;code class=&quot;highlighter-rouge&quot;&gt;EINTR&lt;/code&gt; if running under a debugger.&lt;/p&gt;

&lt;p&gt;Yes, I can fix all these things. But why not put the AI intern on it?&lt;/p&gt;

&lt;h2 id=&quot;ai-coding-detour&quot;&gt;AI coding detour&lt;/h2&gt;

&lt;p&gt;I’ve previously &lt;a href=&quot;https://blog.habets.se/2026/06/Quake-demos-raytraced-again.html&quot;&gt;vibe coded a raytracer&lt;/a&gt; just with the prompt “make
a raytracer for the files in this directory”. The files in question were my
POV-Ray files converted from old Quake demos.&lt;/p&gt;

&lt;p&gt;It worked very well that time (&lt;a href=&quot;https://youtu.be/RoRzPcjjnag&quot;&gt;flat shaded&lt;/a&gt; and
&lt;a href=&quot;https://youtu.be/q9JJL1K9kao&quot;&gt;textured&lt;/a&gt;), so why not?&lt;/p&gt;

&lt;p&gt;(youtube’s compression is Very Aggressive™. The originals look crisp)&lt;/p&gt;

&lt;h2 id=&quot;could-the-ai-solve-it&quot;&gt;Could the AI solve it?&lt;/h2&gt;

&lt;p&gt;Skipping to the end, yeah:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;openat(AT_FDCWD, &quot;/proc/sys/vm/overcommit_memory&quot;, O_RDONLY|O_CLOEXEC)
    = -1 EACCES (Permission denied)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;This is now an error instead of killing the binary.&lt;/p&gt;

&lt;h2 id=&quot;could-i-have-solved-it&quot;&gt;Could I have solved it?&lt;/h2&gt;

&lt;p&gt;Sure. But it reminds me of the well publicised Google interview question “how
many piano tuners are there in New York city?”.&lt;/p&gt;

&lt;p&gt;The best answer I’ve heard is “is it important for the company? — Yes — Then
I’d look it up”. That’s the right answer, of course. There’s &lt;a href=&quot;https://web.archive.org/web/20081220183121/https://hebig.org/blogs/archives/main/000962.php&quot;&gt;another “more
right” answer to the “why are manhole covers round?”
question&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Is it important to know which of my transitive dependencies try to read this
file? Not really. I could blanket allow that file to be read. I know what that
file is, and I actively don’t care if an attacker can read it.&lt;/p&gt;

&lt;p&gt;So without AI I would not have spent more than 30 seconds trying to find out.
And I spent a couple of seconds doing &lt;code class=&quot;highlighter-rouge&quot;&gt;rg overcommit_ ~/.cargo&lt;/code&gt;, only showing
references in jemalloc, which this project &lt;em&gt;doesn’t&lt;/em&gt; transitively depend on.&lt;/p&gt;

&lt;p&gt;So it’s likely somewhere in some C code. Or in the Rust standard library.&lt;/p&gt;

&lt;p&gt;So instead I spent 9 minutes reading up on the QUIC protocol for a
couple of my other projects, while the AI went brrr.&lt;/p&gt;

&lt;h2 id=&quot;the-ai-session&quot;&gt;The AI session&lt;/h2&gt;

&lt;h3 id=&quot;prompt&quot;&gt;Prompt&lt;/h3&gt;
&lt;p&gt;Sometimes the code tries to read &lt;code class=&quot;highlighter-rouge&quot;&gt;/proc/sys/vm/overcommit_memory&lt;/code&gt; readonly and
crashes because seccomp. Why does this happen?&lt;/p&gt;

&lt;h3 id=&quot;output&quot;&gt;Output&lt;/h3&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[… thinking out loud and searching the web before the final answer …]

• It’s glibc malloc, not our code.

  glibc’s Linux malloc has a lazy check_may_shrink_heap() path. When it
  considers shrinking a malloc heap, it checks whether Linux is in strict
  overcommit mode by opening:

  /proc/sys/vm/overcommit_memory

  readonly with O_RDONLY | O_CLOEXEC, reading one byte, then caching the
  result.  Source: glibc malloc-sysdep.h
  (https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/malloc-sysdep.h.html#34),
  called from heap shrink logic in arena.c
  (https://codebrowser.dev/glibc/glibc/malloc/arena.c.html#506).

  It is “sometimes” because that path is lazy and allocation-pattern dependent.
  With multiple worker threads, glibc creates per-thread malloc arenas; later
  frees/trims can hit heap_trim -&amp;gt; shrink_heap -&amp;gt; check_may_shrink_heap. If the
  first time that happens is after seccomp is loaded, our default KillProcess
  action kills the process on openat.

  Best fix is probably not to broadly allow file reads. Add a targeted seccomp
  rule that returns EACCES/EPERM for openat instead of killing, ideally only for
  readonly/CLOEXEC flags. glibc treats open failure as “can’t check, assume no
  strict shrink requirement” and caches that, while Landlock still prevents
  actual filesystem access. Warming this before seccomp is possible but brittle
  because the triggering path is allocator-version and arena-state dependent.

─ Worked for 3m 42s ──────────────────────────────────────────────────────────────
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h3 id=&quot;prompt-1&quot;&gt;Prompt&lt;/h3&gt;
&lt;p&gt;Sounds good. Change it to do that.&lt;/p&gt;

&lt;h3 id=&quot;output-1&quot;&gt;Output&lt;/h3&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;⚠ Heads up, you have less than 25% of your 5h limit left. Run /status for a breakdown.

[uh oh. But to be clear this was far from my first task of the 5h window. In
 fact I have another unrelated task running concurrently]

• Changed src/privs.rs:120 so readonly openat returns EACCES instead of killing
  the process under seccomp.

  The rule is conditional on openat argument 2, masked with O_ACCMODE, being
  O_RDONLY. That covers glibc’s lazy /proc/sys/vm/overcommit_memory probe while
  still leaving write-capable opens under the default KillProcess action. No
  read syscall was added.

  Verified with:

  - cargo check --bins
  - cargo test --bin tarweb-h3
  - cargo build --bin tarweb-h3
  - Live tarweb-h3 --threads 2 + curl -sk --http3 https://[::1]:18083/README.md success
    ful.

─ Worked for 5m 39s ────────────────────────────────────────────────────────────────── 
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h3 id=&quot;final-patch&quot;&gt;Final patch&lt;/h3&gt;

&lt;p&gt;Yeah. Not bad. I can work with this.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gh&quot;&gt;diff --git a/src/privs.rs b/src/privs.rs
index 19b6307..cce5ef6 100644
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;--- a/src/privs.rs
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+++ b/src/privs.rs
&lt;/span&gt;&lt;span class=&quot;gu&quot;&gt;@@ -1,6 +1,6 @@
&lt;/span&gt; use anyhow::{Context, Result, anyhow};
 use caps::CapSet;
&lt;span class=&quot;gd&quot;&gt;-use libseccomp::{ScmpAction, ScmpFilterContext, ScmpSyscall};
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+use libseccomp::{ScmpAction, ScmpArgCompare, ScmpCompareOp, ScmpFilterContext, ScmpSys
&lt;/span&gt;    call};
 use tracing::{info, trace, warn};

 /// Drop privileges to bare minimum.
&lt;span class=&quot;gu&quot;&gt;@@ -127,18 +127,32 @@ fn drop_caps() -&amp;gt; Result&amp;lt;()&amp;gt; {
&lt;/span&gt;     ] {
         f.add_rule(ScmpAction::Allow, ScmpSyscall::from_name(name)?)?;
     }
&lt;span class=&quot;gi&quot;&gt;+    // glibc malloc may lazily probe /proc/sys/vm/overcommit_memory.
+    f.add_rule_conditional(
+        ScmpAction::Errno(libc::EACCES),
+        ScmpSyscall::from_name(&quot;openat&quot;)?,
+        &amp;amp;[ScmpArgCompare::new(
+            2,
+            ScmpCompareOp::MaskedEqual(libc::O_ACCMODE as u64),
+            libc::O_RDONLY as u64,
+        )],
+    )?;
&lt;/span&gt;
     if with_rustls {
         // Rustls does some memory allocation.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;follow-up&quot;&gt;Follow-up&lt;/h2&gt;

&lt;p&gt;So, now it works. But I’d want to adjust it a bit, to not merely return
&lt;code class=&quot;highlighter-rouge&quot;&gt;EACCES&lt;/code&gt; for any probing of files such as &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/passwd&lt;/code&gt; or TLS certificates. I
want that to terminate the binary.&lt;/p&gt;

&lt;p&gt;For merely denying filesystem access on a per path prefix basis I use landlock,
but that doesn’t kill the binary.&lt;/p&gt;

&lt;p&gt;I’m not aware of a way for seccomp to block specific paths (again, seccomp
sucks), but maybe the AI knows? But oh no:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;╭─────────────────────────────────────────────────────────────────────────────────╮
     […]
│  5h limit:             [░░░░░░░░░░░░░░░░░░░░] 0% left (resets 19:26)            │
│  Weekly limit:         [██████████░░░░░░░░░░] 48% left (resets 16:12 on 18 Jun) │
╰─────────────────────────────────────────────────────────────────────────────────╯
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;So maybe it’s time to go outside in the nice weather? “Touch grass”, as the kids say.&lt;/p&gt;

</description>
        <pubDate>Sat, 13 Jun 2026 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2026/06/AI-solving-problems.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2026/06/AI-solving-problems.html</guid>
        
        
        <category>programming</category>
        
        <category>ai</category>
        
      </item>
    
      <item>
        <title>RustRadio UI improved</title>
        <description>&lt;p&gt;This is just a short followup to the last RustRadio post. If you came for more
&lt;a href=&quot;https://blog.habets.se/2026/05/Everything-in-C-is-undefined-behavior.html&quot;&gt;rants about
C&lt;/a&gt;,
you’ll be disappointed.&lt;/p&gt;

&lt;p&gt;I’ve never been that interested in writing UI code, including HTML. You can see
the “programmer art” in the screenshots linked from
&lt;a href=&quot;https://www.habets.pp.se/&quot;&gt;www.habets.pp.se&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And then the slightly different &lt;a href=&quot;https://www.habets.pp.se/synscan/&quot;&gt;tech
section&lt;/a&gt;, that doesn’t serve much of a
purpose now that we have github.&lt;/p&gt;

&lt;p&gt;I’ve not been happier with GTK, QT, and the others either.&lt;/p&gt;

&lt;p&gt;But &lt;a href=&quot;https://github.com/ThomasHabets/rustradio&quot;&gt;RustRadio&lt;/a&gt; needs a UI.&lt;/p&gt;

&lt;p&gt;I feel like the browser is the most stable and portable UI. So I’d already
decided on that. So now I have to manually do a bunch of
&lt;a href=&quot;https://en.wikipedia.org/wiki/Document_Object_Model&quot;&gt;DOM&lt;/a&gt; manipulation, to
create an interactive UI? Or worse, learn the React/Angular/Whatever flavor of
the day, that will be obsolete by tomorrow afternoon? Gag me with a spoon.&lt;/p&gt;

&lt;h2 id=&quot;llm-to-the-rescue&quot;&gt;LLM to the rescue&lt;/h2&gt;

&lt;p&gt;For now I’m just continuing to focus on the SDR and architectural parts of
RustRadio, and I’m letting the LLM-written code do the HTML manipulation.&lt;/p&gt;

&lt;p&gt;Yeah, it’s kinda vibe coding. But doesn’t use &lt;code class=&quot;highlighter-rouge&quot;&gt;unsafe&lt;/code&gt;, and it demonstrably
outputs what I want (I mean, sure it may require some follow-up prompts), so
who cares?&lt;/p&gt;

&lt;p&gt;The vibe coding is isolated to the files doing the drawing. If I want to
artisanally craft better code in the future, that’s the file that needs to be
rewritten. Until then, it works.&lt;/p&gt;

&lt;h2 id=&quot;the-demo-in-all-its-glory&quot;&gt;The demo, in all its glory&lt;/h2&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/7k0JNT6itaI&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;h2 id=&quot;you-can-run-the-demo-too&quot;&gt;You can run the demo too.&lt;/h2&gt;

&lt;p&gt;See the &lt;a href=&quot;https://github.com/ThomasHabets/ruwasm&quot;&gt;quick start instructions in the ruwasm
repo&lt;/a&gt; for how to run this UI live with
an RTL-SDR.&lt;/p&gt;

</description>
        <pubDate>Tue, 26 May 2026 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2026/05/RustRadio-UI-improved.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2026/05/RustRadio-UI-improved.html</guid>
        
        
        <category>radio</category>
        
        <category>rustradio</category>
        
        <category>programming</category>
        
        <category>web</category>
        
        <category>sdr</category>
        
        <category>wasm</category>
        
      </item>
    
      <item>
        <title>Everything in C is undefined behavior</title>
        <description>&lt;p&gt;If he had been a programmer, &lt;a href=&quot;https://en.wikiquote.org/wiki/Cardinal_Richelieu&quot;&gt;Cardinal Richelieu&lt;/a&gt; would have said
“Give me six lines written by the hand of the most expert C programmer in the
world, and I will find enough in them to trigger undefined behavior”.&lt;/p&gt;

&lt;p&gt;Nobody can write correct C, or C++. And I say that as someone who’s written C
and C++ on an almost daily basis for about 30 years. I listen to C++ podcasts.
I watch C++ conference talks. I enjoy reading and writing C++.&lt;/p&gt;

&lt;p&gt;C++ has served us well, but it’s 2026, and the environment of 1985 (C++) or
1972 (C) is not the environment of today.&lt;/p&gt;

&lt;p&gt;I’m definitely not the first to say this. I remember reading a post by someone
prominent about a decade ago saying that a good case can be made that use of
C++ is a &lt;a href=&quot;https://en.wikipedia.org/wiki/Sarbanes%E2%80%93Oxley_Act&quot;&gt;SOX&lt;/a&gt; violation. And while I was not onboard with the rest of
their rant (nor their confusion about “its” vs “it’s”), I never disagreed about
that point.&lt;/p&gt;

&lt;p&gt;With time I found it to be more and more true. WAY more things are
undefined behavior (UB) than you’d expect.&lt;/p&gt;

&lt;p&gt;Everyone knows that double-free, use after free, accessing outside the bounds
of an object (e.g. array), and accessing uninitialized memory is UB. After all,
C &amp;amp; C++ are not memory safe languages. And yet we as an industry seem to be
unable to stop making even those mistakes over and over.&lt;/p&gt;

&lt;p&gt;But there’s more. More subtle. More illogical.&lt;/p&gt;

&lt;h2 id=&quot;its-not-about-optimizations&quot;&gt;It’s not about optimizations&lt;/h2&gt;

&lt;p&gt;Some people seem to think that as long as they don’t compile with optimizations
turned on, undefined behavior can’t hurt them. They believe that the compiler
is somehow being deliberately hostile, going “AHA! UB! I can do whatever I want
here!”, and without optimizations turned on it won’t.&lt;/p&gt;

&lt;p&gt;This is incorrect.&lt;/p&gt;

&lt;p&gt;UB doesn’t mean that the compiler can take advantage of your sloppiness. UB
means that the compiler can assume that your code is valid. It means that the
intention of your code that’s oh so obvious when read by a human, &lt;em&gt;doesn’t even
have a way to be expressed&lt;/em&gt; between compiler stages or modules.&lt;/p&gt;

&lt;p&gt;UB means that the compiler doesn’t even have to implement some special cases in
its code generation, because they “can’t happen”.&lt;/p&gt;

&lt;p&gt;The compiler, and really the underlying hardware too, is playing a game of
telephone with your UB intentions. It may end up with what you wanted, but
there’s no guarantee for now or in the future.&lt;/p&gt;

&lt;h2 id=&quot;ub-is-everywhere&quot;&gt;UB is everywhere&lt;/h2&gt;

&lt;p&gt;The following is not an attempt at enumerating all the UB in the world. It’s
merely making the case that UB is everywhere, and if nobody can do it right,
how is it even fair to blame the programmer? My point is that &lt;strong&gt;ALL&lt;/strong&gt;
nontrivial C and C++ code has UB.&lt;/p&gt;

&lt;h3 id=&quot;accessing-an-object-which-is-not-correctly-aligned&quot;&gt;Accessing an object which is not correctly aligned&lt;/h3&gt;

&lt;p&gt;As an example of this, take this code:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;If this function is called with a pointer not correctly aligned (probably
meaning on an address that’s a multiple of &lt;code class=&quot;highlighter-rouge&quot;&gt;sizeof(int)&lt;/code&gt;, but who knows), this
is UB. C23 6.3.2.3.&lt;/p&gt;

&lt;p&gt;On Linux Alpha, in &lt;em&gt;some&lt;/em&gt; cases this would merely trap to the kernel, which
would software emulate what you intended. In other cases it would (probably)
crash your program with a SIGBUS.&lt;/p&gt;

&lt;p&gt;On SPARC it would cause a SIGBUS.&lt;/p&gt;

&lt;p&gt;Sure, on x86/amd64 (henceforth just “x86”) this is likely fine. Hell, it’s
probably even an atomic read. x86 is famously extremely forgiving about cache
coherency subtleties.&lt;/p&gt;

&lt;p&gt;So here we have three cases:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;kernel gave a helping hand (Alpha for &lt;em&gt;some&lt;/em&gt; loads)&lt;/li&gt;
  &lt;li&gt;crash (other Alpha loads, and SPARC)&lt;/li&gt;
  &lt;li&gt;not a problem (x86)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What about ARM, RISC-V, and others? What about future architectures? A future
architecture could even have special &lt;code class=&quot;highlighter-rouge&quot;&gt;int-pointer registers&lt;/code&gt; that do not
populate the lowest bits, because such pointers cannot exist.&lt;/p&gt;

&lt;p&gt;Even if it works, maybe the compiler one day changes from using one load
instruction to another, and suddenly that’s no longer fixed up by the kernel.&lt;/p&gt;

&lt;p&gt;Because &lt;em&gt;the compiler is not obligated to generate assembly instructions that
work on unaligned pointers&lt;/em&gt;. Because it’s UB.&lt;/p&gt;

&lt;p&gt;Or how about this:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;set_it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;atomic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;atomic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Is this operation atomic when the object is not correctly aligned? That’s the
wrong question to ask. &lt;a href=&quot;https://en.wikipedia.org/wiki/Mu_(negative)&quot;&gt;Mu&lt;/a&gt;, unask the question. It’s UB. (but also yes, in
practice this can easily be an atomicity problem)&lt;/p&gt;

&lt;p&gt;If you want to get even more convinced, you can try thinking about what happens
if an object you thought you were reading atomically spans &lt;a href=&quot;https://en.wikipedia.org/wiki/Memory_paging&quot;&gt;pages&lt;/a&gt;. But
don’t think too much about it, or you may conclude that “it’s fine”. It’s not.
It’s UB.&lt;/p&gt;

&lt;h3 id=&quot;actually-it-was-ub-even-before-that&quot;&gt;Actually, it was UB even before that&lt;/h3&gt;

&lt;p&gt;Don’t blame the &lt;code class=&quot;highlighter-rouge&quot;&gt;foo()&lt;/code&gt; function, above. The act of dereferencing the pointer
wasn’t the problem. Merely &lt;em&gt;creating&lt;/em&gt; the pointer was enough to be a problem.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;parse_packet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint8_t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;magic_intp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;// UB!
&lt;/span&gt;        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;magic_raw&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;magic_intp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// Probably crashes on SPARC.
&lt;/span&gt;        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;magic&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ntohl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;magic_raw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// this is fine, at least.
&lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;…&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;That cast is the problem, not &lt;code class=&quot;highlighter-rouge&quot;&gt;foo()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It’s perfectly valid for the compiler to assign specific meaning, such as
garbage collection or security tagging bits, to the lower bits of an &lt;code class=&quot;highlighter-rouge&quot;&gt;int*&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;isxdigit-on-char-input&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;isxdigit()&lt;/code&gt; on &lt;code class=&quot;highlighter-rouge&quot;&gt;char&lt;/code&gt; input&lt;/h3&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isxdigit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;isxdigit()&lt;/code&gt; is a simple function that takes a character and returns &lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt; if
it’s a hex digit. 0-9 or a-f. It can also take the value &lt;code class=&quot;highlighter-rouge&quot;&gt;EOF&lt;/code&gt;. Uh, ok. What
value is &lt;code class=&quot;highlighter-rouge&quot;&gt;EOF&lt;/code&gt;? Per &lt;a href=&quot;https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3088.pdf&quot;&gt;C23&lt;/a&gt; 7.4p1 we know it’s an &lt;code class=&quot;highlighter-rouge&quot;&gt;int&lt;/code&gt;, and we can infer
that it’s not representable by &lt;code class=&quot;highlighter-rouge&quot;&gt;unsigned char&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;isxdigit()&lt;/code&gt; therefore takes an &lt;code class=&quot;highlighter-rouge&quot;&gt;int&lt;/code&gt;, not a &lt;code class=&quot;highlighter-rouge&quot;&gt;char&lt;/code&gt;. All values of &lt;code class=&quot;highlighter-rouge&quot;&gt;char&lt;/code&gt; fit
inside &lt;code class=&quot;highlighter-rouge&quot;&gt;int&lt;/code&gt;, so we should be fine. Casting from &lt;code class=&quot;highlighter-rouge&quot;&gt;char&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;int&lt;/code&gt; fits, so per
section 6.3.1.3 we’re fine, right?&lt;/p&gt;

&lt;p&gt;No. Because if &lt;code class=&quot;highlighter-rouge&quot;&gt;bar()&lt;/code&gt; is called with a value other than 0-127, &lt;strong&gt;and&lt;/strong&gt; on your
architecture &lt;code class=&quot;highlighter-rouge&quot;&gt;char&lt;/code&gt; is &lt;code class=&quot;highlighter-rouge&quot;&gt;signed&lt;/code&gt; (implementation defined, per 6.2.5, paragraph
20 in &lt;a href=&quot;https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3088.pdf&quot;&gt;C23&lt;/a&gt;), then the integer value ends up negative.&lt;/p&gt;

&lt;p&gt;And the following is a valid implementation of &lt;code class=&quot;highlighter-rouge&quot;&gt;isxdigit()&lt;/code&gt;, that would cause a
read of who-knows-what memory. It could even be I/O mapped memory, triggering
things to happen that is more than merely getting a random value or crash. It
could cause the motor to start. Less likely in an application running in a
desktop operating system than in an embedded system, sure. But there are user
space network drivers (for performance), so even user space won’t protect you.&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isxdigit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;some_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h3 id=&quot;casting-from-float-to-int&quot;&gt;Casting from &lt;code class=&quot;highlighter-rouge&quot;&gt;float&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;int&lt;/code&gt;&lt;/h3&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;milliseconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;seconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seconds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* WRONG */&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* WRONG separately (signed overflow is UB) */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;When a finite value of real floating type is converted to an integer type[…]If
the value of the integral part cannot be represented by the integer type, the
behavior is undefined.
— 6.3.1.4
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;And, by omission, it’s also UB if the float is a non-finite value.&lt;/p&gt;

&lt;p&gt;So how do you compare a float to &lt;code class=&quot;highlighter-rouge&quot;&gt;INT_MAX&lt;/code&gt;? Do you cast the float to
&lt;code class=&quot;highlighter-rouge&quot;&gt;int&lt;/code&gt;? No, that’s the UB you want to avoid. So you cast &lt;code class=&quot;highlighter-rouge&quot;&gt;INT_MAX&lt;/code&gt; to
float? How do you know it can be represented exactly? Maybe casting &lt;code class=&quot;highlighter-rouge&quot;&gt;INT_MAX&lt;/code&gt;
to &lt;code class=&quot;highlighter-rouge&quot;&gt;float&lt;/code&gt; rounds to a value not representable in &lt;code class=&quot;highlighter-rouge&quot;&gt;int&lt;/code&gt;, and your
comparison becomes non-representative?&lt;/p&gt;

&lt;p&gt;Maybe the following works? You’ll miss out on representing some really high
values, but maybe that’s OK?&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;milliseconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;seconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ftmp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;seconds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isfinite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ftmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// or other error reporting.
&lt;/span&gt;                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INT_MIN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ftmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// or other error reporting.
&lt;/span&gt;                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INT_MAX&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ftmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// or other error reporting.
&lt;/span&gt;                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Now safe to convert.
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ftmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INT_MAX&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// or other error reporting.
&lt;/span&gt;                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Now safe to add.
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;I just wanted to convert a float to an int. :-(&lt;/p&gt;

&lt;p&gt;I bet there’s lots of code out there that take a value in seconds, and convert
it to integer milliseconds, by just multiplying and casting.&lt;/p&gt;

&lt;h3 id=&quot;object-at-address-zero&quot;&gt;Object at address zero&lt;/h3&gt;

&lt;p&gt;Most programmers won’t have to deal with this, but I don’t think there’s any C
standards compliant way in practice to put an object at address zero. This can
come up in OS kernel and embedded coding.&lt;/p&gt;

&lt;p&gt;By 6.3.2.3 an integer constant zero (which is convertible to a pointer) and
&lt;code class=&quot;highlighter-rouge&quot;&gt;nullptr&lt;/code&gt; are the “null pointer constant” (which I’ll just call &lt;code class=&quot;highlighter-rouge&quot;&gt;NULL&lt;/code&gt;). &lt;a href=&quot;https://c-faq.com/null/confusion4.html&quot;&gt;C
doesn’t specify that the actual pointer &lt;code class=&quot;highlighter-rouge&quot;&gt;NULL&lt;/code&gt; points addr machine address
zero&lt;/a&gt;, because the C standard only talks of the C abstract machine, not about
hardware.&lt;/p&gt;

&lt;p&gt;All C guarantees is that if you &lt;em&gt;compare&lt;/em&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;NULL&lt;/code&gt; to zero you’ll see them equal.
But for all you know that’s because the zero is converted to the native
platform’s &lt;code class=&quot;highlighter-rouge&quot;&gt;NULL&lt;/code&gt;, which happens to be &lt;code class=&quot;highlighter-rouge&quot;&gt;0xffff&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It also explicitly says that dereferencing a null pointer, no matter what the
value, is undefined behavior. It’s &lt;em&gt;the&lt;/em&gt; example of UB under 3.4.3.&lt;/p&gt;

&lt;p&gt;This also means that you can’t assume that &lt;code class=&quot;highlighter-rouge&quot;&gt;memset(&amp;amp;ptr, 0, sizeof(ptr));&lt;/code&gt; will
create a &lt;code class=&quot;highlighter-rouge&quot;&gt;NULL&lt;/code&gt; pointer! You cannot initialize your structs this way and assume
member pointers are &lt;code class=&quot;highlighter-rouge&quot;&gt;NULL&lt;/code&gt;! And this &lt;em&gt;does&lt;/em&gt; apply to most programmers.&lt;/p&gt;

&lt;p&gt;And yes, &lt;a href=&quot;https://c-faq.com/null/machexamp.html&quot;&gt;some historic machines used non-zero NULL pointers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But let’s say you have a modern machine, where &lt;code class=&quot;highlighter-rouge&quot;&gt;NULL&lt;/code&gt; is a pointer to address
zero, and you actually have an object there.&lt;/p&gt;

&lt;p&gt;Again, C 6.3.2.3 says that &lt;code class=&quot;highlighter-rouge&quot;&gt;NULL&lt;/code&gt; compares unequal to “any object or function”.
So this is UB:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func_ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;func_ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;C says “there is no function there”. For all you know the compiler has no
internal way to even express your intention here. You may argue that “but
surely it’ll just emit a call instruction to the bit pattern of all zeroes?
Nothing else seems reasonable.&lt;/p&gt;

&lt;p&gt;What is “all zeroes”, though? On 16bit x86, is it &lt;code class=&quot;highlighter-rouge&quot;&gt;0000:0000&lt;/code&gt;? Is it &lt;code class=&quot;highlighter-rouge&quot;&gt;CS:0000&lt;/code&gt;?&lt;/p&gt;

&lt;h3 id=&quot;variable-arguments-and-types-eg-printf-with-ld-instead-of-lld&quot;&gt;Variable arguments and types (e.g. printf with &lt;code class=&quot;highlighter-rouge&quot;&gt;%ld&lt;/code&gt; instead of &lt;code class=&quot;highlighter-rouge&quot;&gt;%lld&lt;/code&gt;)&lt;/h3&gt;

&lt;p&gt;This is UB:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;execl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/bin/sh&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;sh&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;-c&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;     &lt;span class=&quot;cm&quot;&gt;/* WRONG */&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;execl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/bin/sh&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;sh&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;-c&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;     &lt;span class=&quot;cm&quot;&gt;/* WRONG */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;This is not:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;execl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/bin/sh&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;sh&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;-c&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;date&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Because the argument needs to be a pointer, and the &lt;code class=&quot;highlighter-rouge&quot;&gt;NULL&lt;/code&gt; macro may be
misinterpreted as an integer zero.&lt;/p&gt;

&lt;p&gt;Similarly, this is UB:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;uint64_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blah&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%ld&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blah&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;  &lt;span class=&quot;cm&quot;&gt;/* WRONG */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;It needs to be:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;uint64_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blah&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%&quot;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PRIu64&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;blah&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;So how do you print an &lt;code class=&quot;highlighter-rouge&quot;&gt;uid_t&lt;/code&gt;? Well, you could cast them to &lt;code class=&quot;highlighter-rouge&quot;&gt;uintmax_t&lt;/code&gt; and
print them using &lt;code class=&quot;highlighter-rouge&quot;&gt;PRIuMAX&lt;/code&gt;. But is &lt;code class=&quot;highlighter-rouge&quot;&gt;uid_t&lt;/code&gt; even unsigned? Oh well, worst case
you get a nonsense value printed instead of &lt;code class=&quot;highlighter-rouge&quot;&gt;-1&lt;/code&gt;, I guess.&lt;/p&gt;

&lt;h2 id=&quot;divide-by-zero-is-ub&quot;&gt;Divide by zero is UB&lt;/h2&gt;

&lt;p&gt;Sure, you probably knew this. But did you consider the security aspects of it?
It’s not rare for the denominator to come from untrusted input.&lt;/p&gt;

&lt;p&gt;And there’s so much more. The C23 standard contains 283 uses of the word
“undefined”. And that’s not even including the things that are undefined by
omission.&lt;/p&gt;

&lt;h2 id=&quot;bonus-non-ub&quot;&gt;Bonus non-UB&lt;/h2&gt;

&lt;p&gt;Nobody can apply integer promotion rules at code skimming speeds. &lt;strong&gt;Nobody&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This post is already long enough, but as a start:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zero&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;overflowed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// overflowed is set to zero, not one.
&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;uint64_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;// Bonus UB(?)
// b is now 18446744071562067968 (ffffffff80000000), not 2147483648 (0x80000000).
// even with all our variables unsigned.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;llms-are-better-than-us-at-this&quot;&gt;LLMs are better than us at this&lt;/h2&gt;

&lt;p&gt;Point an LLM at ANY C code, asking it to find UB, and it will. And it’ll be
right almost all the time, nowadays.&lt;/p&gt;

&lt;p&gt;I felt a bit bad after it correctly found ones in my code, so I thought I’d
point it at the mature and pedantically written OpenBSD. I just picked the
first tool I could think of, &lt;code class=&quot;highlighter-rouge&quot;&gt;find&lt;/code&gt;, and it spit out a bunch.&lt;/p&gt;

&lt;p&gt;I sent the project a patch for &lt;a href=&quot;https://marc.info/?t=177910871100018&amp;amp;r=1&amp;amp;w=2&quot;&gt;an out of bounds write&lt;/a&gt; (and also
for &lt;a href=&quot;https://marc.info/?t=177910871100010&amp;amp;r=1&amp;amp;w=2&quot;&gt;a non-UB logic bug&lt;/a&gt;). I didn’t send them patches for the UB
that was left and right, partly because the OpenBSD project has not been very
receptive in the past for bug reports, my sense of “this is probably fine, in
practice”, and that if OpenBSD wants to weed out UB from their code base, then
that’s a major project that should be done in a better way than me just being
the middle man between the LLM and them for a patch here and there.&lt;/p&gt;

&lt;p&gt;I’ve seen several people complain that “nobody knows how to code C except me”.
And they’re only wrong by one person.&lt;/p&gt;

&lt;h2 id=&quot;so-what-do-we-do-now&quot;&gt;So what do we do now?&lt;/h2&gt;

&lt;p&gt;We can’t just throw away our C and C++ code bases. But leaving them inherently
broken is also not an option.&lt;/p&gt;

&lt;p&gt;We need some way of fixing UB at scale, without committing AI slop nor
overwhelming human reviewers.&lt;/p&gt;

&lt;p&gt;This too is not a new opinion, nor a great revelation.&lt;/p&gt;

&lt;p&gt;But yes, writing C or C++ in 2026 without an LLM supervising you for UB should
probably be seen as a SOX violation, and just plain irresponsible. If OpenBSD
people can’t find these problems given 30+ years, what chance do the rest of us
have?&lt;/p&gt;

&lt;p&gt;It may not scale to large code bases, but for my own projects I’ve asked the
LLM to find UB, if necessary explain it, and fix it. And then stare at the
output until I can confirm the issue and the fix.&lt;/p&gt;

&lt;p&gt;A problem with this is that in order to confirm the findings, you’ll need an
expert human. But generally expert humans are busy doing other things. This is
janitor work, but too subtle to leave to the junior programmers who have
traditionally been assigned janitor work.&lt;/p&gt;

&lt;h2 id=&quot;related&quot;&gt;Related&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.habets.se/2022/10/No-way-to-parse-integers-in-C.html&quot;&gt;No way to parse integers in C&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.habets.se/2022/11/Integer-handling-is-broken.html&quot;&gt;Integer handling is broken&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://pooladkhay.com/posts/first-kernel-patch/&quot;&gt;UB in the Linux kernel&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.123microcontroller.com/en/cpp-integer-promotion/&quot;&gt;Integer promotion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This blog post was &lt;a href=&quot;https://news.ycombinator.com/item?id=48203698&quot;&gt;discussed on
hackernews&lt;/a&gt;.&lt;/p&gt;

</description>
        <pubDate>Tue, 19 May 2026 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2026/05/Everything-in-C-is-undefined-behavior.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2026/05/Everything-in-C-is-undefined-behavior.html</guid>
        
        
        <category>programming</category>
        
        <category>security</category>
        
      </item>
    
      <item>
        <title>Quantum safe amateur radio secure shell</title>
        <description>&lt;p&gt;I’ve &lt;a href=&quot;https://blog.habets.se/2021/11/AX25-user-space.html&quot;&gt;previously pointed out that the AX.25 implementation in the kernel is
pretty poor&lt;/a&gt;. It’s not really being maintained, and even &lt;a href=&quot;https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=219b51a6f040fa5367adadd7d58c4dda0896a01d&quot;&gt;when it
gets fixes&lt;/a&gt; after &lt;a href=&quot;https://marc.info/?l=linux-hams&amp;amp;amp=&amp;amp;m=159319049624305&amp;amp;amp=&amp;amp;w=2&quot;&gt;I reported it&lt;/a&gt;, with people running
&lt;a href=&quot;https://en.wikipedia.org/wiki/Long-term_support&quot;&gt;LTS&lt;/a&gt; OSs it can take like 5 years before before the fix actually reaches
users, if ever. So when writing applications, you still have to work around
kernel bugs from a decade ago. This makes it kind of pointless to upstream
patches.&lt;/p&gt;

&lt;p&gt;The exception is security patches, and reading between the lines of why &lt;a href=&quot;https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=64edfa65062dc4509ba75978116b2f6d392346f5&quot;&gt;the
AX.25 code is now being removed from the kernel&lt;/a&gt;, it sounds like maybe
some LLM (like the looming &lt;a href=&quot;https://en.wikipedia.org/wiki/Claude_(language_model)&quot;&gt;“Mythos”&lt;/a&gt; and the related
&lt;a href=&quot;https://www.anthropic.com/glasswing&quot;&gt;Glasswing&lt;/a&gt;) may have found some severe problems. But even if there
aren’t any known security problems yet, having code is now more of a liability
than ever. Code needs to be removed, or taken responsibility of. (tangent about
ffmpeg at the bottom of this post)&lt;/p&gt;

&lt;p&gt;With the kernel code removed, say goodbye to &lt;a href=&quot;https://blog.habets.se/2020/06/Amateur-packet-radio-walkthrough.html&quot;&gt;the old walkthrough&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;the-new-api&quot;&gt;The new API&lt;/h2&gt;

&lt;p&gt;Well, not “new”, per se, but “replacement”.&lt;/p&gt;

&lt;p&gt;With the socket based API about to be gone, we need some other way for
applications to send packets and manage connections.&lt;/p&gt;

&lt;p&gt;For sending raw packets to and from the modem there’s &lt;a href=&quot;https://en.wikipedia.org/wiki/KISS_(amateur_radio_protocol)&quot;&gt;KISS&lt;/a&gt;. I have no
real complaints about it. Not much to get wrong about sending frames. It’s
implemented by most modems, like the software modem &lt;a href=&quot;https://github.com/wb2osz/direwolf&quot;&gt;Dire Wolf&lt;/a&gt; and by
some radios like the &lt;a href=&quot;https://www.kenwood.com/usa/com/amateur/th-d75a/&quot;&gt;Kenwood TH-D75&lt;/a&gt;, so it’s not going anywhere.&lt;/p&gt;

&lt;p&gt;For connected mode (streams of in order data, like with TCP) the biggest
contender seems to be &lt;a href=&quot;https://www.on7lds.net/42/sites/default/files/AGWPEAPI.HTM&quot;&gt;AGW&lt;/a&gt;. Dire Wolf implements it, and I’ve
made &lt;a href=&quot;https://crates.io/crates/agw&quot;&gt;a messy implementation of an AGW client in Rust&lt;/a&gt;. The &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;
Rust API works, as we’ll see, but the code needs some refactoring and cleanup
due to it being written exploratorily while I was deciding what it should even
do, and how.&lt;/p&gt;

&lt;p&gt;The AGW protocol is not super amazing, but it gets the job done. One can build
a connection API on top of it, &lt;a href=&quot;https://crates.io/crates/agw&quot;&gt;as I have&lt;/a&gt;, and never have to think
about the AGW protocol ever again.&lt;/p&gt;

&lt;p&gt;There’s another protocol called RHP, specified &lt;a href=&quot;https://wiki.oarc.uk/packet:white-papers:pwp-0222&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;https://wiki.oarc.uk/packet:white-papers:pwp-0245&quot;&gt;here&lt;/a&gt;.
It came out of the &lt;a href=&quot;https://wiki.oarc.uk/packet:xrouter&quot;&gt;XRouter&lt;/a&gt; project. Since XRouter is closed source,
I have a strong aversion to it. It seems both counter to how I see amateur
radio, and anachronistic, for it to be closed source. It’s bad enough that
&lt;a href=&quot;https://rosmodem.wordpress.com/&quot;&gt;VARA&lt;/a&gt; and &lt;a href=&quot;https://winlink.org/WinlinkExpress&quot;&gt;Winlink&lt;/a&gt; are closed source. And people are definitely
working on replacing VARA with various other modes because of it.&lt;/p&gt;

&lt;p&gt;tl;dr: I’m going with AGW for now. If someone writes a Rust crate for RHP
exposing a compatible &lt;code class=&quot;highlighter-rouge&quot;&gt;AsyncRead/AsyncWrite&lt;/code&gt; API, I certainly wouldn’t mind
adding that dependency to optionally use.&lt;/p&gt;

&lt;p&gt;I have not yet implemented AGW (or RHP) in &lt;a href=&quot;https://blog.habets.se/2024/09/An-AX.25-implementation-in-Rust.html&quot;&gt;my own AX.25 stack&lt;/a&gt;, but I
plan to. For now that means I’ll use Dire Wolf.&lt;/p&gt;

&lt;h2 id=&quot;axsh&quot;&gt;axsh&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/ThomasHabets/axsh&quot;&gt;Link to code&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-previous-implementation&quot;&gt;The previous implementation&lt;/h3&gt;

&lt;p&gt;My previous axsh implementation, since &lt;a href=&quot;https://github.com/ThomasHabets/radiostuff/commit/2ecc3a2b3a670e225f85bc3ff8d66879437a0f88&quot;&gt;deleted&lt;/a&gt;, had some problems:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;it was implemented in C++, and not only do I prefer Rust, how could I even
call something written in C++ “secure”? (a blog post for another day)&lt;/li&gt;
  &lt;li&gt;used the kernel API, so that needs rewriting,&lt;/li&gt;
  &lt;li&gt;used &lt;code class=&quot;highlighter-rouge&quot;&gt;SEQPACKET&lt;/code&gt;, which proved to be a bit “weird” when interoperating with
some other APIs, and&lt;/li&gt;
  &lt;li&gt;used crypto primitives vulnerable to quantum computers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So with everything but terminal management needing a rewrite, this is a reason
to rewrite the whole thing.&lt;/p&gt;

&lt;h3 id=&quot;requirements&quot;&gt;Requirements&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Don’t use kernel AX.25 sockets — this means use AGW.&lt;/li&gt;
  &lt;li&gt;Use Rust.&lt;/li&gt;
  &lt;li&gt;Also work on TCP (mainly for debugging) — This means using an internal
framing protocol.&lt;/li&gt;
  &lt;li&gt;Be quantum safe — Use ML-DSA+ed25519 dual signed for authentication of server
and client.&lt;/li&gt;
  &lt;li&gt;Be efficient — This means don’t use ML-DSA for per packet signatures (they
are huge), at the cost of some quantum safety (see &lt;a href=&quot;https://github.com/ThomasHabets/axsh&quot;&gt;the README&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Non-requirement: Encrypt — This would violate the amateur radio license. And
then, why not just use SSH?&lt;/p&gt;

&lt;h3 id=&quot;example&quot;&gt;Example&lt;/h3&gt;

&lt;p&gt;If you have an AGW server, such as Dire Wolf, then it’s easy to run axsh. Just
start a server:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;axshd \
    -k server.key \
    -v debug
    -a authorized_keys \
    --agw-addr localhost:8010 \
    -l M0QQQ-1
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Then log in:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;axsh \
    -k client.key \
    -s M0QQQ-2 \
    --agw-addr localhost:8000 \
    M0QQQ-1
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Then wait like 30-40 seconds for the handshake to complete. The reason for the
wait is the large ML-DSA signatures used in the handshake.&lt;/p&gt;

&lt;p&gt;It can’t be the same Dire Wolf instance, since Dire Wolf only shuffles packets
between the radio and AGW clients, not from one AGW client to another. In my
case I had one Dire Wolf connected to an ICom 9700, and another to a Baofeng
UV5R using an &lt;a href=&quot;https://github.com/skuep/AIOC&quot;&gt;AIOC (all in one cable)&lt;/a&gt;. AIOC is highly recommended for
experimentation over the air.&lt;/p&gt;

&lt;p&gt;So yeah my test is between just about the cheapest VHF/UHF radio that exists,
and maybe the most expensive one.&lt;/p&gt;

&lt;iframe width=&quot;540&quot; height=&quot;360&quot; src=&quot;https://www.youtube.com/embed/8rv-Vu-gD3U&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;h3 id=&quot;dire-wolf-setup&quot;&gt;Dire Wolf setup&lt;/h3&gt;

&lt;h4 id=&quot;icom-9700&quot;&gt;ICom 9700&lt;/h4&gt;

&lt;p&gt;In addition to running &lt;code class=&quot;highlighter-rouge&quot;&gt;rigctld -m 3081 -r /dev/ttyUSB0 -s 19200&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;cat direwolf-9700.conf
&lt;span class=&quot;c&quot;&gt;# Identified with `aplay -l`.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# To get pulseaudio to get its dirty hands off of the device, I turned it &quot;Off&quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# under &quot;Configuration&quot; in `pavucontrol`.&lt;/span&gt;
ADEVICE plughw:2,0
PTT RIG 2 localhost:4532
CHANNEL 0
MYCALL M0QQQ-3
AGWPORT 8010
KISSPORT 8011
MODEM 1200
PACLEN 256
&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;direwolf -t 0 -c direwolf-9700.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4 id=&quot;aioc&quot;&gt;AIOC&lt;/h4&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;cat direwolf-aioc.conf
&lt;span class=&quot;c&quot;&gt;# Identified with `aplay -l`.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# To get pulseaudio to get its dirty hands off of the device, I turned it &quot;Off&quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# under &quot;Configuration&quot; in `pavucontrol`.&lt;/span&gt;
ADEVICE plughw:1,0
ARATE 48000
PTT /dev/ttyACM0 DTR -RTS
CHANNEL 0
MYCALL M0THC-8
MODEM 1200
PACLEN 256
&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;direwolf -t 0 -c direwolf-aioc.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;why-not-just-run-this-over-tcpip&quot;&gt;Why not just run this over TCP/IP?&lt;/h2&gt;

&lt;p&gt;With KISS providing packet support (and AGW providing a higher level API on
top, if preferred), why not just run TCP/IP, and let the very stable OS TCP
implementation take care of everything?&lt;/p&gt;

&lt;p&gt;TCP is definitely more modern, stable, and maintained, but it doesn’t scale
down to slow speeds very well. A TCP+IPv4 header is at least 40 bytes, and if
you don’t want to be some sort of caveman, IPv6 is another 20 bytes. At 1200bps
that would be 267-400ms overhead for every packet&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Checking a random TCP
data packet on my laptop I see that with TCP options TCP/IPv4 is actually 52
bytes, or 350ms.&lt;/p&gt;

&lt;p&gt;Counting the air time (milliseconds, not just bytes) makes this overhead
problem more obvious.&lt;/p&gt;

&lt;p&gt;And because of amateur radio license reasons TCP would still need to identify
the callsign, you probably have to add 17 bytes (113ms) as a surrounding
header.&lt;/p&gt;

&lt;p&gt;That leaves TCP with 69 or 89 bytes overhead per packet, meaning 460ms or
593ms. And since you don’t want to tie up the RF channel for too long (only for
the whole packet to be dropped due to interference), you won’t want to send
packets that are too large.&lt;/p&gt;

&lt;p&gt;Of course it’s 4x as slow if you want to do something like &lt;a href=&quot;https://en.wikipedia.org/wiki/Bell_103&quot;&gt;Bell 103&lt;/a&gt;
on HF.&lt;/p&gt;

&lt;p&gt;AX.25 connected mode takes that down to 19 bytes (126ms) overhead (if using Mod
128 mode) per data packet.&lt;/p&gt;

&lt;p&gt;Because of the AX.25 segmenter, for bulk data TCP is not as bad as it may have
sounded. For a 1500 byte TCP segment, fitting in just under 8 200 byte AX.25
frames (totalling &lt;code class=&quot;highlighter-rouge&quot;&gt;17*8+69=205&lt;/code&gt; bytes of overhead), this means 1367ms overhead
instead of plain AX.25 (at &lt;code class=&quot;highlighter-rouge&quot;&gt;19*8=152&lt;/code&gt; bytes) 1013ms. A 1500 byte payload takes
10 seconds to send, so that’s an overhead of 13.7% instead of 10.1%.&lt;/p&gt;

&lt;p&gt;But for interactive use cases, worst case a single payload packet, it’s 467ms
vs 133ms. And that’s only counting the data frames, not the acknowledgments. A
TCP ACK is at a minimum &lt;code class=&quot;highlighter-rouge&quot;&gt;17+40=57&lt;/code&gt; bytes, or 380ms. An AX.25 RR is 18-19 bytes,
or 120-127ms.&lt;/p&gt;

&lt;p&gt;That makes TCP about three times less efficient, compared to AX.25.&lt;/p&gt;

&lt;p&gt;A bigger problem with TCP, especially untweaked, is resend timers and window
sizes. At 1200bps you don’t actually want too big a window size, since you
don’t want to tie up the RF channel for several minutes if the other end has
gone away. So a bunch of airtime tweaks are needed. And at &lt;em&gt;best&lt;/em&gt; you’ll end up
with the numbers above.&lt;/p&gt;

&lt;p&gt;Maybe you could tweak TCP to be more friendly to lower speeds, and find the
other overhead acceptable. If so, then you’ll be happy to hear that axsh
supports running on TCP as well.&lt;/p&gt;

&lt;h2 id=&quot;why-not-quic&quot;&gt;Why not QUIC?&lt;/h2&gt;

&lt;p&gt;Well first, it inherits the same problems from TCP/IP. Sure, the UDP header is
smaller than the TCP header, but then on top of that there’s the QUIC header.&lt;/p&gt;

&lt;p&gt;The second problem is that QUIC is meant to be encrypted. Ripping out
encryption, while staying secure, seems more dangerous that keeping it simple
and just working from the requirements. Probably the whole handshake would have
to be redesigned.&lt;/p&gt;

&lt;h2 id=&quot;ffmpeg--google&quot;&gt;FFmpeg &amp;amp; Google&lt;/h2&gt;

&lt;p&gt;AX.25 being removed from the Linux kernel reminds me of LLM finding &lt;a href=&quot;https://thenewstack.io/ffmpeg-to-google-fund-us-or-stop-sending-bugs/&quot;&gt;that bug
in ffmpeg&lt;/a&gt;, causing all that drama.&lt;/p&gt;

&lt;p&gt;I have no dog in this fight, but in my opinion ffmpeg is in the wrong, here.
Their argument seems to be all about how this particular encoder is rarely
used, is just a hobby project, etc.. Ok, but it’s in your code base. Even if
disabled by default, why would you want to ship a security footgun? Maybe some
hobbyists out there build ffmpeg with all encoders enabled. Do you want them to
be vulnerable to someone’s virus?&lt;/p&gt;

&lt;p&gt;So Google should either keep quiet, or give a patch? Well, keeping quiet
because the codec is rarely used is not really an option. That’s borderline
negligent and morally culpable, for when someone eventually gets hacked.&lt;/p&gt;

&lt;p&gt;So Google “should” always provide a patch in these cases? Perhaps, depending on
the meaning of the word “should”. Google is rich, so “should” be morally forced
to contribute to your software, just because Google (presumably, via youtube)
is a heavy user of ffmpeg?&lt;/p&gt;

&lt;p&gt;Well, that just sounds like the the (non-)problem with open source software (or
free software) in general. The license permits use and profit without
contribution. If you wanted a tithe then you should have put that in the
license. Sounds like you want everyone to be free only to do what &lt;em&gt;you&lt;/em&gt; want.
That’s not how that works.&lt;/p&gt;

&lt;p&gt;This is also why I don’t like &lt;a href=&quot;https://en.wikipedia.org/wiki/GNU_Affero_General_Public_License&quot;&gt;the AGPL license&lt;/a&gt;. It’s not free software
if it binds me in your serfdom.&lt;/p&gt;

&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h2&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;Actually, it’s a tiny bit more, because of the occasional &lt;a href=&quot;https://en.wikipedia.org/wiki/Bit_stuffing&quot;&gt;bit stuffing&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Mon, 11 May 2026 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2026/05/Quantum-safe-amateur-radio-secure-shell.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2026/05/Quantum-safe-amateur-radio-secure-shell.html</guid>
        
        
        <category>radio</category>
        
        <category>crypto</category>
        
        <category>network</category>
        
        <category>security</category>
        
      </item>
    
      <item>
        <title>You cannot sell AI written software</title>
        <description>&lt;p&gt;You may have seen it too. This trend of “I wrote some software to solve a
problem. I think it’s pretty great. Does anyone have any feedback?”. Maybe it’s
a budget app. Or some company management thingy, tracking sales. Or invoicing.&lt;/p&gt;

&lt;p&gt;Maybe you take a look. It looks pretty slick. But then you get a feeling of
&lt;a href=&quot;https://en.wikipedia.org/wiki/Uncanny_valley&quot;&gt;uncanny valley&lt;/a&gt;. It’s just not right. Maybe you can’t even put your
finger on it.&lt;/p&gt;

&lt;p&gt;I’m not an accountant, so when I see some accounting software do something in a
different way, it’s interesting. Why is it that way? What can I learn from the
fact that a professional thinks it should be this way?&lt;/p&gt;

&lt;p&gt;You already know what’s weird about it, if nothing else because of the title of
this post. The software works this way because the LLM wrote it that way.
There’s no reason. It’s &lt;a href=&quot;https://en.wikipedia.org/wiki/Not_even_wrong&quot;&gt;not even wrong&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;How do you give “feedback” on that? My feedback would be that &lt;em&gt;you don’t
understand the problem you’re trying to solve&lt;/em&gt;, and have shown no sign you
intend to understand it, so how could you possibly think you can solve it?&lt;/p&gt;

&lt;p&gt;You’re not asking for feedback. You’re asking for someone else to do
everything.&lt;/p&gt;

&lt;h2 id=&quot;a-house-analogy&quot;&gt;A house analogy&lt;/h2&gt;

&lt;p&gt;Analogies sometimes work, sometimes not. I’m not sure if this one will work.&lt;/p&gt;

&lt;p&gt;You have a house with stairs that go nowhere, because “houses have stairs,
right?”. You didn’t put the stairs there, and you have no sense for why there
should be stairs in any particular place, so sure, why &lt;em&gt;not&lt;/em&gt; there?&lt;/p&gt;

&lt;p&gt;Contrast this with an actual architect. They know why they put the stairs
there. They know why they didn’t put the stairs on the other side of the room.
It’s not (just) that they have actual knowledge, it’s also that they actually
had to think about “how do you get to upstairs, and what’s the experience of
walking up those stairs?”. They had to make many small decisions, each of which
required thinking, and required that the choices add up to a coherent whole.&lt;/p&gt;

&lt;p&gt;The AI put the stairs somewhere, and you said “sure, stairs”. They’re in the
kitchen, but since you only had to OK the stairs, not &lt;strong&gt;actively&lt;/strong&gt; choose which
room they go in, it didn’t even occur to you that room selection was a thing.&lt;/p&gt;

&lt;p&gt;You don’t need architect credentials to do this right. You could have selected
a room. It probably would have been a fine room. Maybe a professional could
give you feedback on that choice. But if you don’t make choices then you’re not
actually contributing to the thing you supposedly built.&lt;/p&gt;

&lt;p&gt;You made no choices, so you don’t understand the problem you supposedly solved.&lt;/p&gt;

&lt;h2 id=&quot;jurassic-park-phrased-it-well&quot;&gt;Jurassic park phrased it well&lt;/h2&gt;

&lt;p&gt;“If I may… Um, I’ll tell you the problem with the scientific power that
you’re using here, it didn’t require any discipline to attain it. You read
what others had done and you took the next step. You didn’t earn the knowledge
for yourselves, so you don’t take any responsibility for it. You stood on the
shoulders of geniuses to accomplish something as fast as you could, and before
you even knew what you had, you patented it, and packaged it, and slapped it
on a plastic lunchbox, and now you’re selling it”&lt;/p&gt;

&lt;p&gt;Except you didn’t even “read what others had done”. Not even that bare minimum.&lt;/p&gt;

&lt;h2 id=&quot;artisanally-crafted-custom-solutions&quot;&gt;Artisanally crafted custom solutions&lt;/h2&gt;

&lt;p&gt;I use &lt;a href=&quot;https://github.com/ThomasHabets/cmdg&quot;&gt;my own email client&lt;/a&gt;, written in artisanally hand crafted code,
like back in the stone age. It solves a problem for me. I have my own &lt;a href=&quot;https://github.com/ThomasHabets/yurate&quot;&gt;youtube
UI&lt;/a&gt;. And my own RSS reader. For a while I used &lt;a href=&quot;https://github.com/ThomasHabets/tlssh&quot;&gt;my own SSH&lt;/a&gt;. I
know people who wrote their own shell, and that is their daily driver. I plan
to move this blog to &lt;a href=&quot;https://github.com/ThomasHabets/tarweb&quot;&gt;my own webserver&lt;/a&gt; behind a &lt;a href=&quot;https://github.com/ThomasHabets/tarweb/blob/main/example-configs/this-and-that.conf&quot;&gt;custom SNI
router&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Those can get feedback. I’m not saying they’re great, but even if the feedback
is “why would you trust OpenSSL more than OpenSSH?”, it’s useful.&lt;/p&gt;

&lt;p&gt;But honestly, if someone wants an email client kind of like mine, but
customized to their preferences, then they may be better off asking an LLM to
write their personal email client. That was not true 5 years ago. I don’t want
to use email threading. Maybe you do. It’d be annoying to support both.&lt;/p&gt;

&lt;h2 id=&quot;ai-can-make-useful-custom-stuff&quot;&gt;AI can make useful custom stuff&lt;/h2&gt;

&lt;p&gt;I was having some glitches with my video conference, and wanted to
troubleshoot. I couldn’t find a tool that did what I wanted exactly.
&lt;a href=&quot;https://github.com/orf/gping&quot;&gt;gping&lt;/a&gt; came close, but not quite.&lt;/p&gt;

&lt;p&gt;I wanted something that pings regularly and frequently, summarizing these many
pings about once a second, to illustrate subsecond network outages. And using
Linux ICMP sockets, so that it doesn’t require root.&lt;/p&gt;

&lt;p&gt;I spent about as much time looking for the perfect tool as I then spent telling
an LLM to write it.&lt;/p&gt;

&lt;p&gt;I had it write &lt;a href=&quot;https://github.com/ThomasHabets/blipfinder&quot;&gt;blipfinder&lt;/a&gt;, and it solved my problem. I could have
had this tool written for me before my meeting had even ended.&lt;/p&gt;

&lt;p&gt;Two relevant points about this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;I am, actually, a network expert. I know right from wrong in this space.&lt;/li&gt;
  &lt;li&gt;I am not, however, vouching for this tool. It solved my problem, and it’s
amazing that custom throwaway tooling can be generated like this.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Just for fun, I had the LLM &lt;a href=&quot;https://github.com/ThomasHabets/blipfinder2&quot;&gt;write it again&lt;/a&gt;. That version was
also acceptable. It made some different choices. Which choices were best? Meh,
nobody gave it any thought.&lt;/p&gt;

&lt;h2 id=&quot;why-can-you-not-sell-it&quot;&gt;Why can you not sell it?&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;You weren’t forced to solve the problem, so you weren’t forced to actually
understand the problem. You cannot identify a correct solution. Your
software will never be fit for purpose for anybody but you.&lt;/li&gt;
  &lt;li&gt;Anybody who’s seen your software can ask an LLM to write it from scratch for
them. If you aren’t adding any value by knowing right from wrong, correct from
incorrect, then your potential customers are unable to buy expertise from you.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It can still be the right custom software &lt;em&gt;for you&lt;/em&gt;. In fact custom software is
a huge win for LLM. But selling that is not.&lt;/p&gt;

&lt;p&gt;5 years ago there was no software in this uncanny valley. It was either obvious
crap, or it was professional. 5 years ago your potential customers couldn’t
replace you and your software with a prompt.&lt;/p&gt;

&lt;p&gt;But now? If you sell this slop then you don’t have customers, you have
&lt;a href=&quot;https://en.wikipedia.org/wiki/Scam&quot;&gt;“marks”&lt;/a&gt;.&lt;/p&gt;

</description>
        <pubDate>Tue, 05 May 2026 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2026/05/You-cannot-sell-AI-written-software.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2026/05/You-cannot-sell-AI-written-software.html</guid>
        
        
        <category>rant</category>
        
        <category>ai</category>
        
      </item>
    
      <item>
        <title>Rustradio - SDR framework now also in the browser, with Wasm</title>
        <description>&lt;p&gt;I’ve previously blogged about &lt;a href=&quot;https://blog.habets.se/categories.html#rustradio&quot;&gt;RustRadio&lt;/a&gt;, my GNU Radio like
framework for writing software defined radio applications in Rust. And now
there’s more progress of an interesting kind.&lt;/p&gt;

&lt;p&gt;Anything that tries to do something similar to GNU Radio needs a few things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Core framework.&lt;/li&gt;
  &lt;li&gt;SDR components (filters, clock recovery, multipliers, etc).&lt;/li&gt;
  &lt;li&gt;A user interface.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to these, GNU Radio also has the excellent GNU Radio Companion for
interactive creation of flowgraphs, but I’m not tackling that yet.&lt;/p&gt;

&lt;p&gt;I have a core framework, and some components (blocks). But the UI has been a
bit lacking.&lt;/p&gt;

&lt;p&gt;I’ve played around with TUI applications, but I always knew I also wanted to
support having a UI in the browser. I’m not as interested in adding support for
QT or Windows native UI. The browser will do fine.&lt;/p&gt;

&lt;p&gt;There are two ways to get the UI in the browser:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Have the browser talk to a backend that’s running the actual DSP.&lt;/li&gt;
  &lt;li&gt;Running the DSP in the browser, with no need for a backend.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While I’ll want (1) eventually, and have some ideas about that, this post is
about running everything in the browser, using Wasm.&lt;/p&gt;

&lt;p&gt;Another great benefit of using Wasm for DSP and UI is that no installation is
required. Users can just point their browser and run it immediately.&lt;/p&gt;

&lt;p&gt;I know that this is just scratching the surface on some aspects of this, but I
hope I at least didn’t get anything wrong.&lt;/p&gt;

&lt;h2 id=&quot;wasm-in-short&quot;&gt;Wasm, in short&lt;/h2&gt;

&lt;p&gt;There have been various technologies to running code in the browser. ActiveX,
VBScript, Java, and Flash come to mind. They’ve all gone away. Only
Javascript remains. Typescript is a better Javascript, but it’s still basically
Javascript.&lt;/p&gt;

&lt;p&gt;Wasm allows you to compile programming languages that normally compile to
native code, like C, C++, and Rust, to a portable binary that the browser can
execute. This is better than compiling (transpiling) them to Javascript,
because it doesn’t require the overhead of guaranteeing Javascript behavior
while actually executing another language’s compiled code.&lt;/p&gt;

&lt;p&gt;Great. I’m not a fan of Javascript, so this should mean I’ll be able to do web
coding without writing even a single line of Javascript. Well, aside from the
line that goes “load the Wasm”.&lt;/p&gt;

&lt;p&gt;More importantly for this project, it means I’ll be able to run RustRadio in
the browser.&lt;/p&gt;

&lt;h2 id=&quot;web-workers&quot;&gt;Web workers&lt;/h2&gt;

&lt;p&gt;Javascript is single threaded. That’s not a problem in itself, since RustRadio
is perfectly happy running single threaded. But if something gets stuck in a
loop then you’ll get the “Page is not responding” dialog, making you and your
users unhappy. And even without that, while some heavy computation is happening
in the main UI thread, the page will be unresponsive.&lt;/p&gt;

&lt;p&gt;Javascript will likely never have threads in the traditional sense (some
frameworks fake it, as I’ll get to).&lt;/p&gt;

&lt;p&gt;So if you want to avoid locking up the main UI thread, then you need to spin
off a “Worker”. This worker runs in its own thread, and has very limited access
to the outside world, including other workers. In particular, it does not have
access to the DOM (the web page). Only the main UI “thread” has access to that.&lt;/p&gt;

&lt;p&gt;What you can do is post messages between the worker and the other worker or
main thread that spawned it. You can also create a shared buffer, but I’ve not
done that yet, so ignoring that for now.&lt;/p&gt;

&lt;p&gt;Any heavy CPU work that’s expected to take a few milliseconds should be
considered for offload to a Worker instead of running on the main UI thread.
And keep in mind low end devices like phones, when estimating “a few
milliseconds”.&lt;/p&gt;

&lt;p&gt;The sandboxing of a Worker is also nice, in that if something runs in a worker,
then you know that it can’t affect the DOM.&lt;/p&gt;

&lt;p&gt;It’s apparently possible to run threads with Wasm in some setups, but it’s not
been standardized, so it’s not something I’m going to build for. In particular,
it doesn’t work on Apple phones, which is a big enough target that it’s a
showstopper.&lt;/p&gt;

&lt;p&gt;So for my purposes the only way to run a separate thread is to run a separate
worker.&lt;/p&gt;

&lt;h3 id=&quot;computing-and-receiving-messages-on-the-same-thread&quot;&gt;Computing and receiving messages on the same thread&lt;/h3&gt;

&lt;p&gt;A worker receives a message by having its &lt;code class=&quot;highlighter-rouge&quot;&gt;onmessage&lt;/code&gt; handler called. But since
the worker is just the one thread, it can’t interrupt whatever heavy
computation was currently happening. It can’t even do something POSIX signal
like, and redirect execution into a new stack. No, it just patiently waits
until the worker finishes what it’s doing before calling that callback.&lt;/p&gt;

&lt;p&gt;There are two ways of doing this. Either the worker snapshots its computation,
and hopes it’ll get a message later, resuming it, or (technically equivalently)
you use &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt; functions.  The latter seems better in my limited experience.
Because &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt; is just fancy syntax for returning a future, it’s not actually
different from returning and resuming; It’s just automatic. It also means that
having returned, any other &lt;code class=&quot;highlighter-rouge&quot;&gt;onmessage&lt;/code&gt; handler for more messages have a chance
to run.&lt;/p&gt;

&lt;h3 id=&quot;async-sync-async&quot;&gt;async-&amp;gt;sync-&amp;gt;async&lt;/h3&gt;

&lt;p&gt;Sometimes async code needs to send a sync function to some API (e.g. register a
sync callback).  This handler is then called outside of async, and can’t
&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; on any async. &lt;code class=&quot;highlighter-rouge&quot;&gt;onmessage&lt;/code&gt; and button click handlers are sync, yet may
need to &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt;. This is part of that whole &lt;a href=&quot;https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/&quot;&gt;“what color is your
function”&lt;/a&gt; thing.&lt;/p&gt;

&lt;p&gt;It seems that the core library of Rust Wasm, &lt;code class=&quot;highlighter-rouge&quot;&gt;wasm_bindgen&lt;/code&gt;, helpfully provides
a function to register more futures for execution, with &lt;code class=&quot;highlighter-rouge&quot;&gt;spawn_local()&lt;/code&gt;. This
makes things easy. The handler then just registers the async call, and returns.&lt;/p&gt;

&lt;h2 id=&quot;my-example-an-ax25-1200bps-decoder-in-the-browser&quot;&gt;My example: An AX.25 1200bps decoder, in the browser&lt;/h2&gt;

&lt;p&gt;I’ve blogged about &lt;a href=&quot;https://en.wikipedia.org/wiki/AX.25&quot;&gt;AX.25&lt;/a&gt; before. For this post all you need to know
is that it’s a data packet sent over radio, and that my example is able to find
and decode the packet from a recording.&lt;/p&gt;

&lt;p&gt;The live site is &lt;a href=&quot;https://thomashabets.github.io/ruwasm/&quot;&gt;here&lt;/a&gt; if you want to try it. Or just enjoy this
screenshot and video if you don’t. :-)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/2026-04-ruwasm.png&quot;&gt;&lt;img src=&quot;/static/2026-04-ruwasm.png&quot; alt=&quot;ruwasm&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;iframe width=&quot;788&quot; height=&quot;503&quot; src=&quot;https://www.youtube.com/embed/vv0oimMiLGM&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;h2 id=&quot;my-first-memory-error-in-rust&quot;&gt;My first memory error in Rust&lt;/h2&gt;

&lt;p&gt;Rust is a memory safe language. JS is too. So how is &lt;em&gt;this&lt;/em&gt; the first time I’ve
managed to get memory corruption with Rust? I get this when I try a
sufficiently large file (50MB+ tends to trigger it).&lt;/p&gt;

&lt;p&gt;Notice in this screenshot how the vector has been corrupted, thinking it’s over
a GB of data. It should be 64Ki.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/2026-04-error.png&quot;&gt;&lt;img src=&quot;/static/2026-04-error.png&quot; alt=&quot;Vector size corruption and drain error&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hopefully I’ll get this fixed, so for future reference this is when running the
code in the &lt;a href=&quot;https://github.com/ThomasHabets/ruwasm&quot;&gt;ruwasm&lt;/a&gt; repo at commit
6088f711141fe566a8a02f9f40d5866ff6d8e82f.&lt;/p&gt;

&lt;p&gt;Maybe it’s from some immature dependency. Maybe (more likely?) it’s a bug in my
sloppily coded buffer handler. It does have a couple of &lt;code class=&quot;highlighter-rouge&quot;&gt;unsafe&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;github-pages-auto-deploy&quot;&gt;GitHub Pages auto-deploy&lt;/h2&gt;

&lt;p&gt;You may have noticed that the URL to the live demo is hosted on github.io.&lt;/p&gt;

&lt;p&gt;Because there’s no active server component, I thought I’d try out continuous
deployment of this Wasm application. It was simpler than I expected. Just
enable github pages, set them to be sourced from github actions, and add &lt;a href=&quot;https://github.com/ThomasHabets/ruwasm/blob/main/.github/workflows/deploy.yml&quot;&gt;a
simple github action&lt;/a&gt;, and upon every &lt;code class=&quot;highlighter-rouge&quot;&gt;git push&lt;/code&gt;, the latest version
gets hosted “in the cloud”.&lt;/p&gt;

&lt;p&gt;That’s it. Now whenever I push a new version to github, it’s live on that URL a
minute later. Pretty cool.&lt;/p&gt;

&lt;p&gt;Future work is to trigger another github action on tagging a release, creating
a permanent location where this tagged version is deployed. That way it’ll be
possible to bisect looking for a bug, without even compiling.&lt;/p&gt;

&lt;p&gt;I’ll also want to have it trigger on pull requests, so that it can be tested
prior to merge, without the need to download and build locally.&lt;/p&gt;

&lt;h2 id=&quot;wasm-the-end-of-javascript&quot;&gt;Wasm, the end of Javascript?&lt;/h2&gt;

&lt;p&gt;Of course this is &lt;a href=&quot;https://en.wikipedia.org/wiki/Betteridge's_law_of_headlines&quot;&gt;Betteridge’s law of headlines&lt;/a&gt;, but &lt;a href=&quot;https://youtu.be/ceH0IT-OBCw?list=PLNVwswC38Mi3MaVkLWlql31s3j5_cDAsZ&quot;&gt;this
video&lt;/a&gt; is still interesting and relevant.&lt;/p&gt;

&lt;p&gt;WebUSB should provide direct access to SDR hardware like RTLSDR and USRPs.
There’s no reason it shouldn’t be possible to run a large complex interactive
SDR application directly against hardware, all inside the browser with no
server components.&lt;/p&gt;

</description>
        <pubDate>Sat, 11 Apr 2026 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2026/04/Rustradio-SDR-framework-now-also-in-the-browser-with-wasm.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2026/04/Rustradio-SDR-framework-now-also-in-the-browser-with-wasm.html</guid>
        
        
        <category>wasm</category>
        
        <category>sdr</category>
        
        <category>radio</category>
        
        <category>rust</category>
        
        <category>rustradio</category>
        
        <category>programming</category>
        
        <category>web</category>
        
      </item>
    
      <item>
        <title>The strange webserver hot potato — sending file descriptors</title>
        <description>&lt;p&gt;I’ve &lt;a href=&quot;/2025/04/io-uring-ktls-and-rust-for-zero-syscall-https-server.html&quot;&gt;previously mentioned my io-uring webserver tarweb&lt;/a&gt;. I’ve now
added another interesting aspect to it.&lt;/p&gt;

&lt;p&gt;As you may or may not be aware, on Linux it’s possible to send a file
descriptor from one process to another over a unix domain socket. That’s
actually pretty magic if you think about it.&lt;/p&gt;

&lt;p&gt;You can also send unix credentials and SELinux security contexts, but that’s a
story for another day.&lt;/p&gt;

&lt;h2 id=&quot;my-goal&quot;&gt;My goal&lt;/h2&gt;

&lt;p&gt;I want to run some domains using my webserver “tarweb”. But not all. And I want
to host them on a single IP address, on the normal HTTPS port 443.&lt;/p&gt;

&lt;p&gt;Simple, right? Just use nginx’s &lt;code class=&quot;highlighter-rouge&quot;&gt;proxy_pass&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Ah, but I don’t want nginx to &lt;em&gt;stay&lt;/em&gt; in the path. After &lt;a href=&quot;https://en.wikipedia.org/wiki/Server_Name_Indication&quot;&gt;SNI&lt;/a&gt; (read:
“browser saying which domain it wants”) has been identified I want the TCP
connection to go &lt;em&gt;directly&lt;/em&gt; from the browser to the correct backend.&lt;/p&gt;

&lt;p&gt;I’m sure somewhere on the internet there’s already an SNI router that does
this, but all the ones I found stay in line with the request path, adding a
hop.&lt;/p&gt;

&lt;h2 id=&quot;why&quot;&gt;Why?&lt;/h2&gt;

&lt;p&gt;A few reasons:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Having all bytes bounce on the SNI router triples the number of total file
descriptors for the connection. (one on the backend, then one each on the
router for upstream and downstream). There are limits per process and system
wide, and the more you have the more you need to juggle them in code.&lt;/li&gt;
  &lt;li&gt;It also wastes CPU and RAM.&lt;/li&gt;
  &lt;li&gt;I want the backend to know the real client IP address, via &lt;code class=&quot;highlighter-rouge&quot;&gt;getpeername()&lt;/code&gt;
or similar, on the socket itself.&lt;/li&gt;
  &lt;li&gt;I don’t want restarting nginx to cut existing connections to backends.&lt;/li&gt;
  &lt;li&gt;I’d like to use TLS keys that the nginx user doesn’t have access to.&lt;/li&gt;
  &lt;li&gt;I used &lt;code class=&quot;highlighter-rouge&quot;&gt;proxy_pass&lt;/code&gt; for &lt;a href=&quot;/2023/04/Counting-current-live-readers.html&quot;&gt;livecount&lt;/a&gt;, and last time I got blog
posts on &lt;a href=&quot;/static/2025-09-top-two.png&quot;&gt;HackerNews&lt;/a&gt; nginx ran out of file descriptors, and started
serving 500 for it serving just plain old static files on disk. For now
I’ve moved livecount to a different port, but in the long run I want it back
on port 443, and yet isolated from nginx so that the latter keeps working
even if livecount is overloaded.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Livecount has an open websocket to every open browser tab in the world reading
a given page, so they add up. (no, it doesn’t log. It just keeps count)&lt;/p&gt;

&lt;h2 id=&quot;what-i-built&quot;&gt;What I built&lt;/h2&gt;

&lt;p&gt;I built &lt;a href=&quot;https://github.com/ThomasHabets/tarweb/blob/main/src/sni/sni.rs&quot;&gt;a proof of concept SNI router&lt;/a&gt;. It is a frontline server
receiving TCP connections, on which it then snoops the SNI from the TLS
ClientHello, and routes the connection according to its given rules.&lt;/p&gt;

&lt;p&gt;Anything it reads from the socket is sent along to the real backend along with
the file descriptor. So the backend (in my use that’s tarweb) needs to have
code cooperating to receive the new connection.&lt;/p&gt;

&lt;p&gt;It’s not the cleanest code, but it works. I got ChatGPT to write the boring
“parse the TLS record / ClientHello” parts. Rust is a memory safe language, so
“how bad could it be?”. :-)&lt;/p&gt;

&lt;p&gt;It seems to work for all the currently used TLS versions.&lt;/p&gt;

&lt;h2 id=&quot;its-not-plug-and-play&quot;&gt;It’s not plug and play&lt;/h2&gt;

&lt;p&gt;As I said, it requires the backend to be ready to receive “hey, here’s a file
descriptor, and here’s the first few hundred bytes you should treat as if
you’ve read them from the client”.&lt;/p&gt;

&lt;p&gt;File descriptors don’t have an operation to “unread”. If they did then this
would be easier.  Then it would “just” be a matter of giving a backend
webserver a file descriptor. For some use cases that could mean starting a new
webserver process that reads and writes from stdin/stdout.&lt;/p&gt;

&lt;p&gt;Not super efficient to go back to the fork-exec-per-connection model from the
previous century, but it would achieve the direct connection.&lt;/p&gt;

&lt;p&gt;But the details are academic. We &lt;em&gt;do&lt;/em&gt; need to pass along the snooped bytes
somehow, or the TLS handshake won’t succeed. Which means it does need
cooperation from the backend.&lt;/p&gt;

&lt;h2 id=&quot;but-it-is-privacy-preserving&quot;&gt;But it is privacy preserving&lt;/h2&gt;

&lt;p&gt;Because the SNI router never writes to the client, and therefore doesn’t
perform a TLS handshake, it doesn’t need any private keys or certificates.&lt;/p&gt;

&lt;p&gt;The SNI router has no secrets, and sees no secrets.&lt;/p&gt;

&lt;p&gt;I also added a mode that proxies the TCP connection, if some SNI should be
routed to a different server. But of course then it’s not possible to pass the
file descriptor. So encrypted bytes will bounce on the SNI router for that kind
of flow. But still the SNI router is not able to decrypt anything.&lt;/p&gt;

&lt;p&gt;A downside is of course that bouncing the connection around the world will slow
it down, add latency, and waste resources. So pass the file descriptor where
possible.&lt;/p&gt;

&lt;h2 id=&quot;the-hot-potato&quot;&gt;The hot potato&lt;/h2&gt;

&lt;p&gt;So now my setup has the SNI router accept the connection, and then throw the
very file descriptor over to tarweb, saying “you deal with this TCP
connection”. Tarweb does the TLS handshake, and then throws the TLS session
keys over to the kernel, saying “I can’t be bothered doing encryption, you do
it”, and then actually handles the HTTP requests.&lt;/p&gt;

&lt;p&gt;Well actually, there’s another strange indirection. When tarweb receives a file
descriptor, it uses io-uring &lt;a href=&quot;https://man7.org/linux/man-pages/man2/io_uring_register.2.html&quot;&gt;“registered files”&lt;/a&gt; to turn it into a
“fixed file handle”, and closes the original file descriptor. On the kernel
side there’s still a file descriptor of course, but there’s nothing in
&lt;code class=&quot;highlighter-rouge&quot;&gt;/proc/&amp;lt;pid&amp;gt;/fd/&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ls /proc/699874/fd -l
total 0
lrwx------ 1 thomas thomas 64 Oct 26 21:47 0 -&amp;gt; /dev/pts/5
lrwx------ 1 thomas thomas 64 Oct 26 21:47 1 -&amp;gt; /dev/pts/5
lrwx------ 1 thomas thomas 64 Oct 26 21:47 2 -&amp;gt; /dev/pts/5
lrwx------ 1 thomas thomas 64 Oct 26 21:47 3 -&amp;gt; anon_inode:[io_uring]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;This improves performance a bit on the linux kernel side.&lt;/p&gt;

&lt;p&gt;The SNI router does not use io-uring. At least not yet. The SNI router’s job is
much smaller (doesn’t even do a TLS handshake), much more brief (it almost
immediately passes the file descriptor to tarweb), and much less concurrency
(because of the connections being so short lived as far as it’s concerned),
that it may not be worth it.&lt;/p&gt;

&lt;p&gt;In normal use the SNI router only needs these syscalls per connection:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;accept()&lt;/code&gt; for the new connection,&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;read()&lt;/code&gt; a few hundred bytes of ClientHello,&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;sendmsg()&lt;/code&gt; of same size to pass it on,&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;close()&lt;/code&gt; to forget the file descriptor.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;http3-can-redirect-connections&quot;&gt;HTTP/3 can redirect connections&lt;/h2&gt;

&lt;p&gt;At the risk of going off on an unrelated tangent, HTTP/3 (QUIC-based) has &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc9000#section-9.6&quot;&gt;an
interesting way of telling a client to “go over there”&lt;/a&gt;. A built in load
balancer inside the protocol, you could say, sparing the load balancer needing
to proxy everything.&lt;/p&gt;

&lt;p&gt;This opens up opportunities to steer not just on SNI, and is much more flexible
than DNS, all without needing the “proxy” to be inline.&lt;/p&gt;

&lt;p&gt;E.g. say a browser is in Sweden, and you have servers in Norway and Italy. And
say you have measured, and find that it would be best if the browser connected
to your Norway server. But due to peering agreements and other fun stuff, Italy
will be preferred on any BGP anycasted address.&lt;/p&gt;

&lt;p&gt;You then have a few possible options, and I do mean they’re all possible:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Have the browser connect to &lt;code class=&quot;highlighter-rouge&quot;&gt;norway.example.com&lt;/code&gt;, with Norway-specific
IP addresses. Not great. People will start bookmarking these URLs, and what
happens when you move your Norway servers to Denmark? &lt;code class=&quot;highlighter-rouge&quot;&gt;norway.example.com&lt;/code&gt;
now goes to servers in Denmark?&lt;/li&gt;
  &lt;li&gt;Use DNS based load balancing, giving Swedish browsers the Norway unicast
IPs. Yes… but this is WAY more work than you probably think. And WAY less
reliable at giving the best experience for the long tail. And sometimes
your most important customer is in that long tail.&lt;/li&gt;
  &lt;li&gt;Try to traffic engineer the whole Internet with BGP announcement tweaks.
Good luck with that, for the medium to long tail.&lt;/li&gt;
  &lt;li&gt;Install servers in Sweden, and any other place you may have users. Then you
can anycast your addresses from there, and have full control of how you
proxy (or packet by packet traffic engineer over tunnels) them. Expensive if
you have many locations you need to do this in. Some traffic will still go
to the wrong anycast entry point, but pretty feasible though expensive.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The two DNS-based ones also have the valid concern that screwing up DNS can
have &lt;a href=&quot;https://aws.amazon.com/message/101925/&quot;&gt;bad consequences&lt;/a&gt;. If you can leave DNS alone that’s better.&lt;/p&gt;

&lt;p&gt;Back to HTTP/3. If you’ve set up HTTP/3 it may be because you care about
latency. It’s then easier to act on information you have about every single
connection. On an individual connection basis you can tell the browser in
Sweden that it should now talk to the servers in Norway. All without DNS or
anycast.&lt;/p&gt;

&lt;p&gt;Which is nice, because running a webserver is hard enough. Also running a
dynamic DNS service or anycast has even more opportunities to blow up
fantastically.&lt;/p&gt;

&lt;h2 id=&quot;where-was-i-oh-yeah-file-descriptors&quot;&gt;Where was I? Oh yeah, file descriptors&lt;/h2&gt;

&lt;p&gt;I should add that HTTP/3 doesn’t have the “running out of file descriptors”
problem. Being based on UDP you can run your entire service with just a single
file descriptor. Connections are identified by IDs, not 5-tuples.&lt;/p&gt;

&lt;p&gt;So why didn’t I just use HTTP/3?&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;HTTP/3 is complex. You can build &lt;a href=&quot;/2025/04/io-uring-ktls-and-rust-for-zero-syscall-https-server.html&quot;&gt;a weird io-uring kTLS based
webserver&lt;/a&gt; on a weekend, and control everything (except TLS
handshakes). Implementing HTTP/3 from scratch, and controlling everything,
is a different beast.&lt;/li&gt;
  &lt;li&gt;HTTP/1 needs to still work. Not all clients support HTTP/3, and HTTP/1 or 2
is even used to bootstrap HTTP/3 via its &lt;code class=&quot;highlighter-rouge&quot;&gt;Alt-Svc&lt;/code&gt; header.&lt;/li&gt;
  &lt;li&gt;Preferred address in HTTP/3 is just a suggestion. Browsers don’t have to
actually move.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;what-about-encrypted-sni-esni-or-encrypted-clienthello-ech&quot;&gt;What about encrypted SNI (ESNI), or encrypted ClientHello (ECH)&lt;/h2&gt;

&lt;p&gt;No support for that (yet). From some skimming &lt;a href=&quot;https://www.cloudflare.com/en-gb/learning/ssl/what-is-encrypted-sni/&quot;&gt;ESNI&lt;/a&gt; should “just work”,
with just a minor decryption operation in the SNI router.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.cloudflare.com/encrypted-client-hello/&quot;&gt;ECH&lt;/a&gt; seems harder. It should still be doable, but the SNI router will
need to do the full handshake, or close to it. And after taking its routing
decision it needs to transfer the encryption state to the backend, along with
the file descriptor.&lt;/p&gt;

&lt;p&gt;This is not impossible, of course. It’s similar to how tarweb passes the TLS
session keys to the kernel. But it likely does mean that the SNI router needs
to have access to both the TLS session keys and maybe even the domain TLS
private keys.&lt;/p&gt;

&lt;p&gt;But that’s a problem for another day.&lt;/p&gt;

&lt;h2 id=&quot;related-previous-posts&quot;&gt;Related previous posts&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2022/11/Fast-zero-copy-static-web-server-with-KTLS.html&quot;&gt;tarweb was first written in C++&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2023/04/Counting-current-live-readers.html&quot;&gt;livecount keeps long lived connection&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/04/io-uring-ktls-and-rust-for-zero-syscall-https-server.html&quot;&gt;tarweb rewritten in Rust, and using io-uring&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2010/05/Redirecting-to-the-closest-site-using-Javascript.html&quot;&gt;You can redirect with Javascript&lt;/a&gt;, but this still has the
&lt;code class=&quot;highlighter-rouge&quot;&gt;norway.example.com&lt;/code&gt; problem.&lt;/li&gt;
  &lt;li&gt;I passed file descriptors between processes &lt;a href=&quot;/2009/03/Moving-a-process-to-another-terminal.html&quot;&gt;in injcode&lt;/a&gt;, but it was
only ever a proof of concept that only worked on 32bit x86, and the code
doesn’t look like it actually does it? Anyway I can’t expect to remember code
from 17 years ago.&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Sun, 26 Oct 2025 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2025/10/The-strange-webserver-hot-potato-sending-file-descriptors.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2025/10/The-strange-webserver-hot-potato-sending-file-descriptors.html</guid>
        
        
        <category>programming</category>
        
        <category>unix</category>
        
        <category>network</category>
        
      </item>
    
      <item>
        <title>Ideal programming language</title>
        <description>&lt;p&gt;My &lt;a href=&quot;/2025/07/Go-is-still-not-good.html&quot;&gt;last post about Go&lt;/a&gt; got &lt;a href=&quot;https://news.ycombinator.com/item?id=44982491&quot;&gt;some
attention&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In fact, &lt;a href=&quot;/static/2025-09-top-two.png&quot;&gt;two of my posts got attention that day&lt;/a&gt;, which broke my nginx
since I was running &lt;a href=&quot;/2023/04/Counting-current-live-readers.html&quot;&gt;livecount&lt;/a&gt; behind nginx, making me run out of
file descriptors when thousands of people had the page opened.&lt;/p&gt;

&lt;p&gt;It’s a shame that I had to turn off livecount, since it’d be cool to see the
stats. But I was out of the country, with unreliable access to both Internet
and even electricity in hotels, so I couldn’t implement the real fix until I
got back, when it had already mostly died down.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static/2025-09-traffic.png&quot;&gt;&lt;img src=&quot;/static/2025-09-traffic.png&quot; alt=&quot;traffic&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I knew this was a problem with livecount, of course, and I even allude to it in
&lt;a href=&quot;/2023/04/Counting-current-live-readers.html&quot;&gt;its blog post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Anyway, back to programming languages.&lt;/p&gt;

&lt;p&gt;The reactions to my post can be summarized as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Oh yes, these things are definite flaws in the language.&lt;/li&gt;
  &lt;li&gt;What you’re saying is true, but it’s not a problem. Your post is pointless.&lt;/li&gt;
  &lt;li&gt;You’re dumb. You don’t understand Go. Here let me explain your own blog post
to you […]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I respect the first two. The last one has to be from people who are too
emotionally invested with their tools, and take articles like this like a
personal attack of some sort. They go out of their way to be offended, and then
start screaming &lt;a href=&quot;https://youtu.be/L3dxMGzt5mU&quot;&gt;“but I don’t fucking want guitar lessons!”&lt;/a&gt;. They want
to counter attack against another programming language, thinking I would take
it personally too. Maybe this heretic is a Java programmer, and that’s why he’s
stupid? (&lt;a href=&quot;/2022/08/Java-a-fractal-of-bad-experiments.html&quot;&gt;bad guess&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;It also reminded me of PHP programmers back in the PHP 3.x days who would die
on the hill of defending PHP as an awesome language, while admitting that they
knew literally no other language. &lt;a href=&quot;https://www.kiplingsociety.co.uk/poem/poems_englishflag.htm&quot;&gt;What should they know of England who only
England know?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’m not offended. Those replies are not offensive; They’re boring. There’s
nothing to learn from the comments, and probably not from the people making
such comments in general, either.&lt;/p&gt;

&lt;p&gt;Also it seems that somebody managed to get my whole Blog comment database
deleted from disqus. Either disqus itself was hacked, or just my account with
them. Or someone tricked disqus into deleting it. They managed to restore it,
though.&lt;/p&gt;

&lt;p&gt;Keeping this third type of commenter in mind, I got an email a few days later
asking what programming languages are “closer to ideal”, and if I maybe have a
blog post in me about that.&lt;/p&gt;

&lt;p&gt;I don’t know who’s asking, so I replied the long version, while still taking
the question at face value.&lt;/p&gt;

&lt;h2 id=&quot;my-reply-after-minor-edits&quot;&gt;My reply (after minor edits)&lt;/h2&gt;

&lt;p&gt;Ideal… Well, this is getting into the space of “what is the best
programming language”, which doesn’t have a perfect answer. To do
what?&lt;/p&gt;

&lt;p&gt;To make an Android app (something I’m not an expert in. I’ve just made
one simple one), I think Kotlin seems nice. But I don’t know it very
well.&lt;/p&gt;

&lt;p&gt;For web development, something I also don’t do much, it’s probably
Typescript.&lt;/p&gt;

&lt;p&gt;For maximum portability for systems programming, C or C++ (depending
on if all your target platforms (e.g. embedded stuff) support C++) is
probably best.&lt;/p&gt;

&lt;p&gt;But these are practical answers. Some people like Lisp. Others like Haskell.
Rust strikes a good position between practical, low level control, safe, and a
high level type system. If the Rust compiler supports your platform, then it’s
pretty much as portable as C/C++.&lt;/p&gt;

&lt;p&gt;I’ve written about the deficiencies of &lt;a href=&quot;/2022/08/Java-a-fractal-of-bad-experiments.html&quot;&gt;Java&lt;/a&gt; and &lt;a href=&quot;/2025/07/Go-is-still-not-good.html&quot;&gt;Go&lt;/a&gt; because the
ways they are deficient are interesting. I don’t find the ways C++ is deficient
to be interesting. C++ is what it is. I happen to enjoy coding C++.&lt;/p&gt;

&lt;p&gt;I also have thoughts about the trap of &lt;a href=&quot;/2021/06/The-uselessness-of-bash.html&quot;&gt;accidentally writing too much in
bash&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have a draft of things wrong with Rust. But so far I think they are
all fixable. (e.g. no placement syntax has been defined &lt;em&gt;yet&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;But I have no interest in writing a blog post about the lack of memory
safety of C++. It is what it is.&lt;/p&gt;

&lt;p&gt;Java’s deficiencies are interesting because they are the best guesses
of the future that 1990 had to offer. And those guesses were almost
all wrong.&lt;/p&gt;

&lt;p&gt;Go’s deficiencies are interesting/frustrating because it (almost entirely) is
the best that the 1980s-early 1990s had to offer. And yet it launched in 2009.&lt;/p&gt;

&lt;h2 id=&quot;enjoyment&quot;&gt;Enjoyment&lt;/h2&gt;

&lt;p&gt;I don’t enjoy coding Javascript. So I’m experimenting writing frontend stuff in
Rust and compile to WASM. So far it works for me, but is not something I’d
recommend for anyone who wants to get anything done.&lt;/p&gt;

&lt;p&gt;But no, I won’t be writing up which programming language is “ideal”,
because it’s one of those “it depends”.&lt;/p&gt;

</description>
        <pubDate>Sun, 07 Sep 2025 00:00:00 +0000</pubDate>
        <link>https://blog.habets.se/2025/09/Ideal-programming-languages.html</link>
        <guid isPermaLink="true">https://blog.habets.se/2025/09/Ideal-programming-languages.html</guid>
        
        
        <category>programming</category>
        
      </item>
    
  </channel>
</rss>
