Skip to content
Merged
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
50 changes: 50 additions & 0 deletions .ci/check-cppcheck.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env bash

# Run cppcheck static analysis on elfuse host source files (src/ only).
# Tests use raw-syscall stubs and are excluded.
#
# CI mode: --max-configs=1 + --enable=warning for speed. Generated headers
# under build/ that the source #includes must exist before invocation:
# build/dispatch.h -- via scripts/gen-syscall-dispatch.py
# build/version.h -- one-line #define
# build/shim_blob.h -- empty stub (the byte array contents are opaque
# to cppcheck and produce no useful findings)
#
# Generating real dispatch.h instead of stubbing it keeps cppcheck honest
# about the syscall dispatch layer. Stubbing shim_blob.h is acceptable
# because the file is just a byte array with no callable surface.

set -e -u -o pipefail

mapfile -d '' SOURCES < <(git ls-files -z -- 'src/*.c' 'src/**/*.c')

if [ ${#SOURCES[@]} -eq 0 ]; then
echo "No tracked C source files found."
exit 0
fi

BUILD_DIR=$(mktemp -d)
trap 'rm -rf "$BUILD_DIR"' EXIT

python3 scripts/gen-syscall-dispatch.py --output "$BUILD_DIR/dispatch.h"
printf '#define ELFUSE_VERSION "ci"\n' > "$BUILD_DIR/version.h"
# shim_blob.h declares an opaque byte array and its length; cppcheck
# gains nothing from the real contents, so a minimal stub suffices.
cat > "$BUILD_DIR/shim_blob.h" << 'EOF'
static const unsigned char shim_bin[1] = {0};
static const unsigned int shim_bin_len = 1;
EOF

# 120s is generous -- this should finish well below that with --max-configs=1.
timeout 120 cppcheck \
-I. -Isrc -I"$BUILD_DIR" \
--platform=unix64 \
--enable=warning \
--max-configs=1 --error-exitcode=1 --inline-suppr \
--suppress=checkersReport --suppress=unmatchedSuppression \
--suppress=missingIncludeSystem --suppress=noValidConfiguration \
--suppress=normalCheckLevelMaxBranches \
--suppress=preprocessorErrorDirective \
--suppress=missingInclude \
-D_GNU_SOURCE -D__APPLE__ -D__aarch64__ \
"${SOURCES[@]}"
38 changes: 38 additions & 0 deletions .ci/check-format.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env bash

# Verify clang-format conformance for all tracked C/H files in src/ and
# tests/. The repository's .clang-format is calibrated against
# clang-format-22; older versions produce different output and are
# rejected to keep CI deterministic.

set -u -o pipefail

if [ -z "${CLANG_FORMAT:-}" ]; then
if command -v clang-format-22 > /dev/null 2>&1; then
CLANG_FORMAT="clang-format-22"
elif command -v clang-format > /dev/null 2>&1; then
# Allow the unversioned binary only if it reports v22.x.
if clang-format --version 2> /dev/null | grep -qE 'version 22\.'; then
CLANG_FORMAT="clang-format"
else
echo "Error: clang-format-22 is required (older versions differ in style)" >&2
exit 1
fi
else
echo "Error: clang-format-22 is required (older versions differ in style)" >&2
exit 1
fi
fi

ret=0
while IFS= read -r -d '' file; do
expected=$(mktemp)
"$CLANG_FORMAT" "$file" > "$expected" 2> /dev/null
if ! diff -u -p --label="$file" --label="expected coding style" "$file" "$expected"; then
ret=1
fi
rm -f "$expected"
done < <(git ls-files -z -- 'src/*.c' 'src/*.h' 'src/**/*.c' 'src/**/*.h' \
'tests/*.c' 'tests/*.h')

exit $ret
20 changes: 20 additions & 0 deletions .ci/check-newline.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash

# Ensure all tracked C/H/S/sh files end with a newline.

set -e -u -o pipefail

ret=0
while IFS= read -rd '' f; do
# `-b` prints just the encoding (e.g. "us-ascii", "binary", "utf-8")
# without the filename, so a path containing the word "binary" can't
# cause a non-binary file to be skipped.
if [ "$(file -b --mime-encoding -- "$f")" != "binary" ]; then
if [ -n "$(tail -c1 < "$f")" ]; then
echo "Warning: No newline at end of file $f"
ret=1
fi
fi
done < <(git ls-files -z -- '*.c' '*.h' '*.S' '*.sh' '*.py' '*.mk' Makefile)

exit $ret
50 changes: 50 additions & 0 deletions .ci/check-security.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env bash

# Security checks for elfuse host source files (src/ only).
# Tests are excluded -- they exercise unsafe patterns deliberately.
#
# 1. Banned functions -- unsafe libc calls with safer alternatives.
# 2. Credential / secret patterns -- catch accidental key leaks.
# 3. Dangerous preprocessor -- detect disabled security features.

set -u -o pipefail

failed=0

# --- Patterns ---
banned='(^|[^[:alnum:]_])(gets|sprintf|vsprintf|strcpy|stpcpy|strcat|atoi|atol|atoll|atof|mktemp|tmpnam|tempnam)[[:space:]]*\('
secrets='(password|secret|api_key|private_key|token)[[:space:]]*=[[:space:]]*"[^"]+'
dangerous_pp='#[[:space:]]*(undef|define)[[:space:]]+((_FORTIFY_SOURCE[[:space:]]+0)|(__SSP__))'
comment_only='^[[:space:]]*(//|/\*|\*|\*/)'

# Only scan elfuse host source, not tests/ or assembly shim.
#
# Each match uses process substitution rather than a shell pipeline:
# under `pipefail`, an early `grep -q` exit closes its stdin, the
# upstream filter receives SIGPIPE, and the pipeline returns non-zero
# even when the pattern matched -- silently dropping real findings.
# Process substitution puts the filter in a separate process whose exit
# status doesn't feed back into the matcher.
while IFS= read -r -d '' f; do
if grep -qE "$banned" < <(grep -vE "$comment_only" -- "$f"); then
echo "Banned function in $f:"
grep -nE "$banned" -- "$f" | grep -vE "$comment_only" || true
failed=1
fi
if grep -iqE "$secrets" < <(grep -vE "$comment_only" -- "$f"); then
echo "Possible hardcoded secret in $f:"
grep -inE "$secrets" -- "$f" | grep -vE "$comment_only" || true
failed=1
fi
if grep -qE "$dangerous_pp" < <(grep -vE "$comment_only" -- "$f"); then
echo "Dangerous preprocessor directive in $f:"
grep -nE "$dangerous_pp" -- "$f" | grep -vE "$comment_only" || true
failed=1
fi
done < <(git ls-files -z -- 'src/*.c' 'src/*.h' 'src/**/*.c' 'src/**/*.h')

if [ $failed -eq 0 ]; then
echo "Security checks passed."
fi

exit $failed
Loading
Loading