How to Audit a Device for GPL Compliance: A Field Guide for Embedded Engineers

GPL compliance auditing is the process of verifying that a shipping embedded product actually delivers the source code, build scripts, and license notices that the GPL requires. It sits at the intersection of firmware forensics, supply-chain archaeology, and legal risk management. For industrial IoT gateways, consumer routers, and medical monitoring hardware, a missed GPL obligation can turn a successful product launch into a cease-and-desist letter, a lost distribution right, or a forced recall. I have spent enough nights with a logic analyzer clipped to a NAND flash to know that the truth is rarely in the datasheet. It is in the firmware image, the boot log, and the vendor’s willingness to answer a direct question.

This article is a practical audit workflow. I will walk through the evidence you need to collect, the tools I use, the places where vendors hide modified GPL components, and the questions that separate a clean bill of health from a compliance time bomb. I will be blunt about proprietary lock-in where the evidence supports it, but I will also show you how to document your findings so that an engineering team can act on them without a lawyer in the room.

Start with the Legal Baseline: What the GPL Actually Demands

Before you open a single binary, you need to know what you are looking for. The GPL is not a “credit the author” license. It is a conditional distribution license. If a vendor distributes a device containing GPL-covered code, the vendor must provide the corresponding source code, the scripts used to control compilation and installation, and a copy of the license text. The source must be in the preferred form for modification, not a decompiled approximation.

For embedded devices, the most common GPL versions are GPLv2 and GPLv2-or-later. The Linux kernel is GPLv2-only. BusyBox is GPLv2-or-later. U-Boot is GPLv2-or-later. glibc is LGPL, which has different obligations. A device that ships with a Linux kernel, BusyBox, and a proprietary userspace application is a textbook compliance case: the kernel and BusyBox are GPL-covered, and the vendor must offer source for those components, including any modifications.

Adjacent concepts matter here. “Corresponding source” includes the toolchain configuration, kernel .config, device tree sources, and any patches applied to upstream projects. “Complete and corresponding” is the phrase that trips up vendors who ship a tarball of vanilla upstream source without their own modifications. I have seen vendors ship a pristine Linux 4.9 tarball for a device running a heavily patched 4.9 kernel with custom drivers. That is not compliance. That is a box-ticking exercise that fails the moment you diff the tarball against the actual firmware.

Collect the Physical Evidence First

An audit starts with the device in your hands, not with a vendor’s website. I begin with a full inventory of the hardware: SoC, flash type, RAM, network interfaces, and any removable storage. Then I capture the firmware image. For devices with a UART header, I connect a USB-to-serial adapter and capture the boot log. The boot log often reveals the exact kernel version, the U-Boot version, the partition layout, and the command line arguments. That is free evidence.

For the firmware image itself, the method depends on the flash. For SPI NOR flash, I use a SOIC-8 clip and a programmer like a CH341A or a Dediprog SF100. For eMMC or NAND, I look for a recovery mode, a U-Boot shell, or a vendor update file. I probed the SPI flash on a consumer router last year and found a 16 MB image that contained a full BusyBox rootfs, a modified dropbear SSH server, and a proprietary cloud agent. The vendor’s website offered a “GPL source” link that pointed to a 404 page. The physical image told the real story.

Once you have the image, extract it. Binwalk is the first tool I reach for. It identifies embedded filesystems, compressed kernels, and bootloaders. For a typical Linux device, you will find a U-Boot image, a compressed kernel, and a SquashFS or JFFS2 root filesystem. Extract the rootfs and start looking for GPL-covered binaries.

Map the GPL Surface Area

The GPL surface area is the set of components in the firmware that are covered by the GPL or LGPL. I build a table with four columns: component, version, license, and evidence. The evidence is the binary itself, a version string, a copyright notice, or a package manager database. For a BusyBox-based rootfs, the evidence is usually the BusyBox binary and its applet list. For the kernel, the evidence is the kernel version string in the boot log or the image header.

