\

Bytecode VMs in surprising places (2024)

108 points - last Friday at 2:12 PM

Source
  • drob518

    today at 1:22 PM

    On one hand, all these mini interpreters and compilers are cool. I have a soft spot for extensible systems. On the other hand, all these things are a huge security problem. When every subsystem and data format is carrying around its own Turing complete bytecode and JIT, they all need to be secure and bug free for the system to be secure and bug free. And that far more code surface to keep clean.

  • jaen

    today at 3:00 PM

    References for the Quake virtual machines:

    Quake 1 had QuakeC: [1] https://en.wikipedia.org/wiki/QuakeC [2] Hello world in QuakeC - https://www.leonrische.me/pages/quakec_bytecode_hello_world....

    Quake 2 moved to native binaries.

    Quake 3 had a new VM that enabled compiling regular C using LCC: [1] https://fabiensanglard.net/quake3/qvm.php [2] Spec - https://www.icculus.org/~phaethon/q3mc/q3vm_specs.html

    • superjan

      today at 11:30 AM

      How about the infamous iOS hack with a VM implemented in a JBIG2 PDF? https://projectzero.google/2021/12/a-deep-dive-into-nso-zero...

      • chirsz

        today at 11:22 AM

        SBus peripherals use the Forth language in their PROMs to initialize themselves[1].

        [1] https://docs.oracle.com/cd/E19957-01/802-3239-10/sbusandfc.h...

          • DonHopkins

            today at 11:48 AM

            Good call! (Whether it's a directly threaded, indirectly threaded, subroutine threaded, token threaded, Huffman threaded, or string threaded call.)

            https://en.wikipedia.org/wiki/Threaded_code#Token_threading

            Mitch Bradley created OpenFirmware. It started at Sun as OpenBoot (informally "SunForth") on the SPARCstation 1 in 1989, was standardized as IEEE 1275-1994, and was renamed OpenFirmware at that time. Its lineage runs back through Mitch's earlier Forthmacs (Bradley Forthware, early 80s), which ran on 68k Macs, Sun-2/3, Atari ST, and Amiga. Mitch credits Henry Laxen and Michael Perry's F83 and Glen Haydon's MVP-Forth as the public-domain ancestors.

            The metacompiler can target many platforms, word sizes, CPUs, and threading models, and produce stripped ROMable images. It can build the kernel as direct-threaded (DTC), indirect-threaded (ITC), subroutine-threaded (STC), or token-threaded (TTC), with 16, 32, or 64 bit cells. Shipping kernels are DTC native code with cell-sized xt pointers: 32 bit on the original SPARC and PowerPC machines, 64 bit on modern PPC64, SPARC64, and ARM64 builds.

            Peripheral expansion cards ship a separate, portable, variable-byte token format called FCode. The kernel interprets FCode at boot/probe time and recompiles it on the fly into the live native dictionary. After probe, FCode-loaded drivers run as ordinary native Forth words. That two-stage design (fast native runtime, portable FCode transport) is what let Sun ship one card PROM image that worked across CPU generations.

            https://github.com/MitchBradley

            https://github.com/MitchBradley/openfirmware

            FCode was designed for SBus on the SPARCstation 1, with cross-CPU portability built in. Sun's earlier and contemporary buses were not interchangeable with SBus (Sun-2 used Multibus, Sun-3 used VMEbus, the Sun386i "Roadrunner" used AT-bus), so the cross-architecture payoff arrived later, when IEEE 1275-1994 standardized OpenFirmware and PCI allowed FCode in option ROMs. After that, the same expansion-card PROM image could boot on Sun SPARC, Apple PowerPC Macs, IBM PowerPC servers (CHRP), and the OLPC XO.

            Interview with Mitch Bradley (he's like the Woz of Forth):

            https://web.archive.org/web/20120118132847/http://howsoftwar...

            In parallel with the OpenBoot work, Mitch also developed an extremely portable C-based Forth (the public version is "C Forth 93"). It runs a switch-threaded inner interpreter over packed tokens, with configurable cell width (16, 32, or 64 bit) and configurable token width (pointer-sized by default, 16 bit with the T16 build flag for tight flash budgets), plus a small hand-rolled FFI built around a fixed-arity 12-argument marshalling trampoline driven by a format string. It is now the embedded variant used in OLPC's OpenFirmware and in PlatformIO targets including RP2040, Teensy, ESP32, ESP8266, and STM32:

            https://github.com/MitchBradley/cforth

            OpenFirmware even has its own song:

            https://www.youtube.com/watch?v=b8Wyvb9GotM

            More on Mitch, OpenFirmware, and CForth:

            https://news.ycombinator.com/item?id=21822840

            https://news.ycombinator.com/item?id=33681531

            https://news.ycombinator.com/item?id=38689282

              • anthk

                today at 12:50 PM

                I ran EForth under the Subleq from Howe R.J at https://github.com/howerj/muxleq (the subleq one) first at QuickJS (trivial tasks, almost a 1:1 map from the C code, made in a hurry) and under... jsinterp.py from the infamous yt-dlp but using arrays instead of printing functions. But... if yt-dlp's "mini-JS" implements some captcha input functions... you can add I/O with ease and run EForth with what they call (not me) a "Not totally functional interpreter".

                Not totally... until people there run the 110 rule program, Conway's Life, Subleq+EForth...

                  • DonHopkins

                    today at 1:18 PM

                    You may need to write a WebGPU shader and run it in a Beowulf Cluster to make that run fast!

        • raddan

          today at 5:41 PM

          I was told by an engineer at Microsoft that Excel's formula interpreter is essentially a kind of bytecode-based stack machine. This came up in the context of a bug I found (while working on a project with Microsoft) that revealed that not only was there a small floating-point bug in some calculations, but (improbably, to me) that Excel preserved this inaccuracy across architectures for decades. So the bytecode interpreter made sense. That said, I've never seen this implementation myself, so it may still be rumor.

          • magnat

            today at 11:13 AM

            Some other examples:

            - ACPI configuration for power management and platform stuff [1]

            - Bitcoin transactions [2]

            - TrueType fonts [3]

            [1] https://wiki.osdev.org/AML

            [2] https://en.bitcoin.it/wiki/Script

            [3] https://learn.microsoft.com/en-us/typography/opentype/spec/t...

              • m132

                today at 12:20 PM

                Since ACPI was mentioned, let's not forget about EFI!

                https://uefi.org/specs/UEFI/2.10/22_EFI_Byte_Code_Virtual_Ma...

                  • segbrk

                    today at 5:48 PM

                    Since that page is a little dense, the higher-level version: PCI supports Option ROMs (OpRoms) - plug in device like a NIC or a GPU, your BIOS actually loads compiled code from it and executes it on the CPU. In many systems for example PXE booting (net booting) is actually a function of the NIC, executing code on the CPU to load an operating system. We're talking actual x86/x86_64 machine code here running in the privileged pre-boot environment. Not portable or secure in any way. OpRoms _may_ now be checked for SecureBoot signatures on systems where that's set up properly at least.

                    EFI ByteCode (EBC) is meant to help at least the portability side. I'm not sure if anybody is actually delivering devices with EBC OpRoms yet though. I'm also not sure if anybody is looking at using the EBC VM to sandbox untrusted OpRoms.

            • tptacek

              today at 5:36 PM

              More surprising to me than the BPF VM itself is the optimizing compiler for it that lives in libpcap.

              • pratikdeoghare

                today at 9:39 AM

                There is one in golang regular expressions https://swtch.com/~rsc/regexp/regexp2.html

                I guess that is why you say re.Compile.

                  • rhdunn

                    today at 9:50 AM

                    That goes back to Ken Thompson's NFA regex interpreter from 1968 [1], [2], [3]. Note: that whole regex series by Russ Cox [4] is great.

                    [1] https://dl.acm.org/doi/10.1145/363347.363387 -- Programming Techniques: Regular expression search algorithm

                    [2] https://swtch.com/~rsc/regexp/regexp1.html -- Regular Expression Matching Can Be Simple And Fast

                    [3] https://swtch.com/~rsc/regexp/regexp2.html -- Regular Expression Matching: the Virtual Machine Approach

                    [4] https://swtch.com/~rsc/regexp/ -- Implementing Regular Expressions

                      • kqr

                        today at 11:26 AM

                        I second the Russ Cox recommendation. I read that ages ago and that was what made me realise some theory could actually be useful in practice.

                    • pjc50

                      today at 9:48 AM

                      All regular expressions are deterministic final automata https://en.wikipedia.org/wiki/Deterministic_finite_automaton (finally, a use for my CS course); the extent to which that counts as a virtual machine varies. Some of the regex syntaxes extend it in ways which don't fit in a DFA and do count as a VM; Perl-compatible RE used to be popular (e.g. in Exim).

                        • titzer

                          today at 11:53 AM

                          It's easier to construct NFAs directly from regular expression definitions (rather than DFAs) because implementing the choice operator is easier. We can convert from NFA to DFA with worst-case exponential blowup.

                      • sureglymop

                        today at 9:44 AM

                        Interesting. Not that surprising that it works like this. But isn't it a little surprising that things like regexes, printf syntax and other DSLs aren't mostly handled and parsed at compile time in 2026?

                          • pjc50

                            today at 10:57 AM

                            Kind of language-dependent since regexes are normally specified as strings and most languages are pretty weak at "run this code at compile time". One of the things Rust users are fond of.

                            C# is in the middle on this one, where specific features get compile-time support and regex is one of them: https://www.devleader.ca/2026/05/03/c-regex-performance-gene...

                            I have also built a C# source generator myself (XML parser generator), but the developer experience is a bit of a hill to climb compared to what it could be.

                    • majorbugger

                      today at 5:53 PM

                      Does it mean we can play Doom on WinRar?

                      • pervasif

                        today at 2:49 PM

                        These little VMs in applications are everywhere. Apple Mach-O binaries have built in opcodes for binding and rebasing symbols interpreted by (numerous) little VMs in dyld:

                        https://github.com/apple-oss-distributions/dyld/blob/e9da5ae...

                        https://github.com/apple-oss-distributions/dyld/blob/e9da5ae...

                        Their use is less common now since the introduction of the mach-o load command LC_DYLD_CHAINED_FIXUPS, but these opcodes still have to be supported for older binaries. Also, some popular compilers including Zig still emit these opcodes for LC_DYLD_INFO and LC_DYLD_INFO_ONLY.

                        • kazinator

                          today at 2:39 PM

                          Busicom 141 PF calculator (1971). This was a product built on the Intel 4004 processor. It was not programmed using Intel 4004 machine langauge directly, but using a more powerful machine language for which the 4004 ran an intepreter included in the image.

                          • ivankelly

                            today at 9:50 AM

                            Quake had it’s own vm also

                              • today at 2:59 PM

                            • twic

                              today at 2:36 PM

                              The Python pickle format is a bytecode [1], although not a Turing-complete one, I think.

                              [1] https://formats.kaitai.io/python_pickle/

                                • hansvm

                                  today at 6:37 PM

                                  Pickle is definitely turing-complete. It's a super easy way to RCE your system.

                              • dlojudice

                                today at 1:23 PM

                                Another World (Out of this world) game had its own bytecode [1]

                                [1] https://github.com/fabiensanglard/Another-World-Bytecode-Int...

                                • self_awareness

                                  today at 10:13 AM

                                  RarVM was used in a previous version of the format, newest RAR has removed it, and RarV5 doesn't have a VM.

                                  • omeid2

                                    today at 9:36 AM

                                    This list is entirely incomplete without mentioning Java Card.

                                    There is a tiny Java Bytecode VM in an insanely large list of places, you can find some of them here:

                                    https://github.com/crocs-muni/javacard-curated-list https://en.wikipedia.org/wiki/Java_Card

                                    • ignoramous

                                      today at 9:31 AM

                                      TikTok shipping XOR cipher'd bytecode & interp is right up there: https://news.ycombinator.com/item?id=34109771

                                    • anthk

                                      today at 10:20 AM

                                      yt-dlp's jsinterp.py

                                      https://jxself.org/compiling-the-trap.shtml

                                      I've got subleq+eforth (https://github.com/howerj/muxleq) running in JS which is dead simple to do. No input but I could output ASCII mapping values to an array.

                                      https://esolangs.org/wiki/Subleq

                                      So, yes. yt-dlp runs propietary Youtube JS code defying the original purpose.

                                        • faangguyindia

                                          today at 3:33 PM

                                          Why youtube does not use tls fingerprint to block ytdlp?

                                            • pocksuppet

                                              today at 4:38 PM

                                              possibly because yt-dlp updates rapidly and would simply switch to the correct fingerprint, but Google-approved clients use many different and uncontrollable fingerprints (as they use OS TLS facilities for example).

                                              • wiseowise

                                                today at 4:35 PM

                                                Hopefully, an iota of decency.

                                        • dsecurity49

                                          today at 9:51 AM

                                          [flagged]