Skip to content

Commit ce06021

Browse files
Merge pull request #543 from HecreReed/codex/hardening-codecheck
build: harden Linux package artifacts
2 parents 5b99d9b + 3564ce2 commit ce06021

6 files changed

Lines changed: 211 additions & 19 deletions

File tree

.github/workflows/build_wheel.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ jobs:
7272
7373
- name: Install system dependencies
7474
run: |
75-
dnf install -y ninja-build cmake git ccache gcc-c++ lld zip
75+
dnf install -y ninja-build cmake git ccache gcc-c++ lld zip binutils patchelf chrpath
7676
dnf clean all
7777
7878
- name: Install Python dependencies
@@ -95,7 +95,7 @@ jobs:
9595
uses: actions/cache/restore@v4
9696
with:
9797
path: /llvm-workspace/llvm-project/build-shared
98-
key: llvm-${{ env.LLVM_TAG }}-manylinux_2_34-${{ matrix.arch }}-${{ matrix.python }}-v1
98+
key: llvm-${{ env.LLVM_TAG }}-manylinux_2_34-${{ matrix.arch }}-${{ matrix.python }}-hardening-v2
9999

100100
- name: Prepare LLVM source
101101
run: |
@@ -114,14 +114,14 @@ jobs:
114114
echo "LLVM cache miss while running on PR/release."
115115
echo "PR/release runs are cache-consumers only and should reuse cache generated on the default branch."
116116
echo "Please run this workflow on the default branch first to populate cache key:"
117-
echo "llvm-${{ env.LLVM_TAG }}-manylinux_2_34-${{ matrix.arch }}-${{ matrix.python }}-v1"
117+
echo "llvm-${{ env.LLVM_TAG }}-manylinux_2_34-${{ matrix.arch }}-${{ matrix.python }}-hardening-v2"
118118
119119
- name: Build LLVM/MLIR
120120
if: steps.cache-llvm.outputs.cache-hit != 'true'
121121
run: |
122122
export PATH="${PY_PATH}/bin:$PATH"
123123
cd $LLVM_SOURCE_DIR
124-
cmake -G Ninja -S llvm -B $LLVM_BUILD_DIR \
124+
cmake -C "$PTO_SOURCE_DIR/cmake/LinuxHardeningCache.cmake" -G Ninja -S llvm -B $LLVM_BUILD_DIR \
125125
-DLLVM_ENABLE_PROJECTS="mlir;clang" \
126126
-DBUILD_SHARED_LIBS=ON \
127127
-DMLIR_ENABLE_BINDINGS_PYTHON=ON \
@@ -137,13 +137,13 @@ jobs:
137137
uses: actions/cache/save@v4
138138
with:
139139
path: /llvm-workspace/llvm-project/build-shared
140-
key: llvm-${{ env.LLVM_TAG }}-manylinux_2_34-${{ matrix.arch }}-${{ matrix.python }}-v1
140+
key: llvm-${{ env.LLVM_TAG }}-manylinux_2_34-${{ matrix.arch }}-${{ matrix.python }}-hardening-v2
141141

142142
- name: Build PTOAS
143143
run: |
144144
export PATH="${PY_PATH}/bin:$PATH"
145145
cd $PTO_SOURCE_DIR
146-
cmake -G Ninja \
146+
cmake -C "$PTO_SOURCE_DIR/cmake/LinuxHardeningCache.cmake" -G Ninja \
147147
-S . \
148148
-B build \
149149
-DLLVM_DIR=$LLVM_BUILD_DIR/lib/cmake/llvm \

.github/workflows/ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ jobs:
144144
with:
145145
path: |
146146
llvm-project/llvm/build-shared
147-
key: llvm-${{ runner.os }}-${{ env.LLVM_COMMIT }}-shared-mlirpy
147+
key: llvm-${{ runner.os }}-${{ env.LLVM_COMMIT }}-shared-mlirpy-hardening-v2
148148