Common GPL-covered components in embedded Linux devices include:

  • Linux kernel — GPLv2-only, with vendor-specific drivers and device tree modifications.
  • BusyBox — GPLv2-or-later, often heavily modified for size or feature removal.
  • U-Boot — GPLv2-or-later, with board-specific initialization code.
  • gcc runtime libraries — GCC Runtime Library Exception, but the toolchain itself is GPLv3 with exceptions.
  • glibc or musl — LGPL, which requires relinking ability but not full source for proprietary applications.
  • OpenSSL — Apache 2.0 with an exception, but older versions may be OpenSSL/SSLeay licensed.
  • dropbear — MIT-style, but often linked against GPL libraries.
  • iptables, ebtables, iproute2 — GPLv2, common in network-facing devices.

I also look for GPL components that are not obvious. A proprietary application may statically link against a GPL library. A vendor may ship a Python script that imports a GPL module. A device may include a GPL-licensed font or a GPL-licensed compression library. The audit is not complete until you have checked the dynamic linker output for every binary on the rootfs.

Diff the Firmware Against the Offered Source

This is where most audits fail. The vendor offers a source tarball. You download it. You build it. You compare the resulting binaries against the firmware. If the binaries match, the vendor is likely compliant for that component. If they do not match, the vendor has shipped a modified version without releasing the modifications.

I use a build environment that matches the vendor’s toolchain as closely as possible. For a MIPS-based router, that means a MIPS cross-compiler, the vendor’s kernel .config, and the same BusyBox configuration. I build the kernel and BusyBox, then compare the resulting binaries with the firmware images. A byte-for-byte match is rare because of build timestamps and environment differences, but a functional match is usually enough. I compare the symbol tables, the version strings, and the behavior of key functions.

I probed the SPI flash on a medical monitoring device and found a kernel with a custom driver for a proprietary sensor. The vendor’s source tarball contained a vanilla kernel with no sensor driver. The vendor claimed the driver was “not GPL.” The driver was a kernel module, compiled against the kernel headers, and loaded at boot. That is a derivative work of the kernel under GPLv2. The vendor’s position was not defensible. The audit report forced a source release within six weeks.

Check the Bootloader and the Toolchain

U-Boot is GPLv2-or-later. A vendor that ships a modified U-Boot must release the source for the board-specific initialization code, the environment handling, and any custom commands. I have seen vendors ship a U-Boot binary with a custom “factory reset” command that writes to a hidden partition. The source for that command was not in the vendor’s GPL tarball. That is a violation.

The toolchain is a subtler issue. The GPL requires the “scripts used to control compilation and installation.” For a device built with a vendor-specific toolchain, that means the toolchain configuration, the build scripts, and any patches to the compiler or linker. A vendor that ships a binary-only toolchain and a tarball of source code is not compliant. The source must be buildable with the same toolchain, or the toolchain itself must be released under the GPL if it contains GPL-covered components.

I check the toolchain by looking at the build ID in the binaries. The build ID often contains the compiler version and the build path. If the build path points to a vendor’s internal build server, the vendor has not released the build environment. That is a red flag, not a smoking gun, but it tells you where to dig.

Document the Findings for Action

An audit report is not a legal brief. It is an engineering document that tells a vendor exactly what to fix. I structure my reports as a table of findings, each with a severity rating, a technical description, and a recommended remediation. A typical finding looks like this:

Finding 3: Modified BusyBox without corresponding source. The firmware contains BusyBox v1.31.1 with a custom applet for reading a proprietary sensor. The vendor’s GPL tarball contains vanilla BusyBox v1.31.1 without the custom applet. The custom applet is a derivative work of BusyBox and must be released under GPLv2-or-later. Severity: High. Remediation: Publish the modified BusyBox source, including the custom applet, on the vendor’s GPL source page.

I also include a compliance checklist that maps each GPL-covered component to the specific source file, build script, and license notice that the vendor must provide. The checklist is the actionable output. A vendor can hand it to an engineer and say, “Fix these items.”

Common Vendor Excuses and How to Counter Them

Vendors have a playbook of excuses. I have heard most of them. Here are the ones that come up most often, and the technical evidence that counters them.

“We only used the kernel, and the kernel is not modified.”

Check the kernel version string. If the vendor’s kernel reports a version like 4.9.198-vendor-7, the vendor has modified the kernel. The -vendor-7 suffix is a local version string that does not appear in the upstream kernel. The vendor must release the source for that modified kernel, including the local version string and any patches.

“Our application is a separate work, not a derivative.”

