Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions .github/scripts/big-endian-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#!/bin/bash
# big-endian-test.sh
#
# Copyright (C) 2006-2025 wolfSSL Inc.
#
# This file is part of wolfProvider.
#
# wolfProvider is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# wolfProvider is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with wolfProvider. If not, see <http://www.gnu.org/licenses/>.
#
# Runs the standard build and unit tests on a big-endian target. Meant to
# run inside an s390x container; the test-deps image is amd64-only, so the
# toolchain is installed here instead.
#
# The unit runner already continues past a failing test, so only a signal
# stops the suite. This restarts at the next case after a crash and takes a
# backtrace, so one crash does not hide the rest of the results.

set -euo pipefail

LOG_DIR=${LOG_DIR:-/wolfprov/be-logs}
RUN_LOG="${LOG_DIR}/unit-run.log"
SUMMARY="${LOG_DIR}/summary.txt"

export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y --no-install-recommends \
autoconf automake build-essential ca-certificates gdb git libtool make \
perl pkg-config

# The workspace is bind-mounted from the runner, so its owner is not root.
git config --global --add safe.directory /wolfprov

# Fail loudly rather than silently retesting x86-64 if the qemu platform
# selection ever regresses.
cat > /tmp/endian-check.c <<'EOF'
int main(void)
{
#if !defined(__BYTE_ORDER__) || (__BYTE_ORDER__ != __ORDER_BIG_ENDIAN__)
#error "not a big-endian target"
#endif
return 0;
}
EOF
gcc -o /tmp/endian-check /tmp/endian-check.c
/tmp/endian-check
echo "confirmed big-endian target: $(uname -m)"

mkdir -p "${LOG_DIR}"

# Build only. The suite is driven below so a crash does not end the run.
WOLFPROV_SKIP_TEST=1 ./scripts/build-wolfprovider.sh

source ./scripts/env-setup

total=$(./test/unit.test --list | grep -cE '^ *[0-9]+: ')
echo "unit test cases: ${total}"

crashed=()
failed=()
next=1

while [ "${next}" -le "${total}" ]; do
echo "=== running cases ${next}..${total} ==="
rc=0
# shellcheck disable=SC2046
./test/unit.test $(seq "${next}" "${total}") 2>&1 | tee -a "${RUN_LOG}" \
|| rc=${PIPESTATUS[0]}

# Under 128 means the runner reached the end on its own, reporting any
# failures as it went. Only a signal leaves cases unrun.
if [ "${rc}" -lt 128 ]; then
break
fi

last=$(grep -oE '^#### Start: [0-9]+' "${RUN_LOG}" | tail -1 \
| grep -oE '[0-9]+$')
name=$(./test/unit.test --list | grep -E "^ *${last}: " \
| sed -E 's/^ *[0-9]+: //')
echo "=== case ${last} (${name}) died with exit ${rc}; getting backtrace ==="
crashed+=("${last} ${name}")

./libtool --mode=execute gdb -batch \
-ex run -ex 'bt full' -ex 'info registers' \
-ex 'thread apply all bt' \
--args ./test/unit.test "${last}" \
> "${LOG_DIR}/backtrace-${last}.log" 2>&1 || true
echo "--- backtrace for case ${last} (${name}) ---"
cat "${LOG_DIR}/backtrace-${last}.log"

next=$((last + 1))
done

mapfile -t failed < <(grep -oE '^#### FAILED: [0-9]+ - [^ ]+' "${RUN_LOG}" \
| sed -E 's/^#### FAILED: //' | sort -u || true)

{
echo "big-endian unit results on $(uname -m)"
echo "cases: ${total}"
echo
echo "crashed (${#crashed[@]}):"
printf ' %s\n' "${crashed[@]:-none}"
echo
echo "failed (${#failed[@]}):"
printf ' %s\n' "${failed[@]:-none}"
echo
echo "byte-order import path (the reason this job exists):"
grep -E '^#### (SUCCESS|FAILED): [0-9]+ - test_(rsa|dh|ec)_fromdata' \
"${RUN_LOG}" || echo " never reached"
} | tee "${SUMMARY}"

