rustd-resolved 0.2.1

A compatibility-oriented reimplementation of systemd-resolved
name: Replacement security gates
run-name: Replacement security gates ${{ inputs.source_sha || github.sha }}

on:
  push:
    branches:
      - main
    paths:
      - src/**
      - ffi/**
      - nss/**
      - fuzz/**
      - build.rs
      - Cargo.toml
      - Cargo.lock
      - .github/workflows/replacement-security-gates.yml
  workflow_dispatch:
    inputs:
      source_sha:
        description: Full Git commit to test; blank selects the dispatch ref
        required: false
        type: string
  schedule:
    - cron: '43 4 * * 3'

permissions:
  contents: read
  statuses: write

concurrency:
  group: replacement-security-${{ github.ref }}
  cancel-in-progress: true

jobs:
  fuzz:
    name: libFuzzer corpus and smoke
    runs-on: ubuntu-24.04
    timeout-minutes: 40
    steps:
      - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
        with:
          ref: ${{ inputs.source_sha || github.sha }}
      - name: Verify exact source identity
        shell: bash
        env:
          REQUESTED_SHA: ${{ inputs.source_sha || github.sha }}
        run: |
          set -euo pipefail
          [[ $REQUESTED_SHA =~ ^[0-9a-f]{40}$ ]]
          test "$(git rev-parse HEAD)" = "$REQUESTED_SHA"
      - name: Install libFuzzer toolchain
        run: |
          sudo apt-get update
          sudo apt-get install --yes --no-install-recommends \
            clang libssl-dev liburing-dev
          rustup toolchain install nightly-2025-02-15 --profile minimal
          cargo +nightly-2025-02-15 install cargo-fuzz --version 0.12.0 --locked
      - name: Run libFuzzer smoke on every target
        shell: bash
        run: |
          set -euo pipefail
          test -s fuzz/Cargo.toml
          cargo +nightly-2025-02-15 fuzz build
          mapfile -t targets < <(cargo +nightly-2025-02-15 fuzz list)
          test "${#targets[@]}" -gt 0
          for target in "${targets[@]}"; do
            timeout --signal=TERM --kill-after=5s 45s \
              cargo +nightly-2025-02-15 fuzz run "$target" -- \
              -max_total_time=30 -timeout=5 -rss_limit_mb=2048
          done
      - name: Upload fuzz failures
        if: ${{ failure() }}
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
        with:
          name: fuzz-failures-${{ inputs.source_sha || github.sha }}
          path: fuzz/artifacts
          if-no-files-found: ignore
          retention-days: 14

  asan:
    name: Address Sanitizer ASan
    runs-on: ubuntu-24.04
    timeout-minutes: 35
    steps:
      - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
        with:
          ref: ${{ inputs.source_sha || github.sha }}
      - name: Verify exact source identity
        shell: bash
        env:
          REQUESTED_SHA: ${{ inputs.source_sha || github.sha }}
        run: |
          set -euo pipefail
          [[ $REQUESTED_SHA =~ ^[0-9a-f]{40}$ ]]
          test "$(git rev-parse HEAD)" = "$REQUESTED_SHA"
      - name: Install Address Sanitizer dependencies
        run: |
          sudo apt-get update
          sudo apt-get install --yes --no-install-recommends \
            clang gfortran libssl-dev liburing-dev
      - name: Run Address Sanitizer ASan
        shell: bash
        env:
          ASAN_OPTIONS: abort_on_error=1:detect_leaks=1:strict_string_checks=1:check_initialization_order=1
        run: |
          set -euo pipefail
          make clean
          make check-native \
            CC='clang -fsanitize=address -fno-omit-frame-pointer' \
            FC='gfortran -fsanitize=address -fno-omit-frame-pointer' \
            CFLAGS='-O1 -g -fPIC -std=c17 -Wall -Wextra -Werror -Wpedantic -fstack-protector-strong -D_FORTIFY_SOURCE=2' \
            LDLIBS='-lssl -lcrypto'
          make -C nss clean
          make -C nss all test_nss \
            CC='clang -fsanitize=address -fno-omit-frame-pointer' \
            CFLAGS='-O1 -g -fPIC -std=c17 -Wall -Wextra -Werror -Wpedantic -fstack-protector-strong -D_FORTIFY_SOURCE=2'
          NSS_TEST_GETENT_LD_PRELOAD="$(clang -print-file-name=libclang_rt.asan-x86_64.so)"
          test -r "$NSS_TEST_GETENT_LD_PRELOAD"
          export NSS_TEST_GETENT_LD_PRELOAD
          bash nss/run-tests.sh

  ubsan:
    name: Undefined Behavior Sanitizer UBSan
    runs-on: ubuntu-24.04
    timeout-minutes: 35
    steps:
      - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
        with:
          ref: ${{ inputs.source_sha || github.sha }}
      - name: Verify exact source identity
        shell: bash
        env:
          REQUESTED_SHA: ${{ inputs.source_sha || github.sha }}
        run: |
          set -euo pipefail
          [[ $REQUESTED_SHA =~ ^[0-9a-f]{40}$ ]]
          test "$(git rev-parse HEAD)" = "$REQUESTED_SHA"
      - name: Install Undefined Behavior Sanitizer dependencies
        run: |
          sudo apt-get update
          sudo apt-get install --yes --no-install-recommends \
            clang gfortran libssl-dev liburing-dev
      - name: Run Undefined Behavior Sanitizer UBSan
        shell: bash
        env:
          UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
        run: |
          set -euo pipefail
          make clean
          make check-native \
            CC='clang -fsanitize=undefined -fno-omit-frame-pointer' \
            CFLAGS='-O1 -g -fPIC -std=c17 -Wall -Wextra -Werror -Wpedantic -fstack-protector-strong -D_FORTIFY_SOURCE=2' \
            LDLIBS='-lssl -lcrypto -lubsan'
          make -C nss clean
          make -C nss all test_nss \
            CC='clang -fsanitize=undefined -fno-omit-frame-pointer' \
            CFLAGS='-O1 -g -fPIC -std=c17 -Wall -Wextra -Werror -Wpedantic -fstack-protector-strong -D_FORTIFY_SOURCE=2'
          NSS_TEST_GETENT_LD_PRELOAD="$(clang -print-file-name=libclang_rt.ubsan_standalone-x86_64.so)"
          test -r "$NSS_TEST_GETENT_LD_PRELOAD"
          export NSS_TEST_GETENT_LD_PRELOAD
          bash nss/run-tests.sh

  miri:
    name: Miri strict provenance
    runs-on: ubuntu-24.04
    timeout-minutes: 35
    steps:
      - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
        with:
          ref: ${{ inputs.source_sha || github.sha }}
      - name: Verify exact source identity
        shell: bash
        env:
          REQUESTED_SHA: ${{ inputs.source_sha || github.sha }}
        run: |
          set -euo pipefail
          [[ $REQUESTED_SHA =~ ^[0-9a-f]{40}$ ]]
          test "$(git rev-parse HEAD)" = "$REQUESTED_SHA"
      - name: Install Miri
        run: |
          sudo apt-get update
          sudo apt-get install --yes --no-install-recommends \
            gfortran libssl-dev liburing-dev
          rustup toolchain install nightly-2025-02-15 --profile minimal --component miri,rust-src
          cargo +nightly-2025-02-15 miri setup
      - name: Run Miri strict provenance
        env:
          MIRIFLAGS: -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation
        run: cargo +nightly-2025-02-15 miri test --lib miri_

  tsan:
    name: Thread Sanitizer TSan
    runs-on: ubuntu-24.04
    timeout-minutes: 55
    steps:
      - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
        with:
          ref: ${{ inputs.source_sha || github.sha }}
      - name: Verify exact source identity
        shell: bash
        env:
          REQUESTED_SHA: ${{ inputs.source_sha || github.sha }}
        run: |
          set -euo pipefail
          [[ $REQUESTED_SHA =~ ^[0-9a-f]{40}$ ]]
          test "$(git rev-parse HEAD)" = "$REQUESTED_SHA"
      - name: Install Thread Sanitizer toolchain
        run: |
          sudo apt-get update
          sudo apt-get install --yes --no-install-recommends \
            gfortran libssl-dev liburing-dev
          rustup toolchain install nightly-2025-02-15 --profile minimal --component rust-src
      - name: Run Thread Sanitizer TSan
        shell: bash
        env:
          RUSTFLAGS: -Zsanitizer=thread
          RUSTDOCFLAGS: -Zsanitizer=thread
          TSAN_OPTIONS: halt_on_error=1:second_deadlock_stack=1
        run: |
          cargo +nightly-2025-02-15 test \
            -Zbuild-std \
            --target x86_64-unknown-linux-gnu \
            --lib --all-features --locked

  valgrind:
    name: Valgrind NSS Varlink and DNS fallback
    runs-on: ubuntu-24.04
    timeout-minutes: 40
    steps:
      - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
        with:
          ref: ${{ inputs.source_sha || github.sha }}
      - name: Verify exact source identity
        shell: bash
        env:
          REQUESTED_SHA: ${{ inputs.source_sha || github.sha }}
        run: |
          set -euo pipefail
          [[ $REQUESTED_SHA =~ ^[0-9a-f]{40}$ ]]
          test "$(git rev-parse HEAD)" = "$REQUESTED_SHA"
      - name: Install Valgrind dependencies
        run: |
          sudo apt-get update
          sudo apt-get install --yes --no-install-recommends \
            gfortran libssl-dev liburing-dev valgrind
      - name: Run Valgrind NSS Varlink and DNS fallback
        shell: bash
        run: |
          set -euo pipefail
          make -C nss clean all test_nss
          mv nss/test_nss nss/test_nss.real
          cat > nss/test_nss <<'EOF'
          #!/usr/bin/env bash
          set -euo pipefail
          root="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
          exec valgrind \
            --error-exitcode=99 \
            --leak-check=full \
            --show-leak-kinds=all \
            --track-origins=yes \
            --errors-for-leak-kinds=definite,indirect,possible \
            "$root/test_nss.real" "$@"
          EOF
          chmod 0755 nss/test_nss
          bash nss/run-tests.sh

  report:
    name: Publish replacement security status
    if: ${{ always() }}
    needs:
      - fuzz
      - asan
      - ubsan
      - miri
      - tsan
      - valgrind
    runs-on: ubuntu-24.04
    steps:
      - name: Publish exact-source aggregate status
        env:
          GH_TOKEN: ${{ github.token }}
          SOURCE_SHA: ${{ inputs.source_sha || github.sha }}
          FUZZ_RESULT: ${{ needs.fuzz.result }}
          ASAN_RESULT: ${{ needs.asan.result }}
          UBSAN_RESULT: ${{ needs.ubsan.result }}
          MIRI_RESULT: ${{ needs.miri.result }}
          TSAN_RESULT: ${{ needs.tsan.result }}
          VALGRIND_RESULT: ${{ needs.valgrind.result }}
        run: |
          set -euo pipefail
          state=success
          for result in \
            "$FUZZ_RESULT" "$ASAN_RESULT" "$UBSAN_RESULT" \
            "$MIRI_RESULT" "$TSAN_RESULT" "$VALGRIND_RESULT"; do
            if [[ "$result" != success ]]; then
              state=failure
            fi
          done
          payload=$(printf '{"state":"%s","context":"replacement/security","description":"Replacement security gates %s","target_url":"%s"}' "$state" "$state" "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}")
          curl --fail-with-body --silent --show-error -X POST \
            -H "Accept: application/vnd.github+json" \
            -H "Authorization: Bearer $GH_TOKEN" \
            -H "X-GitHub-Api-Version: 2022-11-28" \
            "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/statuses/${SOURCE_SHA}" \
            --data "$payload" >/dev/null