tests/api is one binary compiled in every CI configuration, so a test calling
an API the build did not compile is not a test failure -- it is a link error
that takes the whole binary down. It is also invisible to header inspection,
because wolfSSL declares plenty of API unconditionally and implements it under
a narrower condition. That combination broke CI four separate times on this
branch, each time found by CI rather than locally, and each time the fix was
the same: name what the build provides, not what the test needs.
check-api-guards.py walks the enclosing #if chain of every call site and
requires the macros the IMPLEMENTATION carries. It is a whitelist rather than
a parse of ssl.h on purpose: the mapping from symbol to implementation guard
cannot be derived from the declaration, which is the whole problem.
Two things make it usable rather than noisy:
It only looks at call sites this branch changed. Run over everything it
reports 28 long-standing sites that are fine in practice because the
configurations that would break them are not built; auditing those is a
different job, and --all still does it.
It knows which macros imply TLS. A block under WOLFSSL_TLS13 or HAVE_SNI
cannot also need !defined(NO_TLS) spelled out, and comments and string
literals are blanked before matching, since these files discuss the very
API names being searched for.
It refuses to run against a ref it cannot resolve rather than reporting
success, because a shallow checkout would otherwise make every diff empty and
the check would pass without looking at anything. The workflow checks out with
fetch-depth: 0 for that reason, and runs the check before the smoke build --
it needs no build and costs a second.
Verified both directions: clean on this branch, and it reports the exact site
when !defined(NO_TLS) is removed from a guard that needs it.
Shards run 28-38 min against a 45 min limit (p90 36, max 42.6), so a
slow step ahead of the build times the job out. On 2026-09-04 that is
what killed the weekday seed itself: shard 2 spent 31.9 min in "Install
dependencies" and was cut off at 45, leaving that shard's ccache
unrefreshed.
A shard's wall time is one config's, not its share of the work.
Measured over a run at the usual cache hit rate,
all-pq-small-smallstack-noasm took 35.7 min of a 35.7 min shard and
all-pq-small-noasm 33.8 of 33.8, each pinned to about one of four CPUs
while 17 neighbours held the rest - so the pool cannot shorten either,
and neither can rebalancing. A third shard puts those two and
slhdsa-pqc-only (22.3) on separate runners and cuts the neighbour load
from ~87 to ~46 thread-min, which is the part that decides how much of
the machine the critical-path config gets.
Recalibrate "minutes" off one run that has both shards in that regime
(33841296137). They had drifted several-fold: slhdsa-pqc-only was
declared at 2 min against 22.3 measured, all-mlkem-512-standalone at
1.5 against 5.6, so longest-first was not ordering by length. Totals
come out 82.1/81.7/81.7.
The comment on where to read those figures had it backwards, and is
corrected: it asked for a run whose ccache hit, but a hit is the
exception. The seed is built from master, so a PR touching a widely
included header misses everything downstream - editing wolfssl/ssl.h
alone measures a 31% hit rate - and PR runs sit at 42%, against 99.8%
for the rare PR that touches no compiled file. The two regimes differ
2x per config (all-pq-small-noasm: 33.8 min against 16.0), so figures
read off a warm run make every normal run warn.
Shard 3 has no seeded cache and the other two now carry different
configs, so runs before the next weekday seed are colder than usual.
The workflow comments described only the GCM KAT, but the step builds
and runs both the GCM and CBC callback KATs, so say so in both comment
blocks. Drop the unnecessary const cast on the GCM KAT auth tag:
wc_AesGcmDecrypt already takes a const authTag.
The CBC branch re-ran wc_AesSetKey() with a fixed IV from R-memory
slot TROPIC01_AES_IV_RMEM_SLOT on every operation, silently
discarding the per-message IV the caller installed: CBC under the
device key became deterministic. Save aes->reg before the key swap
and pass it back, so the operation chains from the caller's IV (and
multi-block streaming keeps working). The IV slot fetch and the
lt_iv buffer are now dead and go away.
New tests/tropic01_cbc_kat.c known-answer test (device key from
TROPIC01Sim slot 0, caller IV distinct from the device IV fixture)
fails on the old code and passes on this one; the CI step now runs
both the GCM and the CBC KAT.
Tropic01_CryptoCb set the device key with wc_AesSetKey(), which does
not derive aes->gcm.H, so GCM auth used a stale or zero GHASH subkey.
Use wc_AesGcmSetKey() and drop the unused device IV fetch from the
GCM branch. Add a GCM callback KAT (tests/tropic01_gcm_kat.c) run
against TROPIC01Sim in CI.
Fil-C is a memory safe implementation of C that gives every pointer a
capability and checks it on each access. The job pinned v0.674 and ran
two configurations, and the pattern it used to fetch the release now
matches both the x86_64 and the aarch64 tarball, so it would break on
any release from v0.682 on.
Move to v0.684, pick the tarball by uname -m, and grow the matrix to
seventeen legs: post-quantum, crypto only, heap math, fast math, fast
math with WC_PROTECT_ENCRYPTED_MEM, small stack, static memory, sniffer,
low resource, old TLS, fixed size SP, assembly left on, and the default
and full builds on an arm64 runner. One leg reruns the full
build against the assertion enabled Fil-C runtime with the collector in
stress mode. EXTRA_CFLAGS=-g makes a panic report file and line rather
than bare symbol names. The Fil-C runtime environment is set on the
build step rather than on the job, so the download and extract steps do
not run ordinary runner tooling with Fil-C's libc on the search path.
The job also passed -DWC_NO_CACHE_RESISTANT, which compiled out the
cache resistant paths, so CI was not testing the code that ships.
Those paths build a pointer by masking and adding two addresses, which
a capability target cannot do. wolfSSL already has WC_NO_PTR_INT_CAST
for exactly this and defines it for CHERI purecap, so define it for
Fil-C too and drop the flag.
That exposed three places the macro did not cover:
tfm.c never honoured it. Add fp_cond_copy and use it for the six masked
address selections in the timing resistant exptmod ladders.
RsaDec and DoClientKeyExchange selected a pointer with ctMaskCopy, which
copies it a byte at a time and so drops its capability. This fires on
the success path, not just on error. Add ctMaskSelPtr next to ctMaskCopy
in misc.c and use it at both sites: it indexes a two entry table aligned
so that both candidates share a cache line, and still does not branch on
the decryption result.
sp_int.c's x86_64 assembly uses "rm" and "m" operands, which Fil-C's
safe inline assembly does not allow. Route them through SP_ASM_RM and
SP_ASM_M, which only differ under __FILC__, so every other target
compiles unchanged. __FILC__ is compiler defined and never appears in a
#define, so it also goes in .wolfssl_known_macro_extras.
--disable-asm stays for most legs because Fil-C cannot link a .S file
at all. It has no pizlonated entry point, so the link fails.
The retry added for the flaky Docker OpenSSH-server tests was a no-op.
automake generates the recheck target only in the directory that defines
TESTS, so libssh2 has it in tests/Makefile but not at the top level. Every
flaky run ended with "No rule to make target 'recheck'" twice and failed
the job on the first transient error. Run recheck in tests/ instead.
Also correct the description of the race. The handshake fails with -43,
which is LIBSSH2_ERROR_SOCKET_RECV rather than a banner timeout:
docker-proxy binds the published port as soon as the container starts, so
connect() succeeds and sshd resets the connection because it is not
listening inside the container yet. That is why the fixture cannot recover
on its own, as its retry loop only covers connect(), which never fails
here.
Cap the test parallelism at the runner's CPU count. "make -j" is
unbounded, and the failing runs started around 20 containers at once on a
four-CPU runner, which is what starves sshd of the time it needs to come
up. Every observed failure was a single random test out of 46 inside that
burst.
A genuine failure is still retried twice and still fails the job.
The tests/unit-mcdc white-box translation units are not part of unit.test, so
nothing in CI builds or runs them. This builds --enable-all once and runs the
subset that works against it, failing only when one that passed before stops.
wolfSSL/simulators#17 (P-256 + 28-byte-digest verify case in
wolfcrypt-test) merged to main as c827ac7. Pin the workflow to that
merge commit so the STSAFE job exercises the regression test against
the digest normalization fixed on this branch.
The pinned wolfSSL/simulators ref gains the P-256 + 28-byte-digest
verify case in wolfcrypt-test, which exercises the digest
normalization fixed in the previous commit. The simulators branch
carrying 9f9900295 (PR wolfSSL/simulators#17) must be pushed/merged before this ref is reachable.
The gate compared only the published image's kernel label with uname -r,
so a package added to the bundle never shipped until the kernel next
rolled (~monthly) - and workflow_dispatch could not force it either.
The bundle published on 2026-08-14 still carries neither the toolchain
nor ccache for that reason, so fips-dev-no-post's offline ccache probe
fails on every run and reaches the mirror.
Label the image with a hash of the package set and compare that too.
The list now lives in the check step and is passed to the download, so
the gate and the closure cannot drift apart.
The trailing "$@" after the loop made it three, so a wedged mirror
could spend 19m20s of the 20-minute job and be cancelled instead of
reporting - the thing the 5-to-2 cut was for.
pq-all is the only caller whose packages are not a subset of its
bundle: crossbuild-essential-* appear only in the 22.04 lists, and the
offline install is all-or-nothing, so it always takes the apt path -
the one the 45-minute timeout was widened for. The default budget caps
each install attempt at 250s and hard-fails a merely slow download.
Also state the real budget-seconds arithmetic: the loop overshoots by
retry-delay plus the SIGKILL grace, the floors make small values inert,
and the defaults need ~16 minutes rather than the 15 claimed.
build_ipmitool, jwt-cpp's and pam-ipmi's build_pam-ipmi declare no
timeout-minutes - the 4 in each file belongs to the sibling
build_wolfssl, which does not call this action. The comment justifying
the tighter budget was false and the override only cost resilience.
install-apt-deps and ccache-setup run dpkg --configure -a before each
attempt; the five loops open-coded in workflow files did not. timeout
signals the whole process group, so a kill during unpack leaves a dpkg
journal and the next apt-get aborts in debSystem::Lock(), making the
retry useless.
Mirror curve25519: keygen, shared secret, make pub and generic
callbacks, wc_curve448_init_ex/new/delete, new wc_curve448_generic
API with scalar clamp checks, WOLF_CRYPTO_CB_ONLY_CURVE448 mode,
TLS devId plumbing, tests (test.c, api unit tests, swdev),
benchmark devId, CI entries.
The full unit suite has an unrelated SNI runtime failure with the original ASN implementation. Keep this regression focused on configure, compile, and link, which catches the missing SetASNIntMP guard on the parent revision.
Exercise original ASN with RSA key export while ECC, DSA, and DH are disabled. This configuration reproduces the missing ASN helper guards fixed by the preceding commit and protects the minimal SE05x build from regression.
The per-attempt deadlines did not fit callers with a four minute
timeout-minutes, so a wedged mirror cancelled the job before the loop
could report it. Spend a configurable budget across the attempts instead
and set one that fits on the short jobs.
sudo resets the environment, so an exported DEBIAN_FRONTEND never
reached apt-get and a debconf prompt could block until the timeout.
cross-library's containers are bare images with no bash, so Actions runs the
step under sh; the bash array APT_OPTS=(...) was a syntax error and took out
all six cross-library builds. Use a plain word-splitting variable.
The retry budget also has to fit the caller's timeout-minutes - install-apt-deps
is used by jobs with 4 minutes - or the last attempt is cut off before it can
report. Two attempts at 60s/300s instead of three at 120s/900s. The Acquire
timeouts are what actually detect a wedge, so the outer bound only backstops
apt wedging outside its own I/O loop, and apt resumes from archives/partial/.
Also run dpkg --configure -a before the offline ccache probe, not only in the
fallback loop: an interrupted dpkg would otherwise push it to the mirror.
ccache-setup ran a raw apt-get update, so jobs whose ghcr .deb bundle had
already installed cleanly still reached the mirror. On 2026-08-19 that step
stalled on archive.ubuntu.com for 9-43 min and took out 11 jobs. The .deb is
already in /var/cache/apt/archives - install-apt-deps stages the whole bundle
- so install it with --no-download first and only fall back to the mirror.
ccache was missing from the 22.04-minimal and linuxkm bundles; add it.
Neither the fallback in install-apt-deps nor any of the raw apt sites had a
timeout, so a wedged mirror hung instead of failing and the retry loops never
fired. ci-deps-image already solved this for itself (Acquire timeouts plus
`timeout`); apply the same to the consumers, and to the linuxkm producer job
that was still missing it. Also recover from a dpkg interrupted by a kill,
and bound the bundle pull.
The ntp 4.2.8p18 patch (wolfSSL/osp) requires wolfSSL to be built
with OPENSSL_EXTRA_BSD, which provides MD5Init/MD5Update/MD5Final,
and with MD4 for the new test suite. Neither is provided by
--enable-all. Without these flags the 4.2.8p18 job fails at
configure with a clear error.
Pin bump to the simulators commit that adds --enable-pkcs7 to the
wolfcrypt CI build, so the suite's pkcs7signed_test and
pkcs7enveloped_test regression-guard the SE050 RSA verify buffer fix
and the ECDH software-key fallback. The pinned commit is fetchable by
CI once the corresponding simulators change merges.