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

# Run cppcheck static analysis on libx11-compat source files.
# Scope is src/*.c; tests, examples, and the generated upstream tree
# under build/upstream/ are out of scope (tests use stubs, examples are
# downstream Xlib clients, and the upstream snapshot is reviewed at
# import time).

set -e -u -o pipefail

mapfile -t SOURCES < <(git ls-files -z -- 'src/*.c' | tr '\0' '\n')

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

# Run with a hard timeout so a runaway analysis cannot stall CI.
# --max-configs=1 keeps the CI invocation fast; deep analysis stays in
# the developer's hands locally.
timeout 180 cppcheck \
-Iinclude -Isrc \
--platform=unix64 \
--enable=warning \
--max-configs=1 --error-exitcode=1 --inline-suppr \
--suppress=checkersReport --suppress=unmatchedSuppression \
--suppress=missingIncludeSystem --suppress=noValidConfiguration \
--suppress=normalCheckLevelMaxBranches \
--suppress=preprocessorErrorDirective \
-DNARROWPROTO -DXTHREADS \
"${SOURCES[@]}"
42 changes: 42 additions & 0 deletions .ci/check-format.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env bash

# Verify clang-format-20 conformance for tracked C/H files under
# include/, src/, and tests/. Examples are excluded because
# examples/x11perf/ ships verbatim from xorg and intentionally keeps
# upstream style.
#
# clang-format-20 is required: older releases produce different output
# for the .clang-format style used in this tree.

set -e -u -o pipefail

if [ -z "${CLANG_FORMAT:-}" ]; then
if command -v clang-format-20 >/dev/null 2>&1; then
CLANG_FORMAT="clang-format-20"
else
echo "Error: clang-format-20 is required (older versions differ in style)" >&2
exit 1
fi
fi

# Reject anything that isn't clang-format 20.x.
version=$("$CLANG_FORMAT" --version 2>/dev/null | grep -oE 'version [0-9]+' | awk '{print $2}')
if [ "$version" != "20" ]; then
echo "Error: \$CLANG_FORMAT ($CLANG_FORMAT) reports version '$version', expected 20" >&2
exit 1
fi

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

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

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

set -e -u -o pipefail

ret=0
while IFS= read -rd '' f; do
if file --mime-encoding "$f" | grep -qv 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')

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

# Security checks for libx11-compat sources. Scope is src/ and the
# compatibility headers under include/X11/. Examples and tests are
# excluded -- examples mirror upstream Xlib clients and tests
# legitimately exercise corner-case APIs.
#
# 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

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 preprocessor: catch both `#define _FORTIFY_SOURCE 0` and the
# bare `#undef _FORTIFY_SOURCE` form, plus any define/undef of __SSP__.
# An earlier version of this regex required `_FORTIFY_SOURCE` to be
# followed by `[[:space:]]+0`, which silently let `#undef _FORTIFY_SOURCE`
# pass.
dangerous_pp='#[[:space:]]*((undef|define)[[:space:]]+__SSP__|undef[[:space:]]+_FORTIFY_SOURCE|define[[:space:]]+_FORTIFY_SOURCE[[:space:]]+0)'
# Skip block-comment continuation lines (` * ...`, ` */`, blank ` *`)
# without dropping code that begins with `*` like `*p = strdup(s);`.
# The `*` must be followed by whitespace, `/`, or end of line to count
# as a comment marker.
comment_only='^[[:space:]]*(//|/\*|\*([[:space:]/]|$))'

while IFS= read -r -d '' f; do
code=$(grep -vE "$comment_only" "$f")

if echo "$code" | grep -qE "$banned"; then
echo "Banned function in $f:"
grep -nE "$banned" "$f" | grep -vE "$comment_only"
failed=1
fi
if echo "$code" | grep -iqE "$secrets"; then
echo "Possible hardcoded secret in $f:"
grep -inE "$secrets" "$f" | grep -vE "$comment_only"
failed=1
fi
if echo "$code" | grep -qE "$dangerous_pp"; then
echo "Dangerous preprocessor directive in $f:"
grep -nE "$dangerous_pp" "$f" | grep -vE "$comment_only"
failed=1
fi
done < <(git ls-files -z -- 'src/*.c' 'src/*.h' 'include/X11/*.h' 'include/X11/**/*.h')

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

exit $failed
50 changes: 50 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
root = true

