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
23 changes: 23 additions & 0 deletions .github/ubsan-suppressions.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# UBSan runtime suppressions for rsync.
#
# These entries suppress intentional, architecture-safe UB that we do NOT want
# to fix in source. Every other UBSan check class runs FULLY.
#
# Format: <check>:<function-or-glob>
# "alignment" fires when a misaligned pointer dereference is detected.
#
# byteorder.h -- deliberate unaligned 32/64-bit access on the !CAREFUL_ALIGNMENT
# (x86/amd64) fast path. The union-pun approach is intentional; the header's own
# comment documents this. CAREFUL_ALIGNMENT=1 selects a byte-shuffle fallback for
# strict-alignment architectures; the CI runner is x86-64 so the fast path is
# compiled. Suppressed at runtime rather than disabled class-wide so that any NEW
# misaligned access outside byteorder.h is still caught.
alignment:IVALu
alignment:SIVALu
alignment:IVAL64
alignment:SIVAL64
# log.c log_delete() -- intentional pool-style allocation: new_array0(char,...) +
# pointer-arithmetic offset then cast to struct file_struct*. Same x86 alignment-
# tolerance assumption as byteorder.h; not a memory-safety bug.
alignment:log_delete
alignment:log_formatted
51 changes: 51 additions & 0 deletions .github/workflows/analysis-codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: CodeQL (cpp)

# GitHub CodeQL static analysis for C/C++. Uses a MANUAL build (not autobuild)
# because rsync needs ./prepare-source to generate sources before configure/make,
# which autobuild can miss.
#
# GATING: NON-gating by default -- CodeQL surfaces results in the repository's
# Security tab and as PR code-scanning annotations rather than failing this job.
# (Branch-protection "code scanning results" rules are the proper place to gate
# on CodeQL, configured in repo settings, not here.) CI-ONLY: no source/flag
# change; the build uses --disable-md2man like every other CI job.

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
schedule:
- cron: '17 5 * * 1'
workflow_dispatch:

jobs:
analyze:
name: CodeQL analyze (cpp)
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
run: |
sudo apt-get update
sudo apt-get install -y acl libacl1-dev attr libattr1-dev \
liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: cpp
- name: Manual build
run: |
./prepare-source
./configure --disable-md2man
make
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:cpp"
65 changes: 65 additions & 0 deletions .github/workflows/analysis-cppcheck.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Static analysis (cppcheck)

# cppcheck baseline scan. GATING: NON-gating -- `--error-exitcode=0` means
# cppcheck never fails the job; it produces a baseline report artifact + a
# job-summary count. No local baseline (cppcheck is not in the dev shell), so
# this stays non-gating until a CI baseline is triaged. CI-ONLY: no source/flag
# change, cppcheck only reads the sources.

on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/analysis-cppcheck.yml'
pull_request:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/analysis-cppcheck.yml'
schedule:
- cron: '17 3 * * *'
workflow_dispatch:

