diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml
new file mode 100644
index 00000000..96344933
--- /dev/null
+++ b/.github/workflows/static-analysis.yml
@@ -0,0 +1,281 @@
+name: Static Analysis
+
+# START OF COMMON SECTION
+on:
+ schedule:
+ # Run nightly at 2 AM UTC
+ - cron: '0 2 * * *'
+ workflow_dispatch:
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+# END OF COMMON SECTION
+
+jobs:
+ cppcheck:
+ name: cppcheck Static Analysis
+ runs-on: ubuntu-22.04
+ timeout-minutes: 30
+ steps:
+ - name: Checkout wolfProvider
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 1
+
+ - name: Install dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y cppcheck
+
+ - name: Build dependencies (OpenSSL and wolfSSL)
+ run: |
+ OPENSSL_TAG=openssl-3.5.4 WOLFSSL_TAG=master ./scripts/build-wolfprovider.sh 2>&1 | tail -100 || true
+ # We only need the build to succeed enough to have headers available
+
+ - name: Generate configure script
+ run: |
+ ./autogen.sh || true
+
+ - name: Configure to generate config.h
+ run: |
+ OPENSSL_INSTALL_DIR="$PWD/openssl-install"
+ WOLFSSL_INSTALL_DIR="$PWD/wolfssl-install"
+
+ # Configure to generate config.h (needed for cppcheck)
+ ./configure \
+ --with-openssl="$OPENSSL_INSTALL_DIR" \
+ --with-wolfssl="$WOLFSSL_INSTALL_DIR" \
+ --prefix="$PWD/wolfprov-install" \
+ >/dev/null 2>&1 || true
+
+ - name: Run cppcheck
+ run: |
+ # Configure include paths for cppcheck
+ OPENSSL_INC="$PWD/openssl-install/include"
+ WOLFSSL_INC="$PWD/wolfssl-install/include"
+ WOLFPROV_INC="$PWD/include"
+
+ # Run cppcheck on source files only (tests can be analyzed separately if needed)
+ # Use --force and suppress noValidConfiguration to proceed even with incomplete config
+ cppcheck \
+ --enable=all \
+ --suppress=missingIncludeSystem \
+ --suppress=unusedFunction \
+ --suppress=unmatchedSuppression \
+ --suppress=noValidConfiguration \
+ --inline-suppr \
+ --force \
+ --error-exitcode=0 \
+ -I "$OPENSSL_INC" \
+ -I "$WOLFSSL_INC" \
+ -I "$WOLFPROV_INC" \
+ --platform=unix64 \
+ src/ 2>&1 | tee cppcheck-output.txt || true
+
+ # Display output (filter out noValidConfiguration messages and progress)
+ grep -v "noValidConfiguration" cppcheck-output.txt | \
+ grep -v "^Checking " | \
+ grep -v "^[0-9]*/[0-9]* files checked" | \
+ grep -v "^nofile:0:0: information:" || true
+
+ # Count errors and warnings (count lines with error:/warning: that are actual issues)
+ # Use wc -l for more reliable counting, and strip whitespace
+ ERROR_COUNT=$(grep -E "^src/.*:[0-9]+:[0-9]+:.*error:" cppcheck-output.txt 2>/dev/null | wc -l | tr -d '[:space:]' || echo "0")
+ WARNING_COUNT=$(grep -E "^src/.*:[0-9]+:[0-9]+:.*warning:" cppcheck-output.txt 2>/dev/null | wc -l | tr -d '[:space:]' || echo "0")
+
+ # Ensure we have valid integers (default to 0 if empty)
+ ERROR_COUNT=${ERROR_COUNT:-0}
+ WARNING_COUNT=${WARNING_COUNT:-0}
+
+ echo "cppcheck found $ERROR_COUNT errors and $WARNING_COUNT warnings"
+
+ # Fail only if critical errors found (adjust threshold as needed)
+ if [ "${ERROR_COUNT}" -gt 0 ] 2>/dev/null; then
+ echo "cppcheck found errors"
+ exit 1
+ fi
+
+ - name: Upload cppcheck results
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: cppcheck-results
+ path: cppcheck-output.txt
+ retention-days: 7
+
+ scan-build:
+ name: clang Static Analyzer (scan-build)
+ runs-on: ubuntu-22.04
+ timeout-minutes: 45
+ steps:
+ - name: Checkout wolfProvider
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 1
+
+ - name: Install dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y clang clang-tools build-essential autoconf automake libtool pkg-config
+
+ - name: Build dependencies (OpenSSL and wolfSSL)
+ run: |
+ OPENSSL_TAG=openssl-3.5.4 WOLFSSL_TAG=master ./scripts/build-wolfprovider.sh 2>&1 | tail -100 || true
+
+ - name: Generate configure script
+ run: |
+ ./autogen.sh
+
+ - name: Configure project for scan-build
+ run: |
+ OPENSSL_INSTALL_DIR="$PWD/openssl-install"
+ WOLFSSL_INSTALL_DIR="$PWD/wolfssl-install"
+
+ # Configure with clang (scan-build will wrap it later)
+ CC=clang ./configure \
+ --with-openssl="$OPENSSL_INSTALL_DIR" \
+ --with-wolfssl="$WOLFSSL_INSTALL_DIR" \
+ --prefix="$PWD/wolfprov-install"
+
+ - name: Build library with scan-build
+ run: |
+ # Clean any previous build artifacts
+ make clean || true
+
+ # Build only the library with scan-build wrapping clang
+ # scan-build intercepts compiler calls, so we need to ensure CC is set to clang
+ # Use --use-cc and --use-c++ to explicitly tell scan-build which compiler to wrap
+ scan-build -o scan-build-output \
+ --use-cc=clang \
+ --use-c++=clang++ \
+ make -j$(nproc) CC=clang libwolfprov.la 2>&1 | tee scan-build-log.txt || true
+
+ # scan-build returns 0 even if build fails, so we check exit status explicitly
+ echo "Build completed (scan-build may have reports even if build partially failed)"
+
+ - name: Check scan-build results
+ run: |
+ # Find the latest scan-build report directory
+ REPORT_DIR=$(find scan-build-output -maxdepth 1 -type d -name "scan-build-*" | sort -r | head -1)
+
+ if [ -z "$REPORT_DIR" ] || [ ! -d "$REPORT_DIR" ]; then
+ echo "No scan-build report directory found"
+ exit 0
+ fi
+
+ # Count bugs found
+ BUG_COUNT=$(find "$REPORT_DIR" -name "*.html" | wc -l)
+ echo "scan-build found $BUG_COUNT potential issues"
+
+ # Display summary
+ if [ -f "$REPORT_DIR/index.html" ]; then
+ echo "View detailed report in scan-build-output/index.html"
+ # Extract text summary if possible
+ grep -o '
.*' "$REPORT_DIR/index.html" || true
+ fi
+
+ # Fail if critical bugs found (adjust threshold as needed)
+ if [ "$BUG_COUNT" -gt 50 ]; then
+ echo "Too many issues found by scan-build"
+ exit 1
+ fi
+
+ - name: Upload scan-build results
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: scan-build-results
+ path: scan-build-output/
+ retention-days: 7
+
+ infer:
+ name: Facebook Infer Static Analysis
+ runs-on: ubuntu-22.04
+ timeout-minutes: 60
+ steps:
+ - name: Checkout wolfProvider
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 1
+
+ - name: Install dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y build-essential autoconf automake libtool pkg-config python3 opam
+
+ # Install Infer
+ VERSION=1.1.0
+ cd /tmp
+ wget https://github.com/facebook/infer/releases/download/v${VERSION}/infer-linux64-v${VERSION}.tar.xz
+ tar xf infer-linux64-v${VERSION}.tar.xz
+ sudo mv infer-linux64-v${VERSION} /opt/infer
+ sudo ln -sf /opt/infer/bin/infer /usr/local/bin/infer
+
+ - name: Build dependencies (OpenSSL and wolfSSL)
+ run: |
+ OPENSSL_TAG=openssl-3.5.4 WOLFSSL_TAG=master ./scripts/build-wolfprovider.sh 2>&1 | tail -100 || true
+
+ - name: Generate configure script
+ run: |
+ ./autogen.sh
+
+ - name: Configure project
+ run: |
+ OPENSSL_INSTALL_DIR="$PWD/openssl-install"
+ WOLFSSL_INSTALL_DIR="$PWD/wolfssl-install"
+
+ ./configure \
+ --with-openssl="$OPENSSL_INSTALL_DIR" \
+ --with-wolfssl="$WOLFSSL_INSTALL_DIR" \
+ --prefix="$PWD/wolfprov-install" \
+ CC=clang
+
+ - name: Clean build for Infer
+ run: |
+ make clean || true
+ rm -rf infer-out
+
+ - name: Run Infer analysis
+ run: |
+ # Run infer on the build (it wraps the compilation)
+ # Build only the library to avoid test compilation issues
+ # Explicitly set CC=clang for make
+ infer run -- make -j$(nproc) CC=clang libwolfprov.la 2>&1 | tee infer-log.txt || true
+
+ # Generate text report
+ if [ -d infer-out ]; then
+ infer report --issues-csv infer-report.csv 2>&1 || true
+ infer report --issues-txt infer-report.txt 2>&1 || true
+
+ # Display summary
+ if [ -f infer-report.txt ]; then
+ echo "=== Infer Analysis Summary ==="
+ cat infer-report.txt
+
+ # Count issues
+ ISSUE_COUNT=$(grep -c "Found.*issue" infer-report.txt || echo "0")
+ echo "Infer found issues (check infer-report.txt for details)"
+
+ # Fail if too many critical issues (adjust threshold as needed)
+ if [ "$ISSUE_COUNT" -gt 100 ]; then
+ echo "Too many issues found by Infer"
+ exit 1
+ fi
+ fi
+ else
+ echo "Infer did not produce output directory"
+ fi
+
+ - name: Upload Infer results
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: infer-results
+ path: |
+ infer-out/
+ infer-report.txt
+ infer-report.csv
+ infer-log.txt
+ retention-days: 7
+ if-no-files-found: ignore
diff --git a/src/wp_aes_aead.c b/src/wp_aes_aead.c
index 3908d637..a8d0dc12 100644
--- a/src/wp_aes_aead.c
+++ b/src/wp_aes_aead.c
@@ -223,7 +223,7 @@ static int wp_aead_tls_init(wp_AeadCtx* ctx, unsigned char* aad, size_t aadLen)
{
int ok = 1;
unsigned char *buf = ctx->buf;
- size_t len;
+ size_t len = 0;
/* CCM will have a tag length set. */
size_t tagLen = (ctx->tagLen != UNINITIALISED_SIZET) ? ctx->tagLen :
EVP_GCM_TLS_TAG_LEN;
diff --git a/src/wp_dec_epki2pki.c b/src/wp_dec_epki2pki.c
index c58fb656..7284a254 100644
--- a/src/wp_dec_epki2pki.c
+++ b/src/wp_dec_epki2pki.c
@@ -187,11 +187,11 @@ static int wp_epki2pki_decode(wp_Epki2Pki* ctx, OSSL_CORE_BIO* coreBio,
{
int ok = 1;
int done = 0;
- int rc;
+ int rc = 0;
unsigned char* data = NULL;
word32 len = 0;
char password[1024];
- size_t passwordLen;
+ size_t passwordLen = 0;
word32 tradIdx = 0;
WOLFPROV_ENTER(WP_LOG_COMP_PK, "wp_epki2pki_decode");
diff --git a/src/wp_dec_pem2der.c b/src/wp_dec_pem2der.c
index c0b114f0..2000fb74 100644
--- a/src/wp_dec_pem2der.c
+++ b/src/wp_dec_pem2der.c
@@ -207,12 +207,12 @@ static int wp_pem2der_decode_data(const unsigned char* data, word32 len,
{
int ok = 1;
int done = 0;
- int rc;
- int algoId;
- int type;
+ int rc = 0;
+ int algoId = 0;
+ int type = 0;
const char* dataType = NULL;
const char* dataFormat = NULL;
- int obj;
+ int obj = 0;
EncryptedInfo info;
DerBuffer* der = NULL;
OSSL_PARAM params[5];
diff --git a/src/wp_ecc_kmgmt.c b/src/wp_ecc_kmgmt.c
index ca616222..95137a84 100644
--- a/src/wp_ecc_kmgmt.c
+++ b/src/wp_ecc_kmgmt.c
@@ -2222,9 +2222,9 @@ static int wp_ecc_decode_pki(wp_Ecc* ecc, unsigned char* data, word32 len)
/* Keys decoded from pki should always have public key */
if (ecc->key.type == ECC_PRIVATEKEY_ONLY) {
#ifdef ECC_TIMING_RESISTANT
- rc = wc_ecc_make_pub_ex(&ecc->key, NULL, &ecc->rng);
+ (void)wc_ecc_make_pub_ex(&ecc->key, NULL, &ecc->rng);
#else
- rc = wc_ecc_make_pub_ex(&ecc->key, NULL, NULL);
+ (void)wc_ecc_make_pub_ex(&ecc->key, NULL, NULL);
#endif
}
ecc->hasPub = 1;
diff --git a/src/wp_mac_sig.c b/src/wp_mac_sig.c
index fcd30ef3..c1ef17d9 100644
--- a/src/wp_mac_sig.c
+++ b/src/wp_mac_sig.c
@@ -187,10 +187,10 @@ static int wp_mac_digest_sign_init(wp_MacSigCtx *ctx, const char *mdName,
wp_Mac *mac, const OSSL_PARAM params[])
{
int ok = 1;
- unsigned char* priv;
- size_t privLen;
- const char* cipherName;
- const char* properties;
+ unsigned char* priv = NULL;
+ size_t privLen = 0;
+ const char* cipherName = NULL;
+ const char* properties = NULL;
OSSL_PARAM lParams[4];
int lParamSz = 0;
diff --git a/src/wp_rsa_kem.c b/src/wp_rsa_kem.c
index a8744fd4..7ee541bd 100644
--- a/src/wp_rsa_kem.c
+++ b/src/wp_rsa_kem.c
@@ -294,8 +294,8 @@ static int wp_rsasve_generate(wp_RsaKemCtx* ctx, unsigned char* out,
size_t* outLen, unsigned char* secret, size_t* secretLen)
{
int ok = 1;
- word32 nLen;
- word32 oLen;
+ word32 nLen = 0;
+ word32 oLen = 0;
RsaKey* rsa = NULL;
WOLFPROV_ENTER(WP_LOG_COMP_RSA, "wp_rsasve_generate");
diff --git a/test/test_rsa.c b/test/test_rsa.c
index c7042121..df3256cf 100644
--- a/test/test_rsa.c
+++ b/test/test_rsa.c
@@ -1569,12 +1569,17 @@ static int test_rsa_decode_pkcs8(void)
int pnum = RSA_get_multi_prime_extra_count(rsakey1);
BIGNUM **primes1 = malloc(sizeof(BIGNUM *) * pnum);
BIGNUM **primes2 = malloc(sizeof(BIGNUM *) * pnum);
- for (int i = 0; i < pnum; i++) {
- primes1[i] = BN_new();
- primes2[i] = BN_new();
- if (primes1[i] == NULL || primes2[i] == NULL) {
- err = 1;
- break;
+ if (primes1 == NULL || primes2 == NULL) {
+ err = 1;
+ }
+ if (err == 0) {
+ for (int i = 0; i < pnum; i++) {
+ primes1[i] = BN_new();
+ primes2[i] = BN_new();
+ if (primes1[i] == NULL || primes2[i] == NULL) {
+ err = 1;
+ break;
+ }
}
}
if (err == 0) {
@@ -1587,12 +1592,18 @@ static int test_rsa_decode_pkcs8(void)
}
}
}
- for (int i = 0; i < pnum; i++) {
- BN_free(primes1[i]);
- BN_free(primes2[i]);
+ if (primes1 != NULL && primes2 != NULL) {
+ for (int i = 0; i < pnum; i++) {
+ if (primes1[i] != NULL) {
+ BN_free(primes1[i]);
+ }
+ if (primes2[i] != NULL) {
+ BN_free(primes2[i]);
+ }
+ }
+ free(primes1);
+ free(primes2);
}
- free(primes1);
- free(primes2);
}
if (err == 0) {