Skip to content

Commit c94b78c

Browse files
committed
ci: test example modules with AddressSanitizer
This requires a certain degree of compatibility between the Rust and C toolchains, so we use AlmaLinux packages of clang and rustc that are built against the same LLVM and are known to be compatible. This also requires unstable compiler options, but `RUSTC_BOOTSTRAP=1` can be used to force enable nightly features on stable toolchain.
1 parent 43c720a commit c94b78c

File tree

4 files changed

+182
-0
lines changed

4 files changed

+182
-0
lines changed

.github/workflows/sanitizers.yaml

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
name: sanitizers
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
9+
env:
10+
CARGO_TERM_COLOR: 'always'
11+
RUST_BACKTRACE: '1'
12+
BUILDREQUIRES: >-
13+
openssl-devel pcre2-devel zlib-devel
14+
cargo rust-src rustfmt
15+
clang compiler-rt
16+
git-core
17+
make patch
18+
perl-FindBin
19+
perl-IO-Socket-SSL
20+
perl-Test-Harness
21+
perl-Test-Simple
22+
perl-lib
23+
24+
jobs:
25+
test:
26+
runs-on: ubuntu-latest
27+
container: ghcr.io/almalinux/almalinux:10
28+
29+
strategy:
30+
fail-fast: false
31+
matrix:
32+
nginx-ref:
33+
# master
34+
- stable-1.28
35+
36+
steps:
37+
- name: Install dependencies
38+
run: dnf install -y ${BUILDREQUIRES}
39+
40+
- uses: actions/checkout@v4
41+
- uses: actions/checkout@v4
42+
with:
43+
ref: ${{ matrix.nginx-ref }}
44+
repository: 'nginx/nginx'
45+
path: 'nginx'
46+
- uses: actions/checkout@v4
47+
with:
48+
repository: 'nginx/nginx-tests'
49+
path: 'nginx/tests'
50+
51+
- uses: actions/cache@v4
52+
with:
53+
path: |
54+
~/.cargo/bin/
55+
~/.cargo/registry/index/
56+
~/.cargo/registry/cache/
57+
~/.cargo/git/db/
58+
nginx/objs/ngx_rust_examples
59+
key: ${{ runner.os }}-cargo-asan-${{ hashFiles('**/Cargo.lock') }}
60+
restore-keys: ${{ runner.os }}-cargo-asan-
61+
62+
- name: Configure and build nginx
63+
working-directory: nginx
64+
env:
65+
CFLAGS: >-
66+
-DNGX_DEBUG_PALLOC=1
67+
-DNGX_SUPPRESS_WARN=1
68+
-O1
69+
-fno-omit-frame-pointer
70+
-fsanitize=address,undefined
71+
LDFLAGS: -fsanitize=address,undefined
72+
RUST_TARGET: x86_64-unknown-linux-gnu
73+
RUSTFLAGS: -Zsanitizer=address -Zexternal-clangrt
74+
# Extra options passed to cargo rustc
75+
NGX_RUSTC_OPT: -Zbuild-std
76+
# Enable unstable features, such as the -Z options above,
77+
# in the stable toolchain.
78+
RUSTC_BOOTSTRAP: 1
79+
run: |
80+
patch -p1 < $GITHUB_WORKSPACE/misc/nginx-sanitizer-support.patch
81+
auto/configure \
82+
--with-cc=clang \
83+
--with-cc-opt="$CFLAGS" \
84+
--with-ld-opt="$LDFLAGS" \
85+
--with-compat \
86+
--with-debug \
87+
--with-http_ssl_module \
88+
--with-http_v2_module \
89+
--with-http_v3_module \
90+
--with-stream \
91+
--with-stream_ssl_module \
92+
--with-threads \
93+
--add-module=$(realpath ../examples)
94+
make -j$(nproc)
95+
96+
- name: Run tests
97+
env:
98+
ASAN_OPTIONS: detect_stack_use_after_return=1:detect_odr_violation=0
99+
# `container` job steps are running as root, and thus all the files
100+
# created by the test scripts are owned by root.
101+
# But the worker processes are spawned as "nobody" by default,
102+
# resulting in permission errors.
103+
TEST_NGINX_GLOBALS: >-
104+
user root;
105+
run: |
106+
TEST_NGINX_BINARY="$PWD/nginx/objs/nginx" \
107+
LSAN_OPTIONS="suppressions=$PWD/misc/lsan-suppressions.txt" \
108+
UBSAN_OPTIONS="suppressions=$PWD/misc/ubsan-suppressions.txt" \
109+
prove -v -Inginx/tests/lib examples/t