jobs:
cppcheck:
runs-on: ubuntu-latest
name: cppcheck
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
run: |
sudo apt-get update
sudo apt-get install -y cppcheck
- name: prepare-source
# Generate config/derived sources so cppcheck sees the real tree.
run: ./prepare-source || true
- name: cppcheck
run: |
set -o pipefail
cppcheck --enable=warning,portability --error-exitcode=0 \
--inline-suppr --std=c11 \
--suppress=missingInclude --suppress=missingIncludeSystem \
-i zlib -i popt \
. 2>&1 | tee cppcheck.log
- name: report
if: always()
run: |
{
echo "## cppcheck (baseline, non-gating)"
n=$(grep -c "): " cppcheck.log || true)
echo "Findings: ${n:-0}"
echo '```'
grep -oE '\[[a-zA-Z]+\]$' cppcheck.log | sort | uniq -c | sort -rn | head -20 || true
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
- name: upload report
if: always()
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: cppcheck-log
path: cppcheck.log
if-no-files-found: ignore
79 changes: 79 additions & 0 deletions .github/workflows/analysis-gcc-fanalyzer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: Static analysis (gcc -fanalyzer)

# GCC's built-in static analyzer, enabled purely via env CFLAGS passthrough
# (configure.ac preserves CFLAGS -- the same mechanism --enable-coverage uses).
#
# GATING: NON-gating. On HEAD this is clean (0 warnings locally with gcc 15),
# but -fanalyzer's findings vary across gcc versions and configure paths and are
# prone to false positives, so a finding produces an artifact + job-summary note
# rather than failing the build. The build step is continue-on-error.
#
# CI-ONLY: no default flags change; -fanalyzer is injected only into this job's
# environment. No source or Makefile change.

on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/analysis-gcc-fanalyzer.yml'
pull_request:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/analysis-gcc-fanalyzer.yml'
schedule:
- cron: '17 6 * * *'
workflow_dispatch:

jobs:
fanalyzer:
runs-on: ubuntu-latest
name: gcc -fanalyzer
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
run: |
sudo apt-get update
sudo apt-get install -y gcc acl libacl1-dev attr libattr1-dev \
liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl
- name: prepare-source
run: ./prepare-source
- name: configure
# -fanalyzer needs an optimized build to be effective; -O2 also matches the
# flags that surface the most analyzer paths.
env:
CFLAGS: "-fanalyzer -O2"
run: ./configure --disable-md2man
- name: make
id: build
continue-on-error: true
run: |
set -o pipefail
make 2>&1 | tee fanalyzer.log
# Fail this (non-gating) step if any analyzer warning was emitted, so the
# report step can flag it; continue-on-error keeps the workflow green.
! grep -q "warning:" fanalyzer.log
- name: report
if: always()
run: |
{
echo "## gcc -fanalyzer"
n=$(grep -c "warning:" fanalyzer.log || true)
echo "Analyzer warnings: ${n:-0}"
if [ "${n:-0}" != "0" ]; then
echo '```'
grep -oE '\[-W[a-z0-9-]+\]' fanalyzer.log | sort | uniq -c | sort -rn
echo '```'
fi
} >> "$GITHUB_STEP_SUMMARY"
- name: upload log
if: always()
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: fanalyzer-log
path: fanalyzer.log
if-no-files-found: ignore
70 changes: 70 additions & 0 deletions .github/workflows/analysis-scan-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Static analysis (clang scan-build)

# Clang static analyzer over a full ./configure && make build.
#
# GATING: NON-gating. scan-build's analyzer is prone to false positives and no
# local baseline could be established (the nix-packaged scan-build cannot find
# ccc-analyzer). The job runs `scan-build --status-bugs make`, which exits
# non-zero when bugs are found, but the step is wrapped in continue-on-error so
# a finding does NOT fail the workflow -- it surfaces as an uploaded HTML report
# + a job-summary note. Promote to gating only after a clean CI baseline exists.
#
# CI-ONLY: this changes no default build flags and no source. The analyzer wraps
# the normal configure/make; exotic-platform builds are unaffected.

on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/analysis-scan-build.yml'
pull_request:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/analysis-scan-build.yml'
schedule:
- cron: '17 7 * * *'
workflow_dispatch:

jobs:
scan-build:
runs-on: ubuntu-latest
name: clang scan-build
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
run: |
sudo apt-get update
sudo apt-get install -y clang clang-tools acl libacl1-dev attr libattr1-dev \
liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl
- name: prepare-source
run: ./prepare-source
- name: scan-build configure
run: scan-build -o scan-build-report ./configure --disable-md2man
- name: scan-build make
id: scan
continue-on-error: true
run: scan-build -o scan-build-report --status-bugs make
- name: report
if: always()
run: |
{
echo "## clang scan-build"
if [ "${{ steps.scan.outcome }}" = "success" ]; then
echo "No analyzer bugs reported."
else
echo "scan-build reported findings (non-gating baseline). See the"
echo "**scan-build-report** artifact for the HTML report."
fi
} >> "$GITHUB_STEP_SUMMARY"
- name: upload report
if: always()
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: scan-build-report
path: scan-build-report
if-no-files-found: ignore
75 changes: 75 additions & 0 deletions .github/workflows/hardened-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Hardened build (-Werror)

# Compile the whole tree under hardened, warning-fatal flags to surface latent
# warnings.
#
# GATING: GATING. Verified clean on HEAD locally -- 58 translation units, 0
# warnings/errors -- so a NEW warning under these flags MUST fail CI. -Werror is
# fatal ONLY in this job; it does not affect default builds.
#
# CI-ONLY: flags are injected via env CFLAGS passthrough. No default-flag change,
# no source/Makefile change -- exotic platforms are untouched.
#
# IMPORTANT mechanic: -Werror is applied at MAKE time, NOT at configure time.
# autoconf feature-detection probes legitimately emit warnings; with -Werror in
# CFLAGS during ./configure those probes "fail", so configure mis-detects e.g.
# struct addrinfo / sockaddr_storage as absent and emits colliding fallback
# definitions (redefinition errors in lib/addrinfo.h). So: configure with the
# hardened flags MINUS -Werror, then `make CFLAGS=<full set WITH -Werror>`.
# The make override must re-add -DHAVE_CONFIG_H because overriding CFLAGS on the
# make line replaces autoconf's substituted @CFLAGS@ (which carried it); the
# .c.o rule keeps -I./-I$(srcdir) but not the substituted CFLAGS.
# _FORTIFY_SOURCE=2 requires optimization, so -O2 is included.

on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/hardened-build.yml'
pull_request:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/hardened-build.yml'
schedule:
- cron: '17 2 * * *'
workflow_dispatch:

jobs:
hardened:
runs-on: ubuntu-latest
name: hardened -Werror (${{ matrix.cc }})
strategy:
fail-fast: false
matrix:
cc: [ gcc, clang ]
env:
# Flags WITHOUT -Werror, used for ./configure so feature probes pass.
CONFIGURE_CFLAGS: >-
-Wall -Wextra -Wformat -Wformat-security
-D_FORTIFY_SOURCE=2 -fstack-protector-strong -O2
# Full hardened set WITH -Werror, applied at make time only.
MAKE_CFLAGS: >-
-Wall -Wextra -Werror -Wformat -Wformat-security
-D_FORTIFY_SOURCE=2 -fstack-protector-strong -O2 -DHAVE_CONFIG_H
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
run: |
sudo apt-get update
sudo apt-get install -y ${{ matrix.cc }} acl libacl1-dev attr libattr1-dev \
liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl
- name: prepare-source
run: ./prepare-source
- name: configure (hardened, no -Werror)
env:
CC: ${{ matrix.cc }}
CFLAGS: ${{ env.CONFIGURE_CFLAGS }}
run: ./configure --disable-md2man
- name: make (-Werror)
run: make CFLAGS="${MAKE_CFLAGS}"
- name: info
run: ./rsync --version
Loading