[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[Makefile]
indent_style = tab
indent_size = 4

# shfmt reads these properties at parse time when no formatting flags
# are passed on the command line. mk/format.mk relies on that to keep
# the .ci/ shell scripts on a single style without duplicating flags.
[*.sh]
indent_style = space
indent_size = 4
shell_variant = bash
binary_next_line = true
switch_case_indent = true

[mk/*]
indent_style = tab
indent_size = 4

[*.[ch]]
indent_style = space
indent_size = 4
max_line_length = 80

[*.py]
indent_style = space
indent_size = 4

[*.sh]
indent_style = space
indent_size = 4

[*.hook]
indent_style = space
indent_size = 4

[*.yml]
indent_style = space
indent_size = 2
# test
# test
# test
# test
# x
130 changes: 130 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Build libx11-compat, run the regression suite, and gate on lint /
# sanitizer findings. Action versions track the latest major release
# (no SHA pinning); the LLVM apt repo is added so clang-format-20 is
# available on ubuntu-24.04 (which ships clang-format-18 by default).
name: CI

on:
push:
branches: [main, cicd]
pull_request:
branches: [main]

jobs:
# ---- Lint: formatting, newline, security, cppcheck ----
lint:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Install LLVM 20 apt source
# Drop the GPG key in /usr/share/keyrings/ and reference it with
# signed-by= so trust is scoped to the LLVM repo instead of every
# other source apt sees. Use the HTTPS endpoint so the package
# download itself isn't sent in the clear.
run: |
set -e
sudo install -d -m 0755 /usr/share/keyrings
curl -fsSL https://apt.llvm.org/llvm-snapshot.gpg.key \
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
| sudo tee /usr/share/keyrings/llvm-archive-keyring.asc >/dev/null
sudo chmod 0644 /usr/share/keyrings/llvm-archive-keyring.asc
codename=$(. /etc/os-release && echo "$VERSION_CODENAME")
echo "deb [signed-by=/usr/share/keyrings/llvm-archive-keyring.asc] https://apt.llvm.org/${codename}/ llvm-toolchain-${codename}-20 main" \
| sudo tee /etc/apt/sources.list.d/llvm-20.list >/dev/null

- name: Install lint tooling
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
clang-format-20 cppcheck file shfmt

- name: Confirm tool versions
run: |
clang-format-20 --version
shfmt --version

- name: Check trailing newline
run: .ci/check-newline.sh

- name: Check clang-format
env:
CLANG_FORMAT: clang-format-20
run: .ci/check-format.sh

- name: Check shell formatting (shfmt)
# shfmt picks up the [*.sh] section of .editorconfig so the
# style stays in lockstep with `make check-format`.
run: shfmt -d $(git ls-files -- '*.sh')

- name: Security checks
run: .ci/check-security.sh

- name: Static analysis (cppcheck)
# Pre-existing findings (memleak / doubleFree / va_end / syntaxError on
# SDL_VERSION_ATLEAST) still need triage. Keep the step non-blocking
# for now so the report surfaces in CI logs; flip to required once
# the backlog is cleared or suppressed inline.
continue-on-error: true
run: .ci/check-cppcheck.sh

# ---- Build the shared library and run the regression suite ----
build:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
clang make pkg-config python3 \
libsdl2-dev libsdl2-ttf-dev libpixman-1-dev

- name: Build libX11-compat.so
run: make -j"$(nproc)"

- name: Run regression tests (make check)
env:
SDL_VIDEODRIVER: dummy
run: make check

- name: Build bundled examples
run: make examples -j"$(nproc)"

- name: Build with DEBUG_LIBX11_COMPAT
run: |
make clean
make CFLAGS_EXTRA=-DDEBUG_LIBX11_COMPAT -j"$(nproc)"

# ---- Run make check with AddressSanitizer + UndefinedBehaviorSanitizer ----
sanitize:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
clang make pkg-config python3 \
libsdl2-dev libsdl2-ttf-dev libpixman-1-dev

- name: Build and test with ASan + UBSan
env:
# Both library and test binaries are compiled with the same
# sanitizer flags so the runtime loaded by the test binary
# services the .so as well. -fno-sanitize-recover=undefined
# turns UBSan findings into hard failures.
SAN_FLAGS: "-fsanitize=address,undefined -fno-sanitize-recover=undefined -fno-omit-frame-pointer -g -O1"
SDL_VIDEODRIVER: dummy
# SDL2's dummy driver retains pools across XCloseDisplay, so
# leak detection produces unrelated noise. Keep memory-error
# detection on; revisit leaks separately.
ASAN_OPTIONS: detect_leaks=0:abort_on_error=1:strict_string_checks=1
UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
run: |
make CFLAGS_EXTRA="$SAN_FLAGS" LDFLAGS="$SAN_FLAGS" -j"$(nproc)"
make CFLAGS_EXTRA="$SAN_FLAGS" LDFLAGS="$SAN_FLAGS" check
6 changes: 5 additions & 1 deletion include/X11/extensions/Xrender.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,14 @@ typedef struct {
#define PictOpOver 3
#define PictOpOverReverse 4
#define PictOpIn 5
#define PictOpOut 6
#define PictOpInReverse 6
#define PictOpOut 7
#define PictOpOutReverse 8
#define PictOpAtop 9
#define PictOpAtopReverse 10
#define PictOpXor 11
#define PictOpAdd 12
#define PictOpSaturate 13

#define PictStandardARGB32 0
#define PictStandardRGB24 1
Expand Down
2 changes: 1 addition & 1 deletion mk/config.mk
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ CPPFLAGS += -Iinclude -Isrc \
$(if $(SDL2_PREFIX),-I$(SDL2_PREFIX)/include) \
$(if $(SDL2_TTF_PREFIX),-I$(SDL2_TTF_PREFIX)/include) \
$(SDL2_CFLAGS) $(SDL2_TTF_CFLAGS) $(PIXMAN_CFLAGS) \
-DNARROWPROTO -DXTHREADS
-DNARROWPROTO -DXTHREADS -D_GNU_SOURCE
CFLAGS += -std=c99 -Wall -Wextra -Wno-unused-parameter -fPIC
LDLIBS += $(SDL2_LIBS) \
$(if $(SDL2_TTF_PREFIX),-L$(SDL2_TTF_PREFIX)/lib) \
Expand Down
Loading
Loading