name: Windows Build Test on: push: branches: [ 'master', 'main', 'release/**' ] pull_request: branches: [ '*' ] workflow_dispatch: env: WOLFSSL_SOLUTION_FILE_PATH: wolfssl64.sln SOLUTION_FILE_PATH: wolfssh.sln USER_SETTINGS_H_NEW: wolfssh/ide/winvs/user_settings.h USER_SETTINGS_H: wolfssl/IDE/WIN/user_settings.h INCLUDE_DIR: wolfssh # Configuration type to build. # You can convert this to a build matrix if you need coverage of multiple configuration types. # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix WOLFSSL_BUILD_CONFIGURATION: Release WOLFSSH_BUILD_CONFIGURATION: Release BUILD_PLATFORM: x64 TARGET_PLATFORM: 10 jobs: build: runs-on: windows-latest steps: - uses: actions/checkout@v2 with: repository: wolfssl/wolfssl path: wolfssl - uses: actions/checkout@master with: path: wolfssh - name: Add MSBuild to PATH uses: microsoft/setup-msbuild@v1 - name: Restore wolfSSL NuGet packages working-directory: ${{env.GITHUB_WORKSPACE}}wolfssl run: nuget restore ${{env.WOLFSSL_SOLUTION_FILE_PATH}} - name: Enable wolfSSH options (sshd, sftp, x509) in user_settings.h working-directory: ${{env.GITHUB_WORKSPACE}} shell: bash run: | sed -i 's/#if 0/#if 1/g' ${{env.USER_SETTINGS_H_NEW}} cp ${{env.USER_SETTINGS_H_NEW}} ${{env.USER_SETTINGS_H}} - name: Build wolfssl library working-directory: ${{env.GITHUB_WORKSPACE}}wolfssl run: msbuild /m /p:PlatformToolset=v142 /p:Platform=${{env.BUILD_PLATFORM}} /p:Configuration=${{env.WOLFSSL_BUILD_CONFIGURATION}} /t:wolfssl ${{env.WOLFSSL_SOLUTION_FILE_PATH}} - name: Restore NuGet packages working-directory: ${{env.GITHUB_WORKSPACE}}wolfssh\ide\winvs run: nuget restore ${{env.SOLUTION_FILE_PATH}} - name: Build wolfssh working-directory: ${{env.GITHUB_WORKSPACE}}wolfssh\ide\winvs # Add additional options to the MSBuild command line here (like platform or verbosity level). # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference run: msbuild /m /p:PlatformToolset=v142 /p:Platform=${{env.BUILD_PLATFORM}} /p:WindowsTargetPlatformVersion=${{env.TARGET_PLATFORM}} /p:Configuration=${{env.WOLFSSH_BUILD_CONFIGURATION}} ${{env.SOLUTION_FILE_PATH}} - name: Locate wolfsshd.exe and stage wolfssl.dll working-directory: ${{ github.workspace }}\wolfssh shell: pwsh run: | $sshdExe = Get-ChildItem -Path "${{ github.workspace }}\wolfssh" -Recurse -Filter "wolfsshd.exe" -ErrorAction SilentlyContinue | Where-Object { $_.FullName -like "*Release*" } | Select-Object -First 1 if (-not $sshdExe) { Write-Host "ERROR: wolfsshd.exe not found" exit 1 } Add-Content -Path $env:GITHUB_ENV -Value "SSHD_PATH=$($sshdExe.FullName)" $sshdDir = Split-Path -Parent $sshdExe.FullName $wolfsslDll = Get-ChildItem -Path "${{ github.workspace }}\wolfssl" -Recurse -Filter "wolfssl.dll" -ErrorAction SilentlyContinue | Select-Object -First 1 if ($wolfsslDll -and -not (Test-Path (Join-Path $sshdDir "wolfssl.dll"))) { Copy-Item -Path $wolfsslDll.FullName -Destination (Join-Path $sshdDir "wolfssl.dll") -Force } - name: Test LoginGraceTime enforcement on Windows working-directory: ${{ github.workspace }}\wolfssh\apps\wolfsshd\test shell: pwsh timeout-minutes: 2 run: .\sshd_login_grace_test.ps1 -SshdExe "$env:SSHD_PATH" - name: Test wolfsshd -D option parsing on Windows working-directory: ${{ github.workspace }}\wolfssh\apps\wolfsshd\test shell: pwsh timeout-minutes: 2 run: .\sshd_dash_d_test.ps1 -SshdExe "$env:SSHD_PATH" # Build and run the self-contained unit tests with the MSVC AddressSanitizer. # This is the only job that executes wolfSSH tests under a sanitizer on # Windows, where the USE_WINDOWS_API console code (e.g. wolfSSH_DoOSC) is # compiled, so it guards that path against out-of-bounds reads. asan-tests: runs-on: windows-latest env: # print_stats=1 emits allocator stats at exit, positive evidence that the # ASan runtime was active in-process during each test run. ASAN_OPTIONS: abort_on_error=1:print_stats=1 steps: - uses: actions/checkout@v2 with: repository: wolfssl/wolfssl path: wolfssl - uses: actions/checkout@master with: path: wolfssh - name: Add MSBuild to PATH uses: microsoft/setup-msbuild@v1 # The pinned v142 toolset does not ship the AddressSanitizer runtime libs on # the runner. Find an installed MSVC toolset that does and build everything in # this job with it, recording its lib/bin dirs for the link and run steps. # wolfssl, wolfssh and the test projects must share one compiler version # because /GL (whole program optimization) does code generation at link time. - name: Locate MSVC AddressSanitizer runtime shell: pwsh run: | $vs = & "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath $lib = Get-ChildItem "$vs\VC\Tools\MSVC\*\lib\x64\clang_rt.asan_dynamic_runtime_thunk-x86_64.lib" -ErrorAction SilentlyContinue | Sort-Object { [version]($_.FullName -replace '.*\\MSVC\\([0-9.]+)\\.*','$1') } | Select-Object -Last 1 if ($null -eq $lib) { throw "MSVC AddressSanitizer runtime not found in any installed toolset" } $msvcDir = $lib.Directory.Parent.Parent.FullName $ver = [version]($msvcDir.Split('\')[-1]) if ($ver.Minor -ge 30) { $toolset = "v143" } else { $toolset = "v142" } "ASAN_TOOLSET=$toolset" | Out-File $env:GITHUB_ENV -Append "ASAN_LIB_DIR=$($lib.Directory.FullName)" | Out-File $env:GITHUB_ENV -Append "ASAN_BIN_DIR=$(Join-Path $msvcDir 'bin\Hostx64\x64')" | Out-File $env:GITHUB_ENV -Append Write-Host "Using toolset $toolset (MSVC $ver)" - name: Restore wolfSSL NuGet packages working-directory: wolfssl run: nuget restore ${{env.WOLFSSL_SOLUTION_FILE_PATH}} # These env paths already include the wolfssh/ and wolfssl/ checkout # prefixes, so this runs from the default working directory, the workspace # root. - name: Enable wolfSSH options (sshd, sftp, x509) in user_settings.h shell: bash run: | sed -i 's/#if 0/#if 1/g' ${{env.USER_SETTINGS_H_NEW}} cp ${{env.USER_SETTINGS_H_NEW}} ${{env.USER_SETTINGS_H}} # WholeProgramOptimization=false disables /GL so the v142-independent objects # link without a cross-version code-generation requirement (C1047). - name: Build wolfssl library working-directory: wolfssl shell: pwsh run: | msbuild /m /p:PlatformToolset=$env:ASAN_TOOLSET /p:Platform=${{env.BUILD_PLATFORM}} /p:Configuration=${{env.WOLFSSL_BUILD_CONFIGURATION}} /p:WholeProgramOptimization=false /t:wolfssl ${{env.WOLFSSL_SOLUTION_FILE_PATH}} if ($LASTEXITCODE -ne 0) { throw "wolfssl build failed" } - name: Restore NuGet packages working-directory: wolfssh\ide\winvs run: nuget restore ${{env.SOLUTION_FILE_PATH}} # EnableASAN=true adds /fsanitize=address to each test project and to the # referenced wolfssh library project, instrumenting src/wolfterm.c. The # AddressSanitizer lib dir is added to LIB so the runtime thunk resolves. - name: Build api-test and unit-test with AddressSanitizer working-directory: wolfssh\ide\winvs shell: pwsh run: | $env:LIB = "$env:ASAN_LIB_DIR;$env:LIB" foreach ($p in @("api-test\api-test.vcxproj", "unit-test\unit-test.vcxproj")) { msbuild /m /p:PlatformToolset=$env:ASAN_TOOLSET /p:Platform=${{env.BUILD_PLATFORM}} /p:WindowsTargetPlatformVersion=${{env.TARGET_PLATFORM}} /p:Configuration=${{env.WOLFSSH_BUILD_CONFIGURATION}} /p:EnableASAN=true /p:WholeProgramOptimization=false $p if ($LASTEXITCODE -ne 0) { throw "build failed: $p" } } # Positive control: compile a deliberate heap overflow with the same toolset # and confirm ASan aborts on it. This proves ASan detection actually works on # this runner, so a clean test run is meaningful rather than a silent no-op. - name: Verify AddressSanitizer detection (positive control) shell: pwsh run: | $vs = & "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath Import-Module "$vs\Common7\Tools\Microsoft.VisualStudio.DevShell.dll" Enter-VsDevShell -VsInstallPath $vs -SkipAutomaticLocation -DevCmdArguments "-arch=x64" | Out-Null Set-Content canary.c 'int main(int argc, char** argv) { volatile char b[1]; (void)argv; return b[argc + 10]; }' cl /nologo /fsanitize=address /MD canary.c if ($LASTEXITCODE -ne 0) { throw "canary failed to compile" } $out = & .\canary.exe 2>&1 | Out-String $code = $LASTEXITCODE Write-Host $out if ($code -eq 0 -or $out -notmatch "AddressSanitizer") { throw "Positive control FAILED: ASan did not detect the deliberate overflow" } Write-Host "Positive control OK: ASan detected the deliberate out-of-bounds access" # canary.exe aborts with a non-zero code by design; reset so the step # itself reports success. exit 0 # Building a .vcxproj directly leaves $(SolutionDir) unset, so each project # writes to its own \Release\x64 dir. Run from the wolfssh repo root # so the tests find keys/ and certs/, and copy the dynamic ASAN runtime next # to each binary so it loads without a full developer-prompt environment. - name: Run tests under AddressSanitizer working-directory: wolfssh shell: pwsh run: | $cfg = "${{env.WOLFSSH_BUILD_CONFIGURATION}}\${{env.BUILD_PLATFORM}}" $dll = Join-Path $env:ASAN_BIN_DIR "clang_rt.asan_dynamic-x86_64.dll" if (-not (Test-Path $dll)) { throw "ASAN runtime DLL not found: $dll" } foreach ($t in @("api-test", "unit-test")) { $dir = "ide\winvs\$t\$cfg" # Static signal: confirm the binary really imports the ASan runtime, so # we are exercising an instrumented build and not a stale one. $deps = & "$env:ASAN_BIN_DIR\dumpbin.exe" /dependents "$dir\$t.exe" | Out-String if ($deps -notmatch "clang_rt.asan") { throw "$t is not linked against the ASan runtime" } Write-Host "$t links the ASan runtime:" ($deps -split "`n" | Select-String "asan").Line.Trim() | ForEach-Object { Write-Host " $_" } Copy-Item $dll $dir & "$dir\$t.exe" if ($LASTEXITCODE -ne 0) { throw "$t failed under ASAN (exit $LASTEXITCODE)" } } # Build and run the autotools regression tests under MSYS2 MinGW64. MinGW # defines _WIN32, so wolfssh/settings.h turns on USE_WINDOWS_API and the # Windows-only coverage in tests/regress.c (TestSftpWindowsOpenFlagMatrix, # which walks the RecvOpen CREAT/EXCL/TRUNC/APPEND matrix against the # CreateFile() disposition table) actually compiles and runs. The MSVC # solution has no regress project and regress.c does not build with cl (it # uses arpa/inet.h and unistd.h), so without this job that matrix runs in no # CI at all. wolfsshd is left out of the build: its autotools path is not # MinGW-clean and the MSVC solution already covers it. mingw-regress: name: MSYS2 MinGW64 regression tests runs-on: windows-latest timeout-minutes: 40 # Pinned rather than left on wolfssl/wolfssl's default branch, so an # upstream wolfSSL change can't silently break this job. Matches the # ref singlethread-check.yml and x509-interop.yml already pin to. env: WOLFSSL_REF: v5.9.1-stable defaults: run: shell: msys2 {0} steps: - name: Set up MSYS2 MinGW64 uses: msys2/setup-msys2@v2 with: msystem: MINGW64 update: false install: >- base-devel autotools git mingw-w64-x86_64-gcc mingw-w64-x86_64-pkgconf # The msys2 shell's $HOME isn't a stable path across runner images (it # lives under setup-msys2's temp install dir), so build to a # workspace-relative path instead: actions/cache resolves a relative # `path:` against GITHUB_WORKSPACE, and that same directory is what the # rest of this job's msys2 steps read back through $WOLFSSL_INSTALL. - name: Resolve wolfssl install prefix run: echo "WOLFSSL_INSTALL=$(cygpath -u "$GITHUB_WORKSPACE")/wolfssl-install" >> "$GITHUB_ENV" # A single job, unlike the build_wolfssl/build_wolfssh split # singlethread-check.yml and x509-interop.yml use: no lookup-only here, # since there is no downstream job for a second cache step to restore # into. This one restores on a hit and saves automatically post-job on # a miss. - name: Cache wolfssl build uses: actions/cache@v5 id: cache-wolfssl with: path: wolfssl-install key: wolfssh-mingw-regress-wolfssl-${{ env.WOLFSSL_REF }}-windows-latest - name: Checkout wolfssl if: steps.cache-wolfssl.outputs.cache-hit != 'true' uses: actions/checkout@v4 with: repository: wolfssl/wolfssl ref: ${{ env.WOLFSSL_REF }} path: wolfssl - name: Build and install wolfssl if: steps.cache-wolfssl.outputs.cache-hit != 'true' working-directory: wolfssl run: | ./autogen.sh # --enable-all pulls in --enable-crl-monitor, which wolfSSL's configure # rejects on MinGW (it is limited to linux, OS X, and freebsd). Turn it # back off explicitly; wolfSSH does not use the CRL monitor. ./configure --enable-all --disable-crl-monitor \ --enable-static --disable-shared \ --prefix="$WOLFSSL_INSTALL" make -j$(nproc) make install - name: Checkout wolfssh uses: actions/checkout@v4 with: path: wolfssh - name: Build and run the regression tests working-directory: wolfssh run: | # Run autoreconf directly instead of ./autogen.sh: for a git checkout # autogen.sh exports WARNINGS="all,error", turning autotools warnings # into errors that the MSYS2 automake can trip on. autoreconf -ivf # wolfssl is a static archive here, so its Windows socket (ws2_32) and # certificate store (crypt32) references are only resolved when this # configure's own AC_CHECK_LIB and later link steps pull them in too. ./configure --enable-sftp \ CPPFLAGS="-I$WOLFSSL_INSTALL/include" \ LDFLAGS="-L$WOLFSSL_INSTALL/lib" \ LIBS="-lws2_32 -lcrypt32" # MinGW's EXEEXT is ".exe", so the check_PROGRAMS targets automake # generates are tests/regress.test.exe and tests/unit.test.exe, not # the extension-less names make would use on a POSIX host. make -j$(nproc) tests/regress.test.exe tests/unit.test.exe ./tests/regress.test.exe ./tests/unit.test.exe - name: Show config.log on failure if: failure() working-directory: wolfssh run: cat config.log