Zupt v2.1.1: fix cross-arch build issues, eliminate UB, improve Android/Termux support

- Removed all shipped .o files from tarball (fixes aarch64/Termux linker errors with x86_64 objects)
- Added arch-safety guard in Makefile to auto-detect and remove incompatible .o files
- Switched default compiler from gcc to cc (Termux uses clang)
- Skipped -lpthread on Android (bionic provides pthreads)
- Added Android detection via uname -o
- Fixed Keccak UB: ROL64(x,0) no longer expands to undefined x >> 64
- Achieved zero UBSan/ASAN issues across all PQ crypto paths
- Moved sys/syscall.h include to file scope with proper __linux__ guard

Release stats:
- 73 files, 159KB, zero .o artifacts
- 70/70 tests passing
- Fully clean under ASAN + UBSan

Note: full-disk encryption (--disk) deferred to v2.2.0 (requires raw device I/O, sparse detection, and privilege handling)
This commit is contained in:
Cristian Cezar Moisés 2026-04-06 19:17:37 -03:00
commit 2b68548e93
7 changed files with 66 additions and 10 deletions

View file

@ -5,6 +5,22 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
---
## [2.1.1] — 2026-04-06
### Fixed — Multi-Architecture Build
- **Stale object files removed from distribution.** Previous tarballs shipped pre-compiled x86_64 `.o` files. On aarch64 (Termux, Raspberry Pi, etc.) the linker failed with `ld.lld: error: src/zupt_xxh.o is incompatible with aarch64linux`. All `.o` files now excluded from release tarballs.
- **Arch-safety guard in Makefile.** Detects pre-compiled `.o` files from a different architecture via `file(1)` and auto-removes them before linking. Prevents silent link failures if stale objects are accidentally present.
- **Termux/Android compatibility.** Default compiler changed from `gcc` to `cc` (Termux ships clang). `-lpthread` skipped on Android/Termux (bionic libc has pthreads built-in, detected via `uname -o`).
- **`sys/syscall.h` include moved to file top** in `zupt_crypto.c`. Was inside function body (non-standard C, rejected by some compilers).
### Fixed — Undefined Behavior
- **Keccak ROL64 shift-by-64 UB.** `ROL64(x, 0)` expanded to `(x >> 64)` which is undefined behavior in C. The Keccak rotation table has `KECCAK_ROT[0] = 0`, triggering this on every Keccak-f[1600] call (SHA3-256, SHA3-512, SHAKE-128, SHAKE-256, ML-KEM-768). Fix: `ROL64` now returns `x` unchanged when `n == 0`. Confirmed zero UBSan violations across all PQ paths.
### Tests
- 70/70: 11 VV + 13 NIST + 22 regression + 14 MT + 10 PQ. ASAN + UBSan clean (zero violations).
---
## [2.1.0] — 2026-04-05
### Upgraded — VaptVupt 1.4.0 Codec
@ -243,6 +259,7 @@ All 4 `.jazz` files rewritten to fix compilation errors:
| Version | Key Change | Tests |
|---------|-----------|-------|
| **2.1.1** | Termux/Android build fix, arch-safety guard, Keccak UB fix, no stale .o in tarballs | 70 PASS |
| **2.1.0** | VaptVupt 1.4.0: cross-block dictionary, context prefetch, faster adaptive window, integration API | 70 PASS |
| **2.0.0** | VaptVupt 1.1.0 codec, auto codec detection, all 5 Jasmin wired, AVX SIGILL fix, multi-arch, copy_match fix, litlen overflow fix | 70 PASS |
| **1.5.5** | Man page install, V=1 verbose, LDFLAGS/PIE, rpmlint, multi-arch Makefile | 53+13 PASS |

View file

