diff --git a/.github/STATIC_ANALYSIS.md b/.github/STATIC_ANALYSIS.md new file mode 100644 index 000000000..758347efb --- /dev/null +++ b/.github/STATIC_ANALYSIS.md @@ -0,0 +1,59 @@ +# Static Analysis and Code Quality CI + +This workflow provides stricter code quality checks for the FSW repository. + +## Jobs + +### 1. Compiler Warnings (`compiler-warnings`) +- Builds all projects with `-Wall` enabled +- Catches common coding errors and potential issues at compile time +- Runs on `native_sim` platform + +### 2. Address Sanitizer (`asan-build`) +- Builds and tests with AddressSanitizer (ASAN) +- Detects memory errors: + - Use-after-free + - Heap buffer overflow + - Stack buffer overflow + - Memory leaks +- Only runs on `native_sim` (Linux-only) + +### 3. Undefined Behavior Sanitizer (`ubsan-build`) +- Builds and tests with UndefinedBehaviorSanitizer (UBSAN) +- Detects undefined behavior: + - Integer overflows + - Null pointer dereferences + - Invalid type casts + - Alignment violations +- Only runs on `native_sim` (Linux-only) + +### 4. Valgrind Memory Checker (`valgrind-test`) +- Runs executables under Valgrind +- Additional memory error detection +- Detects memory leaks and invalid memory access +- Uses suppressions file for known Zephyr false positives + +## Using Snippets Locally + +You can use these analysis tools locally when building: + +```bash +# Build with ASAN +west build -b native_sim app/your-app -- -DSNIPPET=asan + +# Build with UBSAN +west build -b native_sim app/your-app -- -DSNIPPET=ubsan + +# Build with compiler warnings +west build -b native_sim app/your-app -- -DSNIPPET=compiler-warnings +``` + +## Global Compiler Warnings + +All builds now include `-Wall` by default through the root `CMakeLists.txt`. +This ensures production code meets stricter quality standards. + +## References + +- [Zephyr Static Code Analysis](https://docs.zephyrproject.org/latest/develop/sca/index.html) +- [Zephyr Native Sim with Sanitizers](https://docs.zephyrproject.org/latest/boards/native/native_sim/doc/index.html) diff --git a/.github/valgrind-zephyr.supp b/.github/valgrind-zephyr.supp new file mode 100644 index 000000000..419c83118 --- /dev/null +++ b/.github/valgrind-zephyr.supp @@ -0,0 +1,37 @@ +# Valgrind suppressions for Zephyr RTOS +# This file suppresses known false positives and Zephyr-internal behaviors + +{ + zephyr_thread_stack_uninitialized + Memcheck:Cond + ... + fun:z_* +} + +{ + zephyr_syscall_uninitialized + Memcheck:Value* + ... + fun:z_* +} + +{ + zephyr_kernel_internal + Memcheck:Addr* + ... + fun:z_* +} + +{ + native_posix_driver + Memcheck:Leak + ... + fun:*posix* +} + +{ + native_sim_driver + Memcheck:Leak + ... + fun:*native* +} diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 000000000..e9b807997 --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,204 @@ +name: Static Analysis and Sanitizer Builds + +on: + push: + branches: + - main + pull_request: + branches: + - '*' + schedule: + - cron: "0 0 * * 1" + +# Limit permissions to read-only for security +permissions: + contents: read + +env: + # Only run on native_sim for sanitizer and analysis builds + platforms: | + native_sim + +jobs: + compiler-warnings: + name: Build with -Wall Compiler Warnings + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + path: FSW + fetch-depth: 2 + + - name: Run Starter Steps + uses: ./FSW/.github/actions/fsw-setup + + - name: Set test_roots based on changes + id: set_test_roots + shell: bash + working-directory: FSW + run: | + if [ "$GITHUB_REF_NAME" = "main" ]; then + # On main branch, run all test roots + python3 .github/scripts/determine_test_roots.py --map .github/hardware-roots.yaml --run-all > test_roots.txt + else + # On PR/branch, diff against main + git fetch origin main:refs/remotes/origin/main + git diff --name-only origin/main...HEAD > changed_files.txt + cat changed_files.txt | python3 .github/scripts/determine_test_roots.py --map .github/hardware-roots.yaml > test_roots.txt + fi + echo "test_roots<> $GITHUB_ENV + cat test_roots.txt >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + + - name: Build with compiler warnings + if: steps.set_test_roots.outcome == 'success' && env.test_roots != '' + working-directory: FSW + shell: bash + run: | + export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig + function add_arglist () { + echo "$2" | while read -r line; do + [ -z "$line" ] && continue + echo -n " $1 $line " + done; + } + west twister $(add_arglist -T "${{ env.test_roots }}") $(add_arglist -p "${{ env.platforms }}") -v --inline-logs --integration --extra-args="SNIPPET=compiler-warnings" + + asan-build: + name: Build and Test with Address Sanitizer (ASAN) + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + path: FSW + fetch-depth: 2 + + - name: Run Starter Steps + uses: ./FSW/.github/actions/fsw-setup + + - name: Set test_roots based on changes + id: set_test_roots + shell: bash + working-directory: FSW + run: | + if [ "$GITHUB_REF_NAME" = "main" ]; then + python3 .github/scripts/determine_test_roots.py --map .github/hardware-roots.yaml --run-all > test_roots.txt + else + git fetch origin main:refs/remotes/origin/main + git diff --name-only origin/main...HEAD > changed_files.txt + cat changed_files.txt | python3 .github/scripts/determine_test_roots.py --map .github/hardware-roots.yaml > test_roots.txt + fi + echo "test_roots<> $GITHUB_ENV + cat test_roots.txt >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + + - name: Build with ASAN + if: steps.set_test_roots.outcome == 'success' && env.test_roots != '' + working-directory: FSW + shell: bash + run: | + export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig + function add_arglist () { + echo "$2" | while read -r line; do + [ -z "$line" ] && continue + echo -n " $1 $line " + done; + } + west twister $(add_arglist -T "${{ env.test_roots }}") $(add_arglist -p "${{ env.platforms }}") -v --inline-logs --integration --extra-args="SNIPPET=asan" + + ubsan-build: + name: Build and Test with Undefined Behavior Sanitizer (UBSAN) + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + path: FSW + fetch-depth: 2 + + - name: Run Starter Steps + uses: ./FSW/.github/actions/fsw-setup + + - name: Set test_roots based on changes + id: set_test_roots + shell: bash + working-directory: FSW + run: | + if [ "$GITHUB_REF_NAME" = "main" ]; then + python3 .github/scripts/determine_test_roots.py --map .github/hardware-roots.yaml --run-all > test_roots.txt + else + git fetch origin main:refs/remotes/origin/main + git diff --name-only origin/main...HEAD > changed_files.txt + cat changed_files.txt | python3 .github/scripts/determine_test_roots.py --map .github/hardware-roots.yaml > test_roots.txt + fi + echo "test_roots<> $GITHUB_ENV + cat test_roots.txt >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + + - name: Build with UBSAN + if: steps.set_test_roots.outcome == 'success' && env.test_roots != '' + working-directory: FSW + shell: bash + run: | + export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig + function add_arglist () { + echo "$2" | while read -r line; do + [ -z "$line" ] && continue + echo -n " $1 $line " + done; + } + west twister $(add_arglist -T "${{ env.test_roots }}") $(add_arglist -p "${{ env.platforms }}") -v --inline-logs --integration --extra-args="SNIPPET=ubsan" + + valgrind-test: + name: Run with Valgrind Memory Checker + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + path: FSW + fetch-depth: 2 + + - name: Run Starter Steps + uses: ./FSW/.github/actions/fsw-setup + + - name: Install Valgrind + run: | + sudo apt-get update + sudo apt-get install -y valgrind + + - name: Set test_roots based on changes + id: set_test_roots + shell: bash + working-directory: FSW + run: | + if [ "$GITHUB_REF_NAME" = "main" ]; then + python3 .github/scripts/determine_test_roots.py --map .github/hardware-roots.yaml --run-all > test_roots.txt + else + git fetch origin main:refs/remotes/origin/main + git diff --name-only origin/main...HEAD > changed_files.txt + cat changed_files.txt | python3 .github/scripts/determine_test_roots.py --map .github/hardware-roots.yaml > test_roots.txt + fi + echo "test_roots<> $GITHUB_ENV + cat test_roots.txt >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + + - name: Build for Valgrind + if: steps.set_test_roots.outcome == 'success' && env.test_roots != '' + working-directory: FSW + shell: bash + run: | + export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig + # Set Valgrind options to use our suppressions file + # VALGRIND_OPTS is used by Valgrind when invoked by twister + export VALGRIND_OPTS="--suppressions=${{ github.workspace }}/FSW/.github/valgrind-zephyr.supp" + function add_arglist () { + echo "$2" | while read -r line; do + [ -z "$line" ] && continue + echo -n " $1 $line " + done; + } + # Build native_sim targets without sanitizers (sanitizers and Valgrind don't mix well) + west twister $(add_arglist -T "${{ env.test_roots }}") $(add_arglist -p "${{ env.platforms }}") -v --inline-logs --integration --enable-valgrind diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e8010861..764ce5d8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,9 @@ project(Backplane-Flight-Computer) zephyr_syscall_include_directories(include) zephyr_include_directories(include) + +# Enable strict compiler warnings for all builds +zephyr_compile_options(-Wall) add_subdirectory(drivers) add_subdirectory(lib) diff --git a/cmake/Snippets.cmake b/cmake/Snippets.cmake index 620862fba..87279c958 100644 --- a/cmake/Snippets.cmake +++ b/cmake/Snippets.cmake @@ -39,3 +39,15 @@ function(AddSimSnippets) AddSnippets(${SIM_SNIPPETS}) endfunction() + +function(AddCompilerWarningsSnippets) + AddSnippets("compiler-warnings") +endfunction() + +function(AddAsanSnippets) + AddSnippets("asan") +endfunction() + +function(AddUbsanSnippets) + AddSnippets("ubsan") +endfunction() diff --git a/snippets/asan/asan.conf b/snippets/asan/asan.conf new file mode 100644 index 000000000..04164af8a --- /dev/null +++ b/snippets/asan/asan.conf @@ -0,0 +1,3 @@ +# Address Sanitizer configuration +CONFIG_ASAN=y +CONFIG_NO_OPTIMIZATIONS=y diff --git a/snippets/asan/snippet.yml b/snippets/asan/snippet.yml new file mode 100644 index 000000000..a93ffc0d2 --- /dev/null +++ b/snippets/asan/snippet.yml @@ -0,0 +1,3 @@ +name: asan +append: + EXTRA_CONF_FILE: asan.conf diff --git a/snippets/compiler-warnings/compiler-warnings.conf b/snippets/compiler-warnings/compiler-warnings.conf new file mode 100644 index 000000000..d61d3b094 --- /dev/null +++ b/snippets/compiler-warnings/compiler-warnings.conf @@ -0,0 +1,3 @@ +# Enable compiler warnings for stricter code quality +CONFIG_COMPILER_WARNINGS_AS_ERRORS=y +CONFIG_COMPILER_COLOR_DIAGNOSTICS=y diff --git a/snippets/compiler-warnings/snippet.yml b/snippets/compiler-warnings/snippet.yml new file mode 100644 index 000000000..82a4cced6 --- /dev/null +++ b/snippets/compiler-warnings/snippet.yml @@ -0,0 +1,4 @@ +name: compiler-warnings +append: + EXTRA_CONF_FILE: compiler-warnings.conf + EXTRA_CFLAGS: -Wall diff --git a/snippets/ubsan/snippet.yml b/snippets/ubsan/snippet.yml new file mode 100644 index 000000000..3d4ef8254 --- /dev/null +++ b/snippets/ubsan/snippet.yml @@ -0,0 +1,3 @@ +name: ubsan +append: + EXTRA_CONF_FILE: ubsan.conf diff --git a/snippets/ubsan/ubsan.conf b/snippets/ubsan/ubsan.conf new file mode 100644 index 000000000..0e4f4a75c --- /dev/null +++ b/snippets/ubsan/ubsan.conf @@ -0,0 +1,3 @@ +# Undefined Behavior Sanitizer configuration +CONFIG_UBSAN=y +CONFIG_NO_OPTIMIZATIONS=y