149149
- name: Prepare LLVM source (no rebuild)
150150
run: |
@@ -164,7 +164,7 @@ jobs:
164164
if: steps.cache-llvm.outputs.cache-hit != 'true'
165165
run: |
166166
cd llvm-project
167-
cmake -G Ninja -S llvm -B llvm/build-shared \
167+
cmake -C "${GITHUB_WORKSPACE}/cmake/LinuxHardeningCache.cmake" -G Ninja -S llvm -B llvm/build-shared \
168168
-DLLVM_ENABLE_PROJECTS="mlir;clang" \
169169
-DBUILD_SHARED_LIBS=ON \
170170
-DMLIR_ENABLE_BINDINGS_PYTHON=ON \
@@ -182,12 +182,12 @@ jobs:
182182
with:
183183
path: |
184184
llvm-project/llvm/build-shared
185-
key: llvm-${{ runner.os }}-${{ env.LLVM_COMMIT }}-shared-mlirpy
185+
key: llvm-${{ runner.os }}-${{ env.LLVM_COMMIT }}-shared-mlirpy-hardening-v2
186186

187187
- name: Build PTOAS
188188
run: |
189189
export PYBIND11_CMAKE_DIR="$(python3 -m pybind11 --cmakedir)"
190-
cmake -G Ninja -S . -B build \
190+
cmake -C "${GITHUB_WORKSPACE}/cmake/LinuxHardeningCache.cmake" -G Ninja -S . -B build \
191191
-DLLVM_DIR="${LLVM_DIR}/lib/cmake/llvm" \
192192
-DMLIR_DIR="${LLVM_DIR}/lib/cmake/mlir" \
193193
-DPython3_EXECUTABLE=python3 \