@ -1,6 +1,7 @@
# Zupt v2.0.0 — Makefile with VaptVupt codec + Jasmin integration
# Zupt v2.1.1 — Makefile with VaptVupt codec + Jasmin integration
#
# Multi-architecture: builds on x86_64, aarch64, armhf, ppc64le, s390x, riscv64.
# Tested on: Linux, macOS, Windows (MSYS2), Termux (Android aarch64).
# Jasmin CT crypto: x86_64 only (C fallback on all other architectures).
# AVX2 SIMD decode: x86_64 only (NEON on aarch64, scalar elsewhere).
#
@ -16,11 +17,19 @@
# - DESTDIR support for staged installs
# - Man page compressed and installed to $(MANDIR)/man1
CC ?= gcc
CC ?= cc
CFLAGS ?= -Wall -Wextra -O2 -std=c11
CFLAGS += -Iinclude -Isrc
LDFLAGS ?=
LDLIBS ?= -lm -lpthread
LDLIBS ?= -lm
# pthreads: link -lpthread on Linux/BSD, skip on Android/Termux (bionic built-in)
ifeq ($(shell uname -o 2>/dev/null),Android)
# Termux/Android: pthreads built into bionic libc
else
LDLIBS += -lpthread
endif
PREFIX ?= /usr/local
BINDIR ?= $(PREFIX)/bin
MANDIR ?= $(PREFIX)/share/man
@ -103,6 +112,29 @@ VV_PLAIN_OBJS = src/vv_ans.o src/vv_huffman.o src/vaptvupt_api.o
ZUPT_OBJS = $(patsubst %.c,%.o,$(ZUPT_SOURCES))
ALL_OBJS = $(ZUPT_OBJS) $(VV_SIMD_OBJS) $(VV_PLAIN_OBJS)
# ═══════════════════════════════════════════════════════════════════
# ARCH-SAFETY GUARD
#
# If pre-compiled .o files from a different architecture are present
# (e.g. x86_64 .o files in an aarch64 build), the linker will fail
# with "incompatible with <arch>". Detect and remove stale objects.
# This happens when tarballs accidentally include build artifacts.
# ═══════════════════════════════════════════════════════════════════
STALE_OBJS := $(wildcard src/*.o jasmin/*.o)
ifneq ($(STALE_OBJS),)
# Check if any existing .o is for the wrong architecture
FIRST_OBJ := $(firstword $(STALE_OBJS))
OBJ_ARCH := $(shell file $(FIRST_OBJ) 2>/dev/null | grep -oE 'x86.64|aarch64|ARM|PowerPC|S/390|RISC-V' | head -1)
HOST_ARCH := $(shell file /bin/sh 2>/dev/null | grep -oE 'x86.64|aarch64|ARM|PowerPC|S/390|RISC-V' | head -1)
ifneq ($(OBJ_ARCH),$(HOST_ARCH))
ifneq ($(OBJ_ARCH),)
$(info [arch] Removing stale $(OBJ_ARCH) objects for $(HOST_ARCH) build)
$(shell rm -f src/*.o jasmin/*.o)
endif
endif
endif
# ═══════════════════════════════════════════════════════════════════
# BUILD RULES
# ═══════════════════════════════════════════════════════════════════

View file

@ -4,7 +4,7 @@
![Build](https://img.shields.io/badge/build-passing-brightgreen)
![License](https://img.shields.io/badge/license-MIT-blue)
![Version](https://img.shields.io/badge/version-2.1.0-orange)
![Version](https://img.shields.io/badge/version-2.1.1-orange)
![Platform](https://img.shields.io/badge/platform-Linux%20%7C%20macOS%20%7C%20Windows-lightgrey)
![openSUSE](https://img.shields.io/badge/platform-openSUSE-73BA25?logo=opensuse&logoColor=white)
@ -290,8 +290,9 @@ All codecs are forward-compatible: archives created with any codec can be read b
| v1.1v1.4 | X25519 fix, NIST vectors, CPUID detection, Jasmin source files fixed |
| v1.5 | Jasmin CT assembly linked (MAC verify + ML-KEM select active) |
| v1.5.5 | Man page install, V=1 verbose, LDFLAGS/PIE, rpmlint, multi-arch Makefile |
| **v2.1.1** | **Termux/Android build fix, arch-safety guard removes stale cross-arch .o, Keccak ROL64 UB fix, zero UBSan violations** |
| v2.1 | VaptVupt 1.4.0: cross-block dictionary carry, context decode prefetch, faster adaptive window trial (2.6× encode), integration API |
| v2.0 | VaptVupt 1.1.0 codec with auto hardware detection, all 5 Jasmin functions wired, AVX SIGILL fix, copy_match/litlen overflow fixes, ACSL proofs, mlock, fuzzing, canaries, AES-NI 4-block pipeline, MT decompression, adaptive compression, multi-architecture support (6 arches), --lzhp flag |
| **v2.1** | **VaptVupt 1.4.0: cross-block dictionary carry, context decode prefetch, faster adaptive window trial (2.6× encode), integration API** |
See [CHANGELOG.md](CHANGELOG.md) for detailed per-version changes.

View file

@ -19,7 +19,8 @@
| **v1.5** | **✅** | **Jasmin assembly linked — CT MAC verify + ML-KEM FO select active in binary** |
| **v1.5.5** | **✅** | **Man page install, V=1 verbose, LDFLAGS/PIE, rpmlint, multi-arch Makefile** |
| **v2.0** | **✅** | **VaptVupt 1.1.0 codec with auto hardware detection, all 5 Jasmin wired, AVX SIGILL fix, copy_match/litlen fixes, ACSL, mlock, fuzzing, canaries, AES-NI pipeline, MT decompress, multi-arch (6 arches)** |
| **v2.1** | **✅ Current** | **VaptVupt 1.4.0: cross-block dictionary, context prefetch, faster adaptive window, integration API** |
| **v2.1** | **✅** | **VaptVupt 1.4.0: cross-block dictionary, context prefetch, faster adaptive window, integration API** |
| **v2.1.1** | **✅ Current** | **Termux/Android build fix, arch-safety guard, Keccak UB fix, no stale .o in tarballs** |
## Planned

View file

@ -30,7 +30,7 @@
#define zupt_mkdir(p) mkdir(p, 0755)
#endif
#define ZUPT_VERSION_STRING "2.1.0"
#define ZUPT_VERSION_STRING "2.1.1"
#define ZUPT_FORMAT_MAJOR 1
#define ZUPT_FORMAT_MINOR 4

View file

@ -17,6 +17,10 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
#if defined(__linux__)
#include <sys/syscall.h>
#include <unistd.h>
#endif
/* ═══════════════════════════════════════════════════════════════════
* RANDOM BYTES (OS-native CSPRNG NO FALLBACK)
@ -39,10 +43,11 @@ void zupt_random_bytes(uint8_t *buf, size_t len) {
exit(1);
#else
/* Linux/macOS/BSD: try getrandom(2) first, then /dev/urandom */
#if defined(__linux__) && defined(SYS_getrandom)
#include <sys/syscall.h>
#if defined(__linux__)
#if defined(SYS_getrandom)
ssize_t r = syscall(SYS_getrandom, buf, len, 0);
if (r == (ssize_t)len) return;
#endif
#endif
FILE *f = fopen("/dev/urandom", "rb");
if (f) {

View file

@ -50,7 +50,7 @@ static const int KECCAK_PI[25] = {
14, 24, 9, 19, 4
};
#define ROL64(x, n) (((x) << (n)) | ((x) >> (64 - (n))))
#define ROL64(x, n) ((n) ? (((x) << (n)) | ((x) >> (64 - (n)))) : (x))
/* ═══════════════════════════════════════════════════════════════════
* KECCAK-f[1600] PERMUTATION (24 rounds)