How to Audit a Device for GPL Compliance: A Hardware-First Approach

I probed the SPI flash on a commercial IoT gateway last month and found a 3.18 kernel with no corresponding source on the vendor’s website. No tarball, no written offer, just a binary blob and a shrug from their support team. That’s not an edge case—it’s the default for too many embedded Linux devices I’ve torn down over the past decade. Auditing for GPL compliance isn’t about legal theory. It’s about reading chips, extracting filesystems, and matching the actual build artifacts to the source code the vendor claims to ship. This article lays out the hardware-first methodology I use when a new device lands on my bench, from physical probing to the final compliance report.

GPL compliance in embedded systems lives at the intersection of firmware extraction, toolchain forensics, and supply-chain documentation. The GPLv2 and GPLv3 require that anyone distributing binary firmware also provide the corresponding source code—scripts, patches, build instructions, the works—to anyone who gets the binary. In industrial IoT and medical devices, where Linux runs inside ventilators, PLCs, and connected sensors, non-compliance isn’t just a license violation. It’s a safety and maintainability nightmare. When a hospital can’t audit the kernel on an infusion pump, vulnerability management turns into guesswork. My audits rest on three pillars: verifying the binary matches the offered source, checking that build scripts are complete, and flagging proprietary lock-downs that break the license.

Step 1: Physical Reconnaissance and Firmware Extraction

Before I touch a line of code, I open the enclosure. The board tells you what you’re up against. I note the main SoC, the flash type—eMMC, NAND, NOR, SPI—and hunt for debug interfaces: UART pads, JTAG headers, unpopulated SD card slots. On a recent smart thermostat audit, the vendor swore the device used a “secure boot chain” that blocked firmware extraction. I spotted an unpopulated 4-pin header marked TX/RX/GND/VCC, soldered a lead, and got a U-Boot console with no password. The boot log spilled the entire kernel command line, including the NAND partition map. From there, reading raw flash was straightforward with a $15 CH341A programmer and a SOIC clip.

For devices with eMMC or managed NAND, I grab an SD card sniffer or a low-level eMMC reader. The Exploitee.rs community keeps a solid database of hardware hacking techniques for common consumer and industrial boards. Once I have a raw dump, I carve it with binwalk to pull out filesystems, kernels, and bootloaders. The goal is to reconstruct the exact binary image the device runs—not a lookalike from the vendor’s GPL drop, but the actual bits on the flash. Discrepancies start right there.

Step 2: Source Code Offerings and the Written Offer Trap

Section 6(b) of GPLv2 and Section 6 of GPLv3 let distributors provide source code via a written offer instead of bundling it with the device. I’ve seen this abused more times than I can count. The pattern: the device manual lists a URL that 404s, or an email address that auto-responds with “we’ll get back to you” and never does. I document every attempt—timestamps, email threads, HTTP response codes—because a broken written offer is a license termination event. If the vendor can’t deliver source within a reasonable time, their right to distribute the GPL-covered binaries evaporates until they fix the violation.

When source does show up, I don’t trust it. I compare the tarball’s kernel config against /proc/config.gz pulled from the running device (or extracted from the kernel image via scripts/extract-ikconfig). I diff the module symbols against the actual .ko files on the rootfs. I check whether the toolchain used to compile the source produces bit-identical binaries—rarely perfect, but close enough to verify compiler version, architecture flags, and build paths. In one medical device audit running Linux 4.1, the vendor’s source tarball compiled to a kernel 300KB larger than the shipped binary. The difference? They’d stripped out the GPL-licensed Wi-Fi driver and swapped in a proprietary module loaded via insmod, which taints the kernel and violates the GPL’s derivative-work clause. The vendor’s legal team didn’t grasp that dynamic linking to a GPL kernel creates a combined work. I spelled it out with lsmmod output and kernel symbol tables.

Step 3: Build Scripts and the “Corresponding Source” Problem

GPLv2 Section 3 and GPLv3 Section 1 define “corresponding source” as all the source code needed to generate, install, and run the binary. That means toolchain configuration, build scripts, and any scripts that control compilation. I’ve received source drops that were just a kernel tarball with no .config, no cross-compiler instructions, and no rootfs build system. That’s not compliance—it’s a homework assignment. For embedded Linux, the build system is often Yocto, Buildroot, or OpenWrt. If the vendor used Yocto, I expect the BSP layer, not just the kernel source. I’ve had to reverse-engineer entire Yocto layers from a binary rootfs because the vendor claimed their “build environment was proprietary.” The GPL doesn’t care about your internal CI pipeline; if it’s needed to produce the binary, it’s part of the corresponding source.

I lean on a few tools to verify build reproducibility. For the kernel, I extract the config and run a cross-compile with the same toolchain version (identified via strings on a binary). For the rootfs, I use Buildroot’s legal-info target or Yocto’s archiver class to check whether the vendor’s source drop matches the binary packages. When it doesn’t, I document the exact missing pieces—often proprietary userspace daemons linked against GPL libraries, or kernel modules with no source. The Software Freedom Conservancy’s Copyleft Compliance Guide is a practical reference I keep bookmarked for these discussions.

Step 4: Identifying Derivative Works and Tivoization