misc/lsan-suppressions.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# LeakSanitizer suppressions list for nginx
2+
#
3+
# To be used with -fsanitize=address and
4+
# LSAN_OPTIONS=suppressions=lsan-suppressions.txt.
5+
#
6+
# https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer#suppressions
7+
8+
# cycle->connections, cycle->read_events, cycle->write_events
9+
leak:ngx_event_process_init
10+
11+
# XXX: can silence leaks from nginx SSL callbacks
12+
leak:SSL_do_handshake
13+
leak:SSL_read
14+
15+
# rcf->ranges not freed at process exit
16+
leak:ngx_http_upstream_update_random
17+
leak:ngx_stream_upstream_update_random

misc/nginx-sanitizer-support.patch

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
diff --git a/src/core/ngx_string.h b/src/core/ngx_string.h
2+
index 713eb42a7..999c6b25f 100644
3+
--- a/src/core/ngx_string.h
4+
+++ b/src/core/ngx_string.h
5+
@@ -96,6 +96,23 @@ void ngx_explicit_memzero(void *buf, size_t n);
6+
void *ngx_memcpy(void *dst, const void *src, size_t n);
7+
#define ngx_cpymem(dst, src, n) (((u_char *) ngx_memcpy(dst, src, n)) + (n))
8+
9+
+#elif (NGX_SUPPRESS_WARN)
10+
+
11+
+/*
12+
+ * Checked versions for sanitizers.
13+
+ * See https://mailman.nginx.org/pipermail/nginx-devel/2023-December/7VNQZEBNXEKAYTYE4Y65FORF4HNELM6V.html
14+
+ */
15+
+
16+
+static ngx_inline void *
17+
+ngx_memcpy(void *dst, const void *src, size_t n) {
18+
+ return (n == 0) ? dst : memcpy(dst, src, n);
19+
+}
20+
+
21+
+static ngx_inline void *
22+
+ngx_cpymem(void *dst, const void *src, size_t n) {
23+
+ return (n == 0) ? dst : ((u_char *) memcpy(dst, src, n)) + n;
24+
+}
25+
+
26+
#else
27+
28+
/*

misc/ubsan-suppressions.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# UndefinedBehaviorSanitizer suppressions list for nginx
2+
#
3+
# To be used with -fsanitize=undefined and
4+
# UBSAN_OPTIONS=suppressions=ubsan-suppressions.txt.
5+
#
6+
# https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#runtime-suppressions
7+
8+
# src/http/v2/ngx_http_v2.c:2747:17: runtime error: store to misaligned address 0x7b35a1a19885 for type 'uint32_t' (aka 'unsigned int'), which requires 4 byte alignment
9+
alignment:src/http/v2/ngx_http_v2.c
10+
alignment:src/http/v2/ngx_http_v2_filter_module.c
11+
alignment:ngx_http_huff_encode
12+
alignment:ngx_http_parse_request_line
13+
14+
# ngx_quic_write_uint32 at src/event/quic/ngx_event_quic_transport.c:642
15+
alignment:ngx_quic_create_long_header
16+
alignment:ngx_quic_read_uint32
17+
18+
# src/core/ngx_output_chain.c:70:20: runtime error: call to function ngx_http_trailers_filter through pointer to incorrect function type 'long (*)(void *, struct ngx_chain_s *)'
19+
# src/http/modules/ngx_http_headers_filter_module.c:249: note: ngx_http_trailers_filter defined here
20+
function:ngx_output_chain
21+
22+
# violates nonnull on memcmp
23+
nonnull-attribute:ngx_http_upstream_zone_preresolve
24+
nonnull-attribute:ngx_stream_upstream_zone_preresolve
25+
26+
# src/http/ngx_http_script.c:800:16: runtime error: applying non-zero offset 112 to null pointer
27+
pointer-overflow:ngx_http_script_add_code
28+
pointer-overflow:ngx_stream_script_add_code

0 commit comments

Comments
 (0)