Files
Juliusz Sosinowicz 3a6c31a51e CI: pool the per-config runner matrices into parallel make-check jobs
Replace the one-runner-per-configuration matrices across the
make-check workflow family with a generic pooled runner,
.github/scripts/parallel-make-check.py. Each workflow keeps its
configuration list as JSON next to the invocation; one runner (or a
small fixed set of shards, balanced by measured per-config minutes)
builds every config in its own out-of-tree (VPATH) build directory off
a single checkout/autogen, on a pool of one-per-CPU worker threads,
longest first. Concurrent checks are isolated with bubblewrap network
namespaces, compilations are cached with ccache, the first failure
aborts the rest (fail-fast, with --no-fail-fast to run everything),
and per-config timings plus pool efficiency land in the step summary.
Failure logs upload as artifacts. smoke-test.yml is likewise reworked
into a single pooled job that runs its nine configs on one runner.

Converted workflows (runner jobs per full pass):
  os-check.yml             101 -> 8  (92 Ubuntu configs -> 4 shards;
                           the macOS matrix, the user-settings jobs and
                           the standalone
                           macos-apple-native-cert-validation.yml fold
                           into one macOS runner; Windows unchanged)
  pq-all.yml                21 -> 2 shards
  disable-pk-algs.yml       15 -> 1
  wolfCrypt-Wconversion.yml 11 -> 1
  trackmemory.yml            7 -> 1
  cryptocb-only.yml          8 -> 1  (incl. the two new SHA512 entries)
  multi-compiler.yml         6 -> 1
  smallStackSize.yml         6 -> 1
  multi-arch.yml             6 -> 1
  async.yml                  5 -> 1
  psk.yml                    5 -> 1
  no-malloc.yml              3 -> 1
  wolfsm.yml                 3 -> 1
  opensslcoexist.yml         2 -> 1

Measured against current upstream passing runs (job execution time,
queue excluded): ~200 runner jobs / ~374 runner-minutes per full pass
become 23 jobs / ~168 runner-minutes, with more coverage than before.
multi-arch's old matrix combined an "include" list of four
architectures with an "opts" axis; GitHub's include-merge rules made
each arch entry overwrite the previous one, so only the armel
combinations actually ran. The pooled list restores the intended
aarch64/armhf/riscv64 coverage (23 combinations; riscv64 x sp-math is
omitted as invalid - configure rejects sp-math without SP, and
--enable-riscv-asm, unlike --enable-sp-asm, does not bring SP in).

Out-of-tree build fixes this depends on:
- Makefile.am: symlink the read-only test data (certs/, tests/ config
  files, sniffer captures and helpers, examples/crypto_policies,
  input, quit) into the build tree via a BUILT_SOURCES stamp, removed
  again in distclean-local. ChangeToWolfRoot() and the script tests
  resolve everything relative to the working directory, so out-of-tree
  make check and make distcheck now pass.
- scripts/multi-msg-record.py: locate the client binary from the build
  tree working directory rather than the script's source directory.
- configure.ac + wolfssl/include.am: run
  support/gen-debug-trace-error-codes.sh from $srcdir; it reads the
  error-code headers from the source tree and generates into the build
  tree.
- tests/swdev: a WOLFBUILD variable points the sub-make at the build
  tree for the configure-generated headers (wolfssl/options.h,
  wolfssl/version.h); the in-tree-only guards are dropped.

Portions of PR #10649 are incorporated: the cross-platform
ccache-setup composite action, repository_owner gates on check-headers
and check-source-text, the docs-only paths-ignore on os-check, and the
libspdm timeout bumps.
2026-06-12 09:47:13 +00:00

160 lines
7.4 KiB
Markdown