if [ "${#crashed[@]}" -gt 0 ] || [ "${#failed[@]}" -gt 0 ]; then
exit 1
fi
32 changes: 31 additions & 1 deletion .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ gh workflow run pr-osp-select.yml --ref <branch> -f jobs="all"

### What runs in the nightly fan-out

43 workflows total: 40 third-party OSS integrations, 2 internal
44 workflows total: 40 third-party OSS integrations, 3 internal
validations, and the static-analysis suite. Every one of these patches
the upstream project (where needed) via `osp/wolfProvider/<app>/*.patch`
from [wolfssl/osp](https://github.com/wolfssl/osp), builds it against
Expand Down Expand Up @@ -176,6 +176,7 @@ exercised, with and without `WOLFPROV_FORCE_FAIL=1`.
| `debian-package.yml` | End-to-end check: builds the wolfprov `.deb`s and confirms they install cleanly on a fresh container and the provider loads. |
| `openssl-version.yml` | Sweeps every upstream `openssl-3.X.Y` release tag — catches breakage from OpenSSL point releases before they hit our matrix defaults. |
| `static-analysis.yml` | cppcheck, clang scan-build, Facebook Infer. Heavy enough that it lives in the nightly fan-out rather than per-PR. |
| `big-endian.yml` | Builds and runs the unit tests on s390x (big-endian) under qemu emulation. Guards the `#ifdef LITTLE_ENDIAN_ORDER` byte-order paths, which no other job exercises. |

Sanitizers (ASan+UBSan, TSan) run on every PR/push — see the PR table
above. They're fast enough with caching to gate merges, so they don't
Expand Down Expand Up @@ -264,6 +265,35 @@ The scan-build and infer thresholds are baseline-based, not strict —
they let pre-existing issues slide but flag obvious regressions.
Bringing them to 0 is a future cleanup.

## Big-endian coverage

`big-endian.yml` runs nightly (and via `ci:big-endian` / `ci:all`). Every
other job in this repo runs on x86-64, so the `#else` half of each
`#ifdef LITTLE_ENDIAN_ORDER` block in `src/wp_params.c` is otherwise never
compiled or executed. This job closes that gap.

GitHub offers no big-endian runners, so it emulates s390x with qemu
binfmt (`docker/setup-qemu-action`) and builds inside a `debian:bookworm`
s390x container. Emulation is roughly an order of magnitude slower than
native, which is why it is nightly-only rather than a PR gate.

`.github/scripts/big-endian-test.sh` installs a toolchain — the test-deps
image is amd64-only, so it cannot be reused here — asserts the target
really is big-endian, then hands off to `scripts/build-wolfprovider.sh`
with no arguments. That is the same entry point `simple.yml` uses, at its
default OpenSSL + wolfSSL refs, and it runs `make test` itself. The
endian assert means a qemu or platform regression fails the job instead
of silently retesting x86-64.

Everything after the emulation setup is therefore the stock build. The
job is slow because every instruction is emulated, including the full
OpenSSL and wolfSSL source builds, not because it does anything special.

No big-endian-specific test case exists or is needed: `test_rsa_fromdata`
already compares a wolfProvider `EVP_PKEY_fromdata` import against
OpenSSL's via `EVP_PKEY_cmp`, which is exactly the comparison a byte-order
mistake in the `OSSL_PARAM` import path breaks.

## Overhead regression testing

`perf-regression.yml` (workflow display name **Overhead Regression**) runs nightly at 07:00 UTC (and on
Expand Down
64 changes: 64 additions & 0 deletions .github/workflows/big-endian.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Big-Endian

# GitHub has no big-endian runners, so s390x runs under qemu binfmt
# emulation. Emulation is slow, which is why this is nightly / ci:all
# only and not a per-PR check.

on:
workflow_call: {}
workflow_dispatch: {}

# run-scoped: a reusable workflow's group keys off the caller, so a shared
# group would let one nightly run cancel another's big-endian job.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.run_id }}
cancel-in-progress: false

jobs:
big-endian:
name: Big-Endian s390x
runs-on: ubuntu-22.04
permissions:
contents: read
timeout-minutes: 240
steps:
- name: Checkout wolfProvider
uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Enable s390x emulation
uses: docker/setup-qemu-action@v3
with:
platforms: s390x

- name: Build and test on s390x
run: |
docker run --rm --platform linux/s390x \
-v "$PWD:/wolfprov" -w /wolfprov \
debian:bookworm \
/bin/bash /wolfprov/.github/scripts/big-endian-test.sh

- name: Summary
if: ${{ always() }}
run: |
if [ -f be-logs/summary.txt ] ; then
cat be-logs/summary.txt >> "$GITHUB_STEP_SUMMARY"
cat be-logs/summary.txt
fi

- name: Print build log on failure
if: ${{ failure() }}
run: |
if [ -f scripts/build-release.log ] ; then
tail -200 scripts/build-release.log
fi

# The full run is far too long for the console; backtraces live here.
- name: Upload logs
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: big-endian-logs
path: be-logs/
if-no-files-found: warn
1 change: 1 addition & 0 deletions .github/workflows/nightly-osp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -167,5 +167,6 @@ jobs:
xmlsec-584: { needs: wave1-done, if: always(), uses: ./.github/workflows/xmlsec.yml, with: { wolfssl_refs_json: '["v5.8.4-stable"]' } }

# === Out-of-wave: not wolfssl-version-split ===
big-endian: { uses: ./.github/workflows/big-endian.yml }
static-analysis: { uses: ./.github/workflows/static-analysis.yml }
multi-compiler: { uses: ./.github/workflows/nightly-multi-compiler.yml }
4 changes: 4 additions & 0 deletions .github/workflows/pr-osp-select.yml
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,10 @@ jobs:
needs: select
if: contains(needs.select.outputs.run, ' all ') || contains(needs.select.outputs.run, ' xmlsec ')
uses: ./.github/workflows/xmlsec.yml
big-endian:
needs: select
if: contains(needs.select.outputs.run, ' all ') || contains(needs.select.outputs.run, ' big-endian ')
uses: ./.github/workflows/big-endian.yml
multi-compiler:
needs: select
if: contains(needs.select.outputs.run, ' all ') || contains(needs.select.outputs.run, ' multi-compiler ')
Expand Down
19 changes: 16 additions & 3 deletions src/wp_params.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@


/**
* Read little-endian array of bytes representing a large number.
* Read local endian array of bytes representing a large number.
*
* @param [in, out] mp Multi-precision number.
* @param [in] data Little-endian array of bytes.
* @param [in] data Local endian array of bytes.
* @param [in] len Length of array in bytes.
* @return 1 on success.
* @return 0 on failure.
Expand All @@ -39,12 +39,15 @@ int wp_mp_read_unsigned_bin_le(mp_int* mp, const unsigned char* data,
size_t len)
{
int ok = 1;
int rc;
#ifdef LITTLE_ENDIAN_ORDER
unsigned char rdata[1024];
size_t i;
int rc;
#endif

WOLFPROV_ENTER(WP_LOG_COMP_PROVIDER, "wp_mp_read_unsigned_bin_le");

#ifdef LITTLE_ENDIAN_ORDER
if (len > sizeof(rdata)) {
ok = 0;
}
Expand All @@ -65,6 +68,13 @@ int wp_mp_read_unsigned_bin_le(mp_int* mp, const unsigned char* data,

/* rdata may hold private key material (RSA d/p/q, EC/DH private). */
OPENSSL_cleanse(rdata, sizeof(rdata));
#else
rc = mp_read_unsigned_bin(mp, data, (word32)len);
if (rc != 0) {
WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "mp_read_unsigned_bin", rc);
ok = 0;
}
#endif

WOLFPROV_LEAVE(WP_LOG_COMP_PROVIDER, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok);
return ok;
Expand Down Expand Up @@ -100,6 +110,9 @@ int wp_mp_to_unsigned_bin_le(mp_int* mp, unsigned char* data, size_t len)
data[len - 1 - i] = t;
}
}
#else
(void)i;
(void)len;
#endif

WOLFPROV_LEAVE(WP_LOG_COMP_PROVIDER, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok);
Expand Down
Loading