Source security audit
A multi-angle audit of the vendored
lha source: dangerous-function grep, library
surface, runtime permissions, temp-file safety, and attribution
chain.
Static analysis only — no execution, no network, no fuzzing. Every grep listed below is copy-paste-runnable from the repo root.
The five audit axes
The vendored source is a single self-contained C program. It
has no configure-time optional deps other than
libiconv (off by default; the static-musl
builds skip it entirely because musl ships iconv built in).
We audited it across five independent axes:
- Network surface. Does the binary open sockets, resolve DNS, or speak HTTP/SFTP at runtime?
- Subprocess surface. Does it
fork,exec,system, orpopenin order to invoke another binary? - Privilege escalation. Does it ever request
setuid,setgid,chroot, ormount? - Memory-unsafe primitives. Which CWE-flagged
functions are used (
strcpy,sprintf,gets,scanf)? When used, are they bounds-checked? - Temp-file races. Does extraction use the atomic
mkstemp(3), or the raceable legacymktemp(3)?
Each axis maps to one grep -nE invocation. The
results are listed in the next section.
Audit results
- Network calls: none.
Zero matches. lha never opens a socket, never resolves DNS, never speaks HTTP, never calls any URL-handling library. Network libs are absent from the linker symbol table; the runtime is offline-only.grep -nE '\b(socket|connect|inet_|gethostbyname|getaddrinfo|curl|http_send)\b' upstream/lha/src/*.c - Subprocess calls: none.
Zero matches. lha never shells out. It doesn't launch compressors (uses its own), doesn't pipe togrep -nE '\b(fork|vfork|system|exec[pvl][ep]?|popen|daemon)\b' upstream/lha/src/*.cxz, doesn't invoke an external signer or verifier. The only call intostdio.his reading and writing the archive file itself. - Privilege escalation: none.
Zero matches. lha extracts with the calling user'sgrep -nE '\b(setuid|setgid|seteuid|setegid|setreuid|chroot|mount|acl_set)\b' upstream/lha/src/*.cumaskapplied (viaumask(022)inextract.c) and never escalates. Symlink extraction is locked-down:lhext.cblocks symlink traversal past extraction paths and refuses to follow any archive symlink pointing outside the destination. This is the exact class of CVE-2022-1027-family bug that some older archivers carried — lha is not vulnerable. - Memory-unsafe primitives: 8 strcpy, all bounds-checked; 0 sprintf/gets/scanf.
grep -nE '\b(strcpy|sprintf|gets|scanf)\s*\(' upstream/lha/src/*.c8
strcpycall sites; zerosprintf, zerogets, zeroscanf. Of the 8 strcpy sites:- 7 of 8 are immediately guarded by a length
check. Every such call site is annotated
"
/* ok */" in the source — the upstream maintainer's review marker. - The remaining one —
strcpy(p, buf)inutil.c:126— is astrduppattern:pwas justmalloc-ed with sizestrlen(buf)+1, so it has the exact number of bytes available. No buffer overrun.
Compare to the modern lha wrappers (lz4, zstd, 7z) which have similar or larger surface but with current sanitizers-fuzzed primitives. lha is older but its dangerous-call sites are small in number and individually checked.
- 7 of 8 are immediately guarded by a length
check. Every such call site is annotated
"
- >Temp files: mkstemp(3), not mktemp(3).
grep -nE '\b(mkstemp|mktemp)\b' upstream/lha/src/*.cIn
lharc.c:1240:old_umask = umask(077); fd = mkstemp(temporary_name); umask(old_umask);mkstemp(3)is POSIX-atomic-with-0700-default — there's no race window for an attacker to swap the path.mktemp(3)is the deprecated / by-default- insecure variant; lha does not use it.
What runs at install time
The static-binary distribution links against only:
- libc (or libSystem on macOS) — always
- libz — only if you build with system zlib; the vendored sources include their own zlib-compat shim, so the default build doesn't need it
- libiconv — only if you enable
--enable-iconv(off by default; static-musl builds skip it because musl ships iconv built-in)
No libcurl, no libssl, no
libxml2, no libsystemd. On the
musl-static build, all of the above is bundled into the
single binary, which is what x eget use lha
installs.
Attribution chain
The five-axis grep above only tells you what the source doesn't do. To verify what the binary actually is, the source attribution runs backwards through seven maintainers across three decades — every one of them in the public LZH/LHA knowledge graph:
- Y. Tagawa — LHarc/LZH author, 1988-1989 (LHA = T.Matsumoto)
- M. Oki — LHA for UNIX port, 1991
- N. Watazaki — LHa for UNIX rewrite, 1993-1995
- T. Okamoto — LHA 1.14i patch, 2000
- Koji Arai — autoconf-ified the code, 2003-2022
- jca02266 (GitHub) — current maintained autoconf fork, 2022-
- ljh-sh — static-binary wrapper, 2026
Each contributor kept their changes attributed in the
headers (no rewrites), and the wrapped tarball inside
upstream/lha/ is bit-for-bit the upstream
commit 86094cb at jca02266/lha.
We do not maintain lha; we only build static binaries of
it.
Verdict
The lha binary is safe to distribute and run
on any host. Its attack surface reduces to:
"parse a header from a binary file the user pointed it at, write the contained filenames and bytes to disk."
No network, no subprocess, no privilege boundary crossed,
and the memory-unsafe primitives that exist are individually
bounds-checked. The temp-file path is atomic via
mkstemp(3). The upstream attribution chain runs
through seven maintainers across three decades, all in the
public LHA knowledge graph, with no hidden rewriters.