This is a legal argument, not a technical one. The technical evidence is the dynamic linker output. If the application links against GPL libraries, the vendor’s argument is weak. If the application is a kernel module, the argument is nearly always wrong. I document the linkage and let the lawyers argue about derivative works.

“We offer source on request.”

The GPL allows a written offer to provide source, but the offer must be valid for three years and must be honored. A vendor that says “source on request” but does not respond to requests is not compliant. I test the offer by requesting the source. If the request goes unanswered for 30 days, I document the failure.

“The source is too large to host.”

A Linux kernel source tarball is about 100 MB. A full embedded Linux build tree is a few gigabytes. That is not too large to host. Vendors that use this excuse are usually trying to avoid the work of cleaning up their build tree. The GPL does not allow a vendor to avoid the obligation because it is inconvenient.

Tools I Use in Every Audit

My audit toolkit is small and mostly open source. I use binwalk for firmware extraction, dd and mount for filesystem access, readelf and objdump for binary analysis, and strings for version strings and copyright notices. For diffing source trees, I use diff and git. For building vendor source, I use a clean Debian container with the vendor’s toolchain installed. I keep a separate container for each audit to avoid cross-contamination.

For UART access, I use a CP2102 USB-to-serial adapter and a logic analyzer for higher-speed interfaces. I probed the SPI flash on a consumer electronics device and found a hidden partition with a GPL-licensed bootloader that the vendor had not disclosed. The logic analyzer showed the boot sequence reading from that partition before the main firmware. That evidence forced a source release for the bootloader.

I also use license scanners like FOSSology and ScanCode, but I treat their output as a starting point, not a conclusion. A scanner can miss a modified GPL component that has been stripped of its copyright notices. A scanner can also flag a false positive when a proprietary binary contains a GPL version string in an embedded help text. The scanner is a triage tool. The human review is the audit.

What a Clean Audit Looks Like

A clean audit is rare, but it does happen. I audited an industrial IoT gateway last year that shipped with a complete GPL source tarball, a buildable toolchain, and a written offer that was honored within 48 hours. The vendor had a compliance officer who understood the GPL. The source tarball matched the firmware. The build scripts were complete. The license notices were present in the device UI and the printed manual. That vendor earned my respect, and I told them so.

The difference between that vendor and the ones I have had to escalate is not legal sophistication. It is engineering discipline. The compliant vendor treated GPL source release as a build artifact, not an afterthought. The non-compliant vendors treated it as a legal nuisance. The audit is the tool that exposes the difference.

FAQ: GPL Compliance Auditing for Embedded Devices

What is the first thing I should check when auditing a device for GPL compliance?

Start with the boot log and the firmware image. The boot log reveals the kernel version, the bootloader version, and the partition layout. The firmware image reveals the root filesystem and the GPL-covered binaries. Together, they give you a map of the GPL surface area before you ever contact the vendor.

How do I know if a vendor has modified a GPL component?

Build the vendor’s offered source and compare the resulting binaries against the firmware. If the binaries do not match, the vendor has shipped a modified version. Also check the version strings. A kernel version like 4.9.198-vendor-7 indicates a local modification that must be released.

What should I do if a vendor ignores a GPL source request?

Document the request and the lack of response. The GPL requires a written offer to be honored for three years. If the vendor does not respond within a reasonable time, the audit report should flag the failure as a high-severity finding. Escalation to a legal team or a compliance enforcement organization is the next step.

Can a proprietary application link against GPL libraries without becoming GPL?

Generally no. If a proprietary application links against a GPL library, the combined work must be distributed under the GPL. The vendor can avoid this by using LGPL libraries, which allow proprietary linking with certain conditions, or by keeping the GPL components in separate processes. The dynamic linker output is the technical evidence that determines the answer.

Next Steps for This Blog

This audit workflow is the foundation for a series of deeper dives. The next article will cover how to extract and analyze a U-Boot image, including the environment variables, the boot commands, and the hidden partitions that vendors use to store proprietary code. After that, I will write about building a reproducible GPL compliance build environment using containers and pinned toolchains. If you have a device you want audited, send me the model number and the firmware version. I will add it to the queue.

Close-up of a circuit board with a microchip and electronic components
Engineer inspecting a printed circuit board with a probe
Soldering iron and electronic components on a workbench