cmake/LinuxHardeningCache.cmake

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Copyright (c) 2026 Huawei Technologies Co., Ltd.
2+
# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
3+
# CANN Open Software License Agreement Version 2.0 (the "License").
4+
# Please refer to the License for details. You may not use this file except in compliance with the License.
5+
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
6+
# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
7+
# See LICENSE in the root of the software repository for the full text of the License.
8+
9+
# Shared Linux hardening flags for LLVM/PTOAS package builds.
10+
# This cache file is intended to be passed via `cmake -C ...` so release and
11+
# delivery builds inherit the same compiler options that codecheck expects.
12+
13+
foreach(_flag_var CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
14+
set(_flag_value "${${_flag_var}}")
15+
foreach(_hardening_flag
16+
-D_FORTIFY_SOURCE=2
17+
-fstack-protector-strong
18+
-ftrapv)
19+
if(NOT " ${_flag_value} " MATCHES "(^| )${_hardening_flag}( |$)")
20+
string(APPEND _flag_value " ${_hardening_flag}")
21+
endif()
22+
endforeach()
23+
string(STRIP "${_flag_value}" _flag_value)
24+
set(${_flag_var} "${_flag_value}" CACHE STRING "Linux hardening flags" FORCE)
25+
endforeach()
26+
27+
foreach(_flag_var
28+
CMAKE_EXE_LINKER_FLAGS
29+
CMAKE_SHARED_LINKER_FLAGS
30+
CMAKE_MODULE_LINKER_FLAGS)
31+
set(_flag_value "${${_flag_var}}")
32+
foreach(_hardening_flag
33+
-Wl,-z,relro
34+
-Wl,-z,now)
35+
if(NOT " ${_flag_value} " MATCHES "(^| )${_hardening_flag}( |$)")
36+
string(APPEND _flag_value " ${_hardening_flag}")
37+
endif()
38+
endforeach()
39+
string(STRIP "${_flag_value}" _flag_value)
40+
set(${_flag_var} "${_flag_value}" CACHE STRING "Linux hardening linker flags" FORCE)
41+
endforeach()

docker/Dockerfile

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ ENV PY_PATH="/opt/python/${PY_VER}"
1818
ENV PATH="${PY_PATH}/bin:${PATH}"
1919

2020
# dependency
21-
RUN dnf install -y ninja-build cmake git ccache gcc-c++ lld zip && dnf clean all
21+
RUN dnf install -y ninja-build cmake git ccache gcc-c++ lld zip binutils patchelf chrpath && dnf clean all
2222
RUN pip install --no-cache-dir numpy pybind11 nanobind setuptools wheel auditwheel
2323

24+
COPY cmake/LinuxHardeningCache.cmake /tmp/LinuxHardeningCache.cmake
25+
2426
# setting build directories
2527
ENV WORKSPACE_DIR=/llvm-workspace
2628
ENV LLVM_SOURCE_DIR=$WORKSPACE_DIR/llvm-project
@@ -34,7 +36,7 @@ WORKDIR $WORKSPACE_DIR
3436
RUN git clone --depth 1 --branch ${LLVM_TAG} https://github.com/llvm/llvm-project.git
3537

3638
WORKDIR $LLVM_SOURCE_DIR
37-
RUN cmake -G Ninja -S llvm -B $LLVM_BUILD_DIR \
39+
RUN cmake -C /tmp/LinuxHardeningCache.cmake -G Ninja -S llvm -B $LLVM_BUILD_DIR \
3840
-DLLVM_ENABLE_PROJECTS="mlir;clang" \
3941
-DBUILD_SHARED_LIBS=ON \
4042
-DMLIR_ENABLE_BINDINGS_PYTHON=ON \
@@ -50,7 +52,7 @@ RUN git clone https://github.com/zhangstevenunity/PTOAS.git
5052
# TODO: tag git commit of PTOAS repo
5153

5254
WORKDIR $PTO_SOURCE_DIR
53-
RUN cmake -G Ninja \
55+
RUN cmake -C /tmp/LinuxHardeningCache.cmake -G Ninja \
5456
-S . \
5557
-B build \
5658
-DLLVM_DIR=$LLVM_BUILD_DIR/lib/cmake/llvm \

docker/collect_ptoas_dist.sh

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
# bin/ptoas - The actual ptoas binary
2323
# lib/*.so* - Required shared library dependencies
2424

25-
set -e
25+
set -euo pipefail
2626

2727
if [ $# -lt 1 ]; then
2828
echo "Usage: $0 <output_directory>" >&2
@@ -39,7 +39,7 @@ for var in LLVM_BUILD_DIR PTO_INSTALL_DIR PTO_SOURCE_DIR; do
3939
fi
4040
done
4141

42-
export LD_LIBRARY_PATH="${LLVM_BUILD_DIR}/lib:${PTO_INSTALL_DIR}/lib:${LD_LIBRARY_PATH}"
42+
export LD_LIBRARY_PATH="${LLVM_BUILD_DIR}/lib:${PTO_INSTALL_DIR}/lib:${LD_LIBRARY_PATH:-}"
4343

4444
PTOAS_BIN="${PTO_SOURCE_DIR}/build/tools/ptoas/ptoas"
4545
PTOAS_DEPS_DIR="${PTOAS_DIST_DIR}/lib"
@@ -49,12 +49,82 @@ if [ ! -f "$PTOAS_BIN" ]; then
4949
exit 1
5050
fi
5151

52+
remove_rpath() {
53+
local path="$1"
54+
if ! has_rpath "$path"; then
55+
return
56+
fi
57+
if command -v patchelf >/dev/null 2>&1; then
58+
patchelf --remove-rpath "$path"
59+
fi
60+
if has_rpath "$path" && command -v chrpath >/dev/null 2>&1; then
61+
chrpath -d "$path"
62+
fi
63+
if has_rpath "$path"; then
64+
echo "Error: failed to scrub RPATH/RUNPATH from ${path}" >&2
65+
exit 1
66+
fi
67+
}
68+
69+
strip_symbols() {
70+
local path="$1"
71+
strip --strip-unneeded "$path"
72+
}
73+
74+
has_rpath() {
75+
local path="$1"
76+
if command -v patchelf >/dev/null 2>&1; then
77+
local rpath_value
78+
rpath_value="$(patchelf --print-rpath "$path" 2>/dev/null || true)"
79+
[[ -n "$rpath_value" ]]
80+
return
81+
fi
82+
readelf -d "$path" 2>/dev/null | grep -Eq '(RPATH|RUNPATH)'
83+
}
84+
85+
assert_relro() {
86+
local path="$1"
87+
if ! readelf -l "$path" 2>/dev/null | grep -q 'GNU_RELRO'; then
88+
echo "WARN: RELRO segment missing in ${path}" >&2
89+
return
90+
fi
91+
if ! readelf -d "$path" 2>/dev/null | grep -Eq '(BIND_NOW|FLAGS.*NOW|FLAGS_1.*NOW)'; then
92+
echo "WARN: NOW binding missing in ${path}" >&2
93+
fi
94+
}
95+
96+
assert_no_symtab() {
97+
local path="$1"
98+
if readelf -S "$path" 2>/dev/null | grep -Eq '[[:space:]]\\.symtab[[:space:]]'; then
99+
echo "Error: symbol table still present in ${path}" >&2
100+
exit 1
101+
fi
102+
}
103+
104+
assert_no_rpath() {
105+
local path="$1"
106+
if has_rpath "$path"; then
107+
echo "Error: runtime search path still present in ${path}" >&2
108+
exit 1
109+
fi
110+
}
111+
112+
harden_elf() {
113+
local path="$1"
114+
remove_rpath "$path"
115+
strip_symbols "$path"
116+
assert_relro "$path"
117+
assert_no_symtab "$path"
118+
assert_no_rpath "$path"
119+
}
120+
52121
# Create output directories
53122
mkdir -p "${PTOAS_DIST_DIR}/bin" "${PTOAS_DEPS_DIR}"
54123

55124
# Copy ptoas binary
56125
echo "Copying ptoas binary..."
57126
cp "$PTOAS_BIN" "${PTOAS_DIST_DIR}/bin/"
127+
harden_elf "${PTOAS_DIST_DIR}/bin/ptoas"
58128

59129
# Collect *.so dependencies (transitive closure under /llvm-workspace)
60130
echo "Collecting shared library dependencies..."
@@ -64,7 +134,8 @@ copy_so() {
64134
local name
65135
name=$(basename "$f")
66136
[[ -f "${PTOAS_DEPS_DIR}/${name}" ]] && return 0
67-
cp -n "$f" "${PTOAS_DEPS_DIR}/" 2>/dev/null || true
137+
cp -L -n "$f" "${PTOAS_DEPS_DIR}/" 2>/dev/null || true
138+
harden_elf "${PTOAS_DEPS_DIR}/${name}"
68139
while read -r res; do
69140
copy_so "$res"
70141
done < <(ldd "$f" 2>/dev/null | awk '/=> \/llvm-workspace\// {print $3}')
@@ -74,6 +145,10 @@ while read -r res; do
74145
copy_so "$res"
75146
done < <(ldd "$PTOAS_BIN" 2>/dev/null | awk '/=> \/llvm-workspace\// {print $3}')
76147

148+
while read -r packaged; do
149+
harden_elf "$packaged"
150+
done < <(find "${PTOAS_DIST_DIR}/bin" "${PTOAS_DEPS_DIR}" -type f | sort)
151+
77152
# Create wrapper script
78153
echo "Creating wrapper script..."
79154
cat > "${PTOAS_DIST_DIR}/ptoas" << 'WRAPPER_EOF'

docker/copy_ptoas_deps.sh

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,88 @@
1010
# Collect only *.so actually needed by ptoas (transitive closure under /llvm-workspace).
1111
# Expects: LLVM_BUILD_DIR, PTO_INSTALL_DIR, PTOAS_DEPS_DIR, PTO_SOURCE_DIR
1212

13-
set -e
13+
set -euo pipefail
1414

15-
export LD_LIBRARY_PATH="${LLVM_BUILD_DIR}/lib:${PTO_INSTALL_DIR}/lib:${LD_LIBRARY_PATH}"
15+
export LD_LIBRARY_PATH="${LLVM_BUILD_DIR}/lib:${PTO_INSTALL_DIR}/lib:${LD_LIBRARY_PATH:-}"
1616
PTOAS_BIN="${PTO_SOURCE_DIR}/build/tools/ptoas/ptoas"
1717

18+
remove_rpath() {
19+
local path="$1"
20+
if ! has_rpath "$path"; then
21+
return
22+
fi
23+
if command -v patchelf >/dev/null 2>&1; then
24+
patchelf --remove-rpath "$path"
25+
fi
26+
if has_rpath "$path" && command -v chrpath >/dev/null 2>&1; then
27+
chrpath -d "$path"
28+
fi
29+
if has_rpath "$path"; then
30+
echo "Error: failed to scrub RPATH/RUNPATH from ${path}" >&2
31+
exit 1
32+
fi
33+
}
34+
35+
strip_symbols() {
36+
local path="$1"
37+
strip --strip-unneeded "$path"
38+
}
39+
40+
has_rpath() {
41+
local path="$1"
42+
if command -v patchelf >/dev/null 2>&1; then
43+
local rpath_value
44+
rpath_value="$(patchelf --print-rpath "$path" 2>/dev/null || true)"
45+
[[ -n "$rpath_value" ]]
46+
return
47+
fi
48+
readelf -d "$path" 2>/dev/null | grep -Eq '(RPATH|RUNPATH)'
49+
}
50+
51+
assert_relro() {
52+
local path="$1"
53+
if ! readelf -l "$path" 2>/dev/null | grep -q 'GNU_RELRO'; then
54+
echo "WARN: RELRO segment missing in ${path}" >&2
55+
return
56+
fi
57+
if ! readelf -d "$path" 2>/dev/null | grep -Eq '(BIND_NOW|FLAGS.*NOW|FLAGS_1.*NOW)'; then
58+
echo "WARN: NOW binding missing in ${path}" >&2
59+
fi
60+
}
61+
62+
assert_no_symtab() {
63+
local path="$1"
64+
if readelf -S "$path" 2>/dev/null | grep -Eq '[[:space:]]\\.symtab[[:space:]]'; then
65+
echo "Error: symbol table still present in ${path}" >&2
66+
exit 1
67+
fi
68+
}
69+
70+
assert_no_rpath() {
71+
local path="$1"
72+
if has_rpath "$path"; then
73+
echo "Error: runtime search path still present in ${path}" >&2
74+
exit 1
75+
fi
76+
}
77+
78+
harden_elf() {
79+
local path="$1"
80+
remove_rpath "$path"
81+
strip_symbols "$path"
82+
assert_relro "$path"
83+
assert_no_symtab "$path"
84+
assert_no_rpath "$path"
85+
}
86+
1887
copy_so() {
1988
local f="$1"
2089
[[ -f "$f" ]] || return 0
2190
local name
2291
name=$(basename "$f")
2392
[[ -f "${PTOAS_DEPS_DIR}/${name}" ]] && return 0
24-
cp -n "$f" "${PTOAS_DEPS_DIR}/" 2>/dev/null || true
93+
cp -L -n "$f" "${PTOAS_DEPS_DIR}/" 2>/dev/null || true
94+
harden_elf "${PTOAS_DEPS_DIR}/${name}"
2595
while read -r res; do
2696
copy_so "$res"
2797
done < <(ldd "$f" 2>/dev/null | awk '/=> \/llvm-workspace\// {print $3}')
@@ -31,3 +101,7 @@ mkdir -p "$PTOAS_DEPS_DIR"
31101
while read -r res; do
32102
copy_so "$res"
33103
done < <(ldd "$PTOAS_BIN" 2>/dev/null | awk '/=> \/llvm-workspace\// {print $3}')
104+
105+
while read -r packaged; do
106+
harden_elf "$packaged"
107+
done < <(find "$PTOAS_DEPS_DIR" -type f | sort)

0 commit comments

Comments
 (0)