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
187 changes: 187 additions & 0 deletions contrib/windows/codeql/Invoke-CodeQLZFSinAnalysis.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# *****************************************************************************
# Copyright (c) 2026 DataCore Software Corporation. All rights reserved.
# *****************************************************************************

<#
.SYNOPSIS
Runs CodeQL static analysis over the ZFSin kernel driver target only
(not the full CMake build) and reports the Must-Fix findings required
for the HLK "Static Tools Logo Test" (WHCP certification).

.DESCRIPTION
1. Builds a CodeQL database by tracing a CMake+Ninja build of ONLY the
`ZFSin` target (the driver .sys and everything statically linked
into it: splkern, zlibkern, icpkern, luakern, zfskern, zfskern_os,
zcommonkern, nvpairkern, unicodekern, zstdkern). This deliberately
excludes the user-mode tools (zfs.exe, zpool.exe, zfsinstaller.exe,
etc.) and user-mode libraries (libzfs, libnvpair, libzpool, ...) -
several of those share source files with the driver (e.g.
module/zfs/*.c is also compiled into libzpool) but under different
macros/headers, and mixing both into one database produces
confusing multi-context findings for the same source line.
2. Analyzes the database with the WHCP `mustfix.qls` suite, writing
SARIF to the repo root.
3. Prints a summary grouped by rule/API.

.PREREQUISITES
- CodeQL CLI 2.20.1 unpacked to -CodeQLHome (WHCP matrix version).
- Query packs downloaded: microsoft/windows-drivers@1.8.0 and
microsoft/cpp-queries@0.0.4.
- VS2019 (vcvars64.bat) + WDK 10.0.19041 + OpenSSL-Win64 + the
prebuilt ISA-L static libs under lib\ISA-L\<Debug|Release> - all
already required by this repo's normal CMake build.

.EXAMPLE
.\Invoke-CodeQLZFSinAnalysis.ps1
#>

PARAM
(
[Parameter(HelpMessage = "Directory containing the CodeQL CLI (codeql.exe)")]
[string]$CodeQLHome = "C:\codeql-home\codeql",

[Parameter(HelpMessage = "WHCP query suite to run")]
[ValidateSet("mustfix", "recommended", "mustrun")]
[string]$Suite = "mustfix",

[Parameter(HelpMessage = "CMake build configuration")]
[ValidateSet("Debug", "Release")]
[string]$Configuration = "Debug",

[Parameter(HelpMessage = "Path to vcvars64.bat (VS2019)")]
[string]$VcVars64 = "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Auxiliary\Build\vcvars64.bat",

[Parameter(HelpMessage = "Directory for the CodeQL database")]
[string]$DatabasePath,

[Parameter(HelpMessage = "Output SARIF path")]
[string]$SarifPath,

[Parameter(HelpMessage = "Reuse an existing CodeQL database instead of rebuilding it")]
[switch]$ReuseDatabase
)

$ErrorActionPreference = "Stop"

# Repo root is two levels up from contrib\windows\codeql.
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..\..")).Path

if (!$DatabasePath) { $DatabasePath = Join-Path $repoRoot "out\CodeQL\databases\ZFSin" }
if (!$SarifPath) { $SarifPath = Join-Path $repoRoot "ZFSin.codeql.sarif" }

$buildDir = Join-Path $repoRoot "out\build\codeql-zfsin"

function Invoke-Tool([string]$Exe, [string[]]$ToolArgs)
{
$prevEap = $script:ErrorActionPreference
$script:ErrorActionPreference = "Continue"
try
{
& $Exe @ToolArgs 2>&1 | ForEach-Object { Write-Host $_.ToString() }
return $LASTEXITCODE
}
finally
{
$script:ErrorActionPreference = $prevEap
}
}

$codeqlExe = Join-Path $CodeQLHome "codeql.exe"
if (!(Test-Path $codeqlExe))
{
$nested = Join-Path $CodeQLHome "codeql\codeql.exe"
if (Test-Path $nested) { $codeqlExe = $nested }
}
if (!(Test-Path $codeqlExe))
{
throw "codeql.exe not found under '$CodeQLHome'."
}

if (!(Test-Path $VcVars64))
{
throw "vcvars64.bat not found at '$VcVars64'. Pass -VcVars64 explicitly."
}

$suiteSpec = "microsoft/windows-drivers:windows-driver-suites\$Suite.qls"

Write-Host "Repo root : $repoRoot"
Write-Host "CodeQL : $codeqlExe"
Write-Host "Query suite : $suiteSpec"
Write-Host "Build dir : $buildDir"
Write-Host "Database : $DatabasePath"
Write-Host "SARIF output : $SarifPath"
Write-Host ""

# Fail early if the WHCP query packs are missing.
$prevEap = $ErrorActionPreference
$ErrorActionPreference = "Continue"
$qlpacks = (& $codeqlExe resolve packs 2>&1 | ForEach-Object { $_.ToString() }) -join "`n"
$ErrorActionPreference = $prevEap
if ($qlpacks -notmatch "microsoft/windows-drivers")
{
throw "Query pack microsoft/windows-drivers not found. Run: codeql pack download microsoft/windows-drivers@1.8.0 (and microsoft/cpp-queries@0.0.4)."
}

New-Item -ItemType Directory -Force (Split-Path $DatabasePath -Parent) > $null

if ($ReuseDatabase -and (Test-Path (Join-Path $DatabasePath "codeql-database.yml")))
{
Write-Host "Reusing existing database $DatabasePath"
}
else
{
# Build script: vcvars64 -> configure (only if needed) -> build ONLY the
# ZFSin target (not the default `all` target, which would also build the
# unrelated user-mode tools/libraries).
$buildScript = Join-Path $env:TEMP "build-zfsin-codeql.cmd"
@"
@echo off
setlocal
call "$VcVars64"
if errorlevel 1 exit /b 1
set "CMAKE=%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe"
set "NINJA=%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja\ninja.exe"
cd /d "$repoRoot"
if not exist "$buildDir\CMakeCache.txt" (
"%CMAKE%" -S . -B "$buildDir" -G Ninja -DCMAKE_BUILD_TYPE=$Configuration -DCMAKE_MAKE_PROGRAM="%NINJA%"
if errorlevel 1 exit /b 1
)
"%CMAKE%" --build "$buildDir" --target ZFSin --clean-first
if errorlevel 1 exit /b 1
exit /b 0
"@ | Out-File -FilePath $buildScript -Encoding ASCII

if (Test-Path $DatabasePath) { Remove-Item -Recurse -Force $DatabasePath }
$code = Invoke-Tool $codeqlExe @("database", "create", $DatabasePath, "--language=cpp",
"--source-root=$repoRoot", "--command=$buildScript", "--overwrite")
if ($code -ne 0) { throw "codeql database create failed (exit $code)" }
}

$code = Invoke-Tool $codeqlExe @("database", "analyze", $DatabasePath, $suiteSpec,
"--format=sarifv2.1.0", "--output=$SarifPath", "--rerun")
if ($code -ne 0) { throw "codeql database analyze failed (exit $code)" }

$sarif = Get-Content $SarifPath -Raw | ConvertFrom-Json
$findings = @()
foreach ($run in $sarif.runs) { if ($run.results) { $findings += $run.results } }

Write-Host ""
Write-Host "================ ZFSin ($Suite) ================" -ForegroundColor Cyan
if ($findings.Count -eq 0)
{
Write-Host "clean - 0 findings" -ForegroundColor Green
}
else
{
Write-Host "$($findings.Count) finding(s):" -ForegroundColor Yellow
$findings | Group-Object ruleId | Sort-Object Count -Descending | ForEach-Object {
Write-Host (" {0,4} x {1}" -f $_.Count, $_.Name) -ForegroundColor Yellow
}
if ($Suite -eq "mustfix")
{
Write-Host ""
Write-Host "FAILS certification until fixed" -ForegroundColor Red
}
}

exit $findings.Count
50 changes: 4 additions & 46 deletions include/os/windows/spl/sys/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,54 +146,12 @@ spl_snprintf(char *buf, size_t size, const char *fmt, ...)
*/

/*
* Kernel-mode strncpy() does not NUL-terminate if strlen(src) >= n,
* and zero-fills the whole remainder of the buffer if strlen(src) < n
* - neither behavior is depended on by any of this codebase's callers.
* strlcpy() is the semantically-closest safe replacement (always
* terminates, never overflows) but has no kernel-linkable
* implementation here, so provide one - mirrors lib/libspl/strlcpy.c's
* existing user-mode algorithm exactly. Callers pass n == the size of
* the destination buffer (or the intended-substring-length + 1),
* strncpy() and strcat() are replaced tree-wide by strlcpy() and
* strlcat(), which are declared in sys/sunddi.h and implemented for
* kernel mode in module/os/windows/spl/spl-ddi.c. Callers pass n == the
* size of the destination buffer (or the intended-substring-length + 1),
* unlike strncpy's n == copy-length - not a drop-in same-args swap.
*/
static __inline size_t
spl_strlcpy(char *dst, const char *src, size_t dstsize)
{
size_t srclen = strlen(src);
size_t copied = (srclen < dstsize) ? srclen : dstsize - 1;

if (dstsize != 0) {
memcpy(dst, src, copied);
dst[copied] = '\0';
}
return (srclen);
}

/*
* strcat() has no size parameter at all - unbounded by construction.
* strlcat() is the closest safe replacement (always terminates, never
* overflows, return value is the total length it tried to create) but
* - same as strlcpy() - has no kernel-linkable implementation here.
* Mirrors lib/libspl/strlcat.c's existing user-mode algorithm exactly.
*/
static __inline size_t
spl_strlcat(char *dst, const char *src, size_t dstsize)
{
char *df = dst;
size_t left = dstsize;
size_t l1, l2 = strlen(src), copied;

while (left-- != 0 && *df != '\0')
df++;
l1 = df - dst;
if (dstsize == l1)
return (l1 + l2);

copied = (l1 + l2 >= dstsize) ? dstsize - l1 - 1 : l2;
memcpy(dst + l1, src, copied);
dst[l1 + copied] = '\0';
return (l1 + l2);
}

#ifndef ULLONG_MAX
#define ULLONG_MAX (~0ULL)
Expand Down
24 changes: 6 additions & 18 deletions lib/libspl/include/os/windows/sys/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,27 +232,15 @@ typedef uint64_t zoff_t;
#include <stdio.h>

/*
* Mirrors include/os/windows/spl/sys/types.h's kernel-mode shims of the
* Mirrors include/os/windows/spl/sys/types.h's kernel-mode shim of the
* same name. Several shared module/zfs, module/icp, and module/lua
* source files (built both into the ZFSin kernel driver and into
* user-mode libzpool/libicp/zlib here) call these directly by name, not
* through a portable macro. In user mode, real strlcpy/strlcat
* (lib/libspl) and real, C99-conformant UCRT vsnprintf are already
* available, so these are simple passthroughs - no downlevel-
* unavailability workaround is needed here, unlike the kernel version.
* user-mode libzpool/libicp/zlib here) call this directly by name, not
* through a portable macro. In user mode a real, C99-conformant UCRT
* vsnprintf is already available, so this is a simple passthrough - no
* downlevel-unavailability workaround is needed here, unlike the kernel
* version.
*/
static __inline size_t
spl_strlcpy(char *dst, const char *src, size_t dstsize)
{
return (strlcpy(dst, src, dstsize));
}

static __inline size_t
spl_strlcat(char *dst, const char *src, size_t dstsize)
{
return (strlcat(dst, src, dstsize));
}

static __inline int
spl_vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
{
Expand Down
8 changes: 4 additions & 4 deletions lib/os/windows/zlib-1.2.3/gzio.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ gz_open(
if (s->path == NULL) {
return (destroy(s), (gzFile)Z_NULL);
}
spl_strlcpy(s->path, path, strlen(path) + 1); /* do this early for debugging */
strlcpy(s->path, path, strlen(path) + 1); /* do this early for debugging */

s->mode = '\0';
do {
Expand Down Expand Up @@ -1094,9 +1094,9 @@ gzerror(
s->msg = (char *)ALLOC(strlen(s->path) + strlen(m) + 3);
if (s->msg == Z_NULL)
return ((const char *)ERR_MSG(Z_MEM_ERROR));
spl_strlcpy(s->msg, s->path, strlen(s->path) + strlen(m) + 3);
spl_strlcat(s->msg, ": ", strlen(s->path) + strlen(m) + 3);
spl_strlcat(s->msg, m, strlen(s->path) + strlen(m) + 3);
strlcpy(s->msg, s->path, strlen(s->path) + strlen(m) + 3);
strlcat(s->msg, ": ", strlen(s->path) + strlen(m) + 3);
strlcat(s->msg, m, strlen(s->path) + strlen(m) + 3);
return ((const char *)s->msg);
}

Expand Down
32 changes: 16 additions & 16 deletions module/icp/core/kcf_mech_tabs.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,72 +177,72 @@ kcf_init_mech_tabs(void)
/* Then the pre-defined mechanism entries */

/* Two digests */
(void) spl_strlcpy(kcf_digest_mechs_tab[0].me_name, SUN_CKM_MD5,
(void) strlcpy(kcf_digest_mechs_tab[0].me_name, SUN_CKM_MD5,
CRYPTO_MAX_MECH_NAME);
kcf_digest_mechs_tab[0].me_threshold = kcf_md5_threshold;

(void) spl_strlcpy(kcf_digest_mechs_tab[1].me_name, SUN_CKM_SHA1,
(void) strlcpy(kcf_digest_mechs_tab[1].me_name, SUN_CKM_SHA1,
CRYPTO_MAX_MECH_NAME);
kcf_digest_mechs_tab[1].me_threshold = kcf_sha1_threshold;

/* The symmetric ciphers in various modes */
(void) spl_strlcpy(kcf_cipher_mechs_tab[0].me_name, SUN_CKM_DES_CBC,
(void) strlcpy(kcf_cipher_mechs_tab[0].me_name, SUN_CKM_DES_CBC,
CRYPTO_MAX_MECH_NAME);
kcf_cipher_mechs_tab[0].me_threshold = kcf_des_threshold;

(void) spl_strlcpy(kcf_cipher_mechs_tab[1].me_name, SUN_CKM_DES3_CBC,
(void) strlcpy(kcf_cipher_mechs_tab[1].me_name, SUN_CKM_DES3_CBC,
CRYPTO_MAX_MECH_NAME);
kcf_cipher_mechs_tab[1].me_threshold = kcf_des3_threshold;

(void) spl_strlcpy(kcf_cipher_mechs_tab[2].me_name, SUN_CKM_DES_ECB,
(void) strlcpy(kcf_cipher_mechs_tab[2].me_name, SUN_CKM_DES_ECB,
CRYPTO_MAX_MECH_NAME);
kcf_cipher_mechs_tab[2].me_threshold = kcf_des_threshold;

(void) spl_strlcpy(kcf_cipher_mechs_tab[3].me_name, SUN_CKM_DES3_ECB,
(void) strlcpy(kcf_cipher_mechs_tab[3].me_name, SUN_CKM_DES3_ECB,
CRYPTO_MAX_MECH_NAME);
kcf_cipher_mechs_tab[3].me_threshold = kcf_des3_threshold;

(void) spl_strlcpy(kcf_cipher_mechs_tab[4].me_name, SUN_CKM_BLOWFISH_CBC,
(void) strlcpy(kcf_cipher_mechs_tab[4].me_name, SUN_CKM_BLOWFISH_CBC,
CRYPTO_MAX_MECH_NAME);
kcf_cipher_mechs_tab[4].me_threshold = kcf_bf_threshold;

(void) spl_strlcpy(kcf_cipher_mechs_tab[5].me_name, SUN_CKM_BLOWFISH_ECB,
(void) strlcpy(kcf_cipher_mechs_tab[5].me_name, SUN_CKM_BLOWFISH_ECB,
CRYPTO_MAX_MECH_NAME);
kcf_cipher_mechs_tab[5].me_threshold = kcf_bf_threshold;

(void) spl_strlcpy(kcf_cipher_mechs_tab[6].me_name, SUN_CKM_AES_CBC,
(void) strlcpy(kcf_cipher_mechs_tab[6].me_name, SUN_CKM_AES_CBC,
CRYPTO_MAX_MECH_NAME);
kcf_cipher_mechs_tab[6].me_threshold = kcf_aes_threshold;

(void) spl_strlcpy(kcf_cipher_mechs_tab[7].me_name, SUN_CKM_AES_ECB,
(void) strlcpy(kcf_cipher_mechs_tab[7].me_name, SUN_CKM_AES_ECB,
CRYPTO_MAX_MECH_NAME);
kcf_cipher_mechs_tab[7].me_threshold = kcf_aes_threshold;

(void) spl_strlcpy(kcf_cipher_mechs_tab[8].me_name, SUN_CKM_RC4,
(void) strlcpy(kcf_cipher_mechs_tab[8].me_name, SUN_CKM_RC4,
CRYPTO_MAX_MECH_NAME);
kcf_cipher_mechs_tab[8].me_threshold = kcf_rc4_threshold;


/* 4 HMACs */
(void) spl_strlcpy(kcf_mac_mechs_tab[0].me_name, SUN_CKM_MD5_HMAC,
(void) strlcpy(kcf_mac_mechs_tab[0].me_name, SUN_CKM_MD5_HMAC,
CRYPTO_MAX_MECH_NAME);
kcf_mac_mechs_tab[0].me_threshold = kcf_md5_threshold;

(void) spl_strlcpy(kcf_mac_mechs_tab[1].me_name, SUN_CKM_MD5_HMAC_GENERAL,
(void) strlcpy(kcf_mac_mechs_tab[1].me_name, SUN_CKM_MD5_HMAC_GENERAL,
CRYPTO_MAX_MECH_NAME);
kcf_mac_mechs_tab[1].me_threshold = kcf_md5_threshold;

(void) spl_strlcpy(kcf_mac_mechs_tab[2].me_name, SUN_CKM_SHA1_HMAC,
(void) strlcpy(kcf_mac_mechs_tab[2].me_name, SUN_CKM_SHA1_HMAC,
CRYPTO_MAX_MECH_NAME);
kcf_mac_mechs_tab[2].me_threshold = kcf_sha1_threshold;

(void) spl_strlcpy(kcf_mac_mechs_tab[3].me_name, SUN_CKM_SHA1_HMAC_GENERAL,
(void) strlcpy(kcf_mac_mechs_tab[3].me_name, SUN_CKM_SHA1_HMAC_GENERAL,
CRYPTO_MAX_MECH_NAME);
kcf_mac_mechs_tab[3].me_threshold = kcf_sha1_threshold;


/* 1 random number generation pseudo mechanism */
(void) spl_strlcpy(kcf_misc_mechs_tab[0].me_name, SUN_RANDOM,
(void) strlcpy(kcf_misc_mechs_tab[0].me_name, SUN_RANDOM,
CRYPTO_MAX_MECH_NAME);

kcf_mech_hash = mod_hash_create_strhash_nodtr("kcf mech2id hash",
Expand Down
Loading
Loading