GPLv3 introduced anti-tivoization language in Section 6, stopping distributors from locking down hardware to reject modified versions of GPLv3 software. I’ve run into this on several medical devices and set-top boxes where the bootloader verifies a signed kernel image and refuses to boot unsigned code. If the kernel is GPLv3—or if the vendor ships GPLv3 userspace components—that signature check must be user-disableable. I test this by compiling a modified kernel with a printk banner, signing it with my own key, and attempting to flash it. If the device bricks or refuses to boot, I document the exact failure mode: secure boot key storage, eFuse configuration, boot ROM behavior. One industrial gateway I audited used a GPLv3 U-Boot but had a locked boot ROM that only accepted images signed by the vendor’s private key. The vendor argued the boot ROM was “hardware” and exempt. I pointed to the FSF’s guidance that if the boot ROM is field-updateable and the signing key is under the vendor’s control, it’s a tivoization violation. The vendor eventually released a firmware update allowing user-controlled keys.

Step 5: Documenting and Reporting Findings

My audit reports follow a strict structure: device identification (model, revision, firmware version), extraction methodology, binary analysis, source comparison, and compliance gaps. Each gap gets a severity rating and a specific remediation step. I include raw console logs, hex dumps, and diff outputs. This isn’t about shaming vendors—it’s about giving them a clear path to fix the problem. When a vendor ignores the report, I escalate to the copyright holders whose code is being distributed without source. The Software Freedom Conservancy and gpl-violations.org have both been effective in enforcement actions I’ve supported.

For engineers and compliance officers reading this, the single most important takeaway is: audit the binary, not the source drop. The binary on the flash is the truth. The source tarball is a story the vendor tells. Your job is to check if the story matches reality.

Common Compliance Gaps I’ve Found in the Field

Missing Kernel Configs and Toolchains

I pulled the kernel image from a patient monitoring system and found it was compiled with GCC 4.9.3, arm-linux-gnueabihf, and a custom patchset for a touchscreen controller. The vendor’s source drop included a 3.14 kernel tarball with no .config and no mention of the touchscreen patches. Without the exact config and patches, the source is useless for rebuilding or auditing. I extracted the config from /proc/config.gz and the patches by diffing against the vanilla kernel—but that’s my work, not the vendor’s compliance.

Proprietary Kernel Modules

On an industrial gateway running Linux 4.4, I found a kernel module for a proprietary CAN bus controller. The module was loaded at boot and tainted the kernel. The vendor’s source drop included only the GPL-licensed kernel tree, not the module source. When confronted, they claimed the module was a “userspace driver” communicating via mmap. I dumped the module’s .modinfo and showed it was a kernel module, not a userspace driver. The vendor eventually released the source under GPLv2 after I involved their legal department.

Obfuscated Scripts and Binary Blobs

I once audited a consumer NAS device that shipped with a rootfs containing obfuscated shell scripts and precompiled Python bytecode. The vendor argued that the .pyc files were the “preferred form for modification.” I decompiled them with uncompyle6, showed the original source was trivially recoverable, and demonstrated that the obfuscated shell scripts were just base64-encoded binaries. The GPL requires the preferred form for modification—not the obfuscated form the vendor prefers. The vendor settled and released clean source.

FAQ: GPL Compliance Audits for Embedded Devices

What’s the first thing I should check when I receive a device for audit?

Open the enclosure and identify the flash storage. Look for debug interfaces—UART, JTAG, unpopulated headers. Dump the firmware directly from the flash chip if possible; if not, use the debug console to interrupt the bootloader and extract images via TFTP or SD card. The physical device is the ground truth. Never start with the vendor’s source drop.

How do I know if a vendor’s source code offering is complete?

Rebuild the binary from the provided source and compare it to the running firmware. At minimum, the kernel config must match, the module symbols must align, and the rootfs packages must be reproducible. If the vendor uses Yocto or Buildroot, ask for the full BSP layer, not just individual package tarballs. A source drop that can’t produce the shipped binary is incomplete.

What’s the difference between GPLv2 and GPLv3 for embedded devices?

GPLv3 adds explicit anti-tivoization language (Section 6) that requires installation information for user-modified software. If a device runs GPLv3 code and uses signing keys to prevent modified firmware from booting, the vendor must provide a way to disable that check or sign user-provided images. GPLv2 doesn’t have this requirement, but many devices mix GPLv2 and GPLv3 components, making the stricter terms apply to the combined work.

What tools do you use for firmware extraction and analysis?

I rely on a CH341A programmer with SOIC clips for SPI flash, binwalk for carving filesystems, and a J-Link for JTAG access. For software analysis, I use the kernel’s scripts/extract-ikconfig, lsmmod, and strings. For rootfs comparison, Buildroot’s legal-info and Yocto’s archiver class are essential. I also keep a collection of cross-compilers for ARM, MIPS, and x86 to verify build reproducibility.

Next Steps: Building a Compliance Program

If you’re an engineer at a company shipping embedded Linux devices, don’t wait for an audit demand. Set up an internal compliance process now. Start by inventorying every GPL component in your firmware—kernel, bootloader, libraries, userspace tools. Automate source tarball generation in your CI pipeline. Test that the generated source actually builds the shipped binary. Document your toolchain, build scripts, and configuration files. When a customer or a compliance investigator comes asking, you’ll have everything ready. And if you’re on the receiving end of a non-compliant device, the methodology above will give you the evidence you need to demand proper source release. The GPL isn’t a suggestion—it’s a condition of the license you received when you bought that hardware.

Close-up of a circuit board with a flash memory chip and test probes attached for firmware extraction
Engineer using a logic analyzer to debug an embedded Linux device on a workbench
Screwdriver opening the enclosure of an industrial IoT gateway for hardware inspection