# wc_swdev: Software CryptoCb Device for Tests
`wc_swdev` is a **test-only** software backend used to exercise builds
that strip a wolfCrypt algorithm in favor of CryptoCb dispatch. It is
compiled separately from the main library, linked into the test
programs only, and exposes exactly two C symbols. **It is not a
production component and must not be linked into shipping binaries.**
The four switches it supports are:
| Macro | Strips | Test target |
|--------------------------------|----------------|--------------------|
| `WOLF_CRYPTO_CB_ONLY_RSA` | software RSA | RSA via CryptoCb |
| `WOLF_CRYPTO_CB_ONLY_ECC` | software ECC | ECC via CryptoCb |
| `WOLF_CRYPTO_CB_ONLY_SHA256` | software SHA-256 | SHA-256 via CryptoCb |
| `WOLF_CRYPTO_CB_ONLY_AES` | software AES | AES via CryptoCb |
When a test program calls e.g. `wc_AesCbcEncrypt()` against a libwolfssl
built with `-DWOLF_CRYPTO_CB_ONLY_AES`, the software AES path is gone;
the call routes through the CryptoCb dispatch layer. swdev registers
itself as that callback, executes the operation against its own
internal copy of the AES code, and returns the result.
## Architecture
```
+-----------------------------------------------------------+
| TEST PROGRAM (testwolfcrypt, unit.test, examples/...) |
| |
| wolfCrypt_Init() |
| wc_SwDev_Init() -- registers swdev device + a |
| WOLF_CRYPTO_CB_FIND hook |
| ... wc_AesCbcEncrypt(), wc_Sha256Update(), etc. ... |
| wc_SwDev_Cleanup() |
| wolfCrypt_Cleanup() |
+-----------------------------------------------------------+
| ^
| call into LIBWOLFSSL | result
v |
+-----------------------------------------------------------+
| LIBWOLFSSL (compiled with -DWOLF_CRYPTO_CB_ONLY_AES, |
| -DWOLF_CRYPTO_CB_ONLY_SHA256, ...) |
| |
| wc_AesCbcEncrypt() |
| - software AES is #ifdef'd out |
| - dispatch via wc_CryptoCb_AesCbc...() |
+-----------------------------------------------------------+
| ^
| CryptoCb dispatch |
v |
+-----------------------------------------------------------+
| tests/swdev/build/swdev.o |
| (single relocatable .o, only 2 visible symbols) |
| |
| wc_SwDev_Callback(devId, info, ctx) |
| - swdev_ensure_init() lazy wolfCrypt_Init |
| - switch (info->algo_type): |
| PK -> RSA / ECC software impl |
| HASH -> SHA-256 software impl |
| CIPHER -> AES (CBC/CTR/ECB/GCM/CCM) software impl |
| |
| swdev was compiled WITHOUT the WOLF_CRYPTO_CB_ONLY_* |
| gates, so its private copy of wolfcrypt still has the |
| full software implementations. |
+-----------------------------------------------------------+
```
## How the Two-Compile Trick Works
The whole mechanism rests on compiling the wolfcrypt sources twice:
1. **libwolfssl** is built normally with the user's `_ONLY_*` flags
set, so its software RSA/ECC/SHA-256/AES paths are gone.
2. **swdev** recompiles the same source set under
`tests/swdev/user_settings.h`, which `#undef`s all four `_ONLY_*`
macros. swdev therefore contains the full software implementations.
To prevent symbol collisions when both are linked into the same test
binary, `tests/swdev/Makefile` does the following:
- Compiles every swdev TU with `-fvisibility=hidden -fno-common`.
- Drops `-DBUILDING_WOLFSSL` so `WOLFSSL_API` does not re-promote
symbols to default visibility.
- Links all swdev objects with `ld -r` into a single relocatable
`swdev.partial.o`.
- Runs `objcopy --keep-global-symbol=wc_SwDev_Callback
--keep-global-symbol=wc_SwDev_InternalCleanup` to localize every
remaining global except the two intended exports.
The Makefile then enforces the invariant directly:
```sh
nm --extern-only --defined-only build/swdev.o
```
must list **only** `wc_SwDev_Callback` and `wc_SwDev_InternalCleanup`.
The build fails loudly otherwise (see `tests/swdev/Makefile:122-129`).
If you add a third `WC_SWDEV_EXPORT` API, update the keep-list in the
Makefile too.
## ABI Constraint
swdev and libwolfssl share C structs across the CryptoCb boundary
(`wc_Sha256`, `Aes`, `RsaKey`, `ecc_key`, ...). One compilation
allocates them, the other operates on them. They must therefore be
ABI-identical. The `_ONLY_*` macros only gate function bodies, not
struct layouts, so flipping them between the two compiles is safe.
**Do not introduce other macros that change struct layout into
`tests/swdev/user_settings.h`.**
## Building
`wc_swdev` is enabled with the `--enable-swdev` configure flag.
```sh
./autogen.sh
./configure --enable-swdev --enable-cryptocb \
<other flags> \
CPPFLAGS="-DWOLF_CRYPTO_CB_ONLY_ECC \
-DWOLF_CRYPTO_CB_ONLY_RSA \
-DWOLF_CRYPTO_CB_ONLY_SHA256 \
-DWOLF_CRYPTO_CB_ONLY_AES"
make
make check
```
Notes:
- `--enable-swdev` requires `--enable-cryptocb`, or `--enable-usersettings`
with `WOLF_CRYPTO_CB` defined in the user's `user_settings.h`.
- `--enable-swdev` defines `WOLFSSL_SWDEV` and `WOLF_CRYPTO_CB_FIND`
automatically; see `configure.ac`.
- swdev is built from `wolfcrypt/test/include.am` and inherits
`PARENT_SRCS`, `PARENT_BUILD_CFLAGS`, etc., from the parent build.
Out-of-tree (VPATH) builds work: `WOLFBUILD` points the sub-make at
the build tree for the configure-generated headers
(`wolfssl/options.h`, `wolfssl/version.h`).
For the full CI matrix that exercises each `_ONLY_*` macro, see
`.github/workflows/cryptocb-only.yml`.
## Files
| File | Role |
|---------------------|---------------------------------------------------------------|
| `swdev.h` | Public swdev interface (the two exported symbols) |
| `swdev.c` | CryptoCb dispatcher: PK / HASH / CIPHER algorithms |
| `swdev_loader.h` | Test-harness API: `wc_SwDev_Init`, `wc_SwDev_Cleanup` |
| `swdev_loader.c` | Refcounted Init/Cleanup; registers the callback + Find hook |
| `user_settings.h` | `#undef`s the `WOLF_CRYPTO_CB_ONLY_*` gates for swdev's TU |
| `Makefile` | Two-compile + objcopy + symbol-invariant check |
## Production Use
**None.** swdev exists only to make the `WOLF_CRYPTO_CB_ONLY_*` builds
testable on a generic Linux runner. Real deployments are expected to
provide their own CryptoCb backed by a hardware engine (TPM, HSM, SoC
crypto block, etc.). swdev is not API-stable, not benchmarked, and not
audited as a production cryptographic provider.