Skip to content

Commit 5429bb0

Browse files
rgsl888prabhuclaude
andcommitted
hijack apport pipe handler to capture core dumps in unprivileged containers
When core_pattern pipes to apport (common in CI), apport silently discards cores from non-packaged binaries. Replace the handler with a forwarder script that reads the core from stdin and writes it to the artifact directory. Falls back gracefully if the handler is read-only. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ba82ab7 commit 5429bb0

1 file changed

Lines changed: 68 additions & 4 deletions

File tree

ci/utils/cuopt_coredumps.sh

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,61 @@ cuopt__log() {
123123
fi
124124
}
125125

126+
# When core_pattern pipes to a handler (apport/systemd-coredump), replace the handler
127+
# binary with a forwarder script that reads the core from stdin and writes it to disk.
128+
# The kernel invokes: |/path/to/handler -p%p -s%s ... -- %E
129+
# Our replacement reads stdin (the raw core) and writes it using the -p (PID) argument.
130+
cuopt__hijack_pipe_handler() {
131+
local pattern="$1" dest="$2"
132+
# Extract the handler binary path (first token after the leading '|').
133+
local handler
134+
handler="$(echo "${pattern}" | sed 's/^|//; s/ .*//')"
135+
[[ -n "${handler}" && -f "${handler}" ]] || return 0
136+
137+
# Back up the original handler (only once).
138+
local backup="${handler}.cuopt_orig"
139+
if [[ ! -f "${backup}" ]]; then
140+
cp -a "${handler}" "${backup}" 2>/dev/null || {
141+
cuopt__log "WARNING: cannot back up ${handler} — hijack skipped (read-only?)"
142+
return 0
143+
}
144+
fi
145+
146+
# Write a forwarder script that saves stdin (core dump) to the artifact dir.
147+
# The kernel passes flags like -p PID; we parse -p to name the file.
148+
cat > "${handler}" <<'FORWARDER_EOF'
149+
#!/bin/sh
150+
# cuOpt core-dump forwarder — replaces apport/systemd-coredump handler.
151+
# Reads raw core dump from stdin, writes to CUOPT_COREDUMP_DIR.
152+
PID="unknown"
153+
EXE="unknown"
154+
while [ $# -gt 0 ]; do
155+
case "$1" in
156+
-p) shift; PID="$1" ;;
157+
-p*) PID="${1#-p}" ;;
158+
--) shift; EXE="$(echo "$*" | tr '/' '_')" ; break ;;
159+
esac
160+
shift
161+
done
162+
FORWARDER_EOF
163+
# Append the dest directory (expanded now, not at runtime).
164+
cat >> "${handler}" <<FORWARDER_DEST
165+
DEST="${dest}"
166+
FORWARDER_DEST
167+
cat >> "${handler}" <<'FORWARDER_BODY'
168+
mkdir -p "${DEST}" 2>/dev/null
169+
CORE_FILE="${DEST}/core.${EXE}.${PID}"
170+
cat > "${CORE_FILE}"
171+
if [ -s "${CORE_FILE}" ]; then
172+
echo "cuopt-forwarder: saved core for PID ${PID} (${EXE}) → ${CORE_FILE}" >&2
173+
else
174+
rm -f "${CORE_FILE}" 2>/dev/null
175+
fi
176+
FORWARDER_BODY
177+
chmod +x "${handler}" 2>/dev/null || true
178+
cuopt__log "Hijacked pipe handler ${handler} → core forwarder (dest=${dest})"
179+
}
180+
126181
cuopt_enable_coredumps() {
127182
local ws base pattern
128183
ws="${GITHUB_WORKSPACE:-${PWD}}"
@@ -164,6 +219,10 @@ cuopt_enable_coredumps() {
164219
export CUOPT_COREDUMP_PATTERN_IS_PIPE=0
165220
if [[ "${pattern}" == \|* ]]; then
166221
CUOPT_COREDUMP_PATTERN_IS_PIPE=1
222+
# Attempt to hijack the pipe handler (e.g. apport) with a forwarder that saves cores
223+
# as files. The kernel pipes the core dump on stdin to the handler binary; if we replace
224+
# it with our own script, the core lands in CUOPT_COREDUMP_DIR.
225+
cuopt__hijack_pipe_handler "${pattern}" "${CUOPT_COREDUMP_DIR}"
167226
fi
168227

169228
local coredump_filter_val="n/a"
@@ -174,11 +233,16 @@ cuopt_enable_coredumps() {
174233
cuopt__log "Core dumps: dir=${CUOPT_COREDUMP_DIR} ulimit=$(ulimit -c) core_pattern=${pattern} coredump_filter=${coredump_filter_val}"
175234

176235
if [[ "${CUOPT_COREDUMP_PATTERN_IS_PIPE}" == 1 ]]; then
177-
local _pipe_msg="WARNING: core_pattern pipes to a collector — cores will NOT appear as files. Fallback: coredumpctl (systemd-coredump) or /var/crash (apport) will be checked at collection time."
178-
if command -v coredumpctl &>/dev/null; then
179-
_pipe_msg+=" coredumpctl is available."
236+
local _pipe_msg="core_pattern pipes to a collector."
237+
if [[ -f "$(echo "${pattern}" | sed 's/^|//; s/ .*//' ).cuopt_orig" ]]; then
238+
_pipe_msg+=" Handler hijacked with core forwarder — cores should land in ${CUOPT_COREDUMP_DIR}."
180239
else
181-
_pipe_msg+=" coredumpctl NOT found; if systemd-coredump is the handler, cores may be lost."
240+
_pipe_msg+=" Handler hijack failed. Fallback: coredumpctl / /var/crash at collection time."
241+
if command -v coredumpctl &>/dev/null; then
242+
_pipe_msg+=" coredumpctl is available."
243+
else
244+
_pipe_msg+=" coredumpctl NOT found; cores may be lost."
245+
fi
182246
fi
183247
cuopt__log "${_pipe_msg}"
184248
fi

0 commit comments

Comments
 (0)