selinux-policy/m4-bad-data: add M4 bad-data tests for modular policy builds

Add beakerlib/TMT coverage and fixtures for M4 preprocessing of modular
policy builds: .if to all_interfaces.conf, .te + interfaces to .tmp
(then checkmodule), and .fc to .mod.fc, using live support macros from a
selinux-policy checkout and a snapshotted modular M4 recipe.

Each case is its own rlRun phase in runtest.sh. Covers interface path
validation (including duplicates and broken gen_require), .te arity
edge cases, post-M4 checkmodule failures from bad interface expansion,
.fc path edge cases, M4-to-semodule_package E2E, and .fc content where
labeling validation is deferred to sefcontext_compile.

When SELINUX_POLICY_SRC is unset, clones fedora-selinux/selinux-policy
using a host-detected branch (f$VERSION_ID on Fedora, c${major}s
otherwise), overridable with SELINUX_POLICY_BRANCH.

Signed-off-by: Akhil Kohli <akkohli@redhat.com>
This commit is contained in:
Akhil Kohli 2026-07-21 13:14:40 +02:00 committed by plautrba
commit 5cd19cd9d3
7 changed files with 338 additions and 810 deletions

View file

@ -3,3 +3,8 @@ Author: Akhil Kohli <akkohli@redhat.com>
Description: Negative bad-data tests for the M4 preprocessing stage of modular
SELinux policy builds (.if, .te, and .fc inputs before checkmodule).
Execution is via FMF/TMT (main.fmf -> runtest.sh) using beakerlib rlRun
phases. Support macros prefer SELINUX_POLICY_SRC, else installed
selinux-policy-devel (/usr/share/selinux/devel/include/support), else a
git clone (Fedora falls back to rawhide when f$N is missing).

View file

@ -1 +0,0 @@
/tmp gen_context(system_u:object_r:tmp_t,s0)

View file

@ -1,4 +0,0 @@
interface(`test_good_iface',`
gen_require(`type $1; class file { read };')
allow $1 self:file read;
')

View file

@ -8,9 +8,9 @@ component:
require:
- checkpolicy
- m4
- make
- git
- policycoreutils
- selinux-policy-devel
recommend:
- selinux-policy
- selinux-policy-targeted

View file

@ -1,57 +0,0 @@
# Test harness Makefile (not the standard selinux-policy build / Rules.modular).
# Mirrors the modular primary dependency from Rules.modular (~lines 73-86):
# .mod/.pp require the module .te. Used only by m4-bad-data/run.sh for the
# missing-.te negative case. Real M4 support files still come from POLICY_ROOT.
POLICY_ROOT ?= ../../..
MODDIR ?=
OUTDIR ?= $(MODDIR)/out
MOD ?= bad_missing_te
M4 ?= m4
CHECKMODULE ?= checkmodule
CHECKMODULE_OPTS ?= -M -m
SEMOD_PKG ?= semodule_package
SED ?= sed
M4SUPPORT = $(POLICY_ROOT)/support/divert.m4 \
$(POLICY_ROOT)/policy/support/misc_macros.spt \
$(POLICY_ROOT)/policy/support/mls_mcs_macros.spt \
$(POLICY_ROOT)/policy/support/loadable_module.spt \
$(POLICY_ROOT)/policy/support/obj_perm_sets.spt \
$(POLICY_ROOT)/support/undivert.m4
M4PARAM = -D enable_mcs -D distro_redhat -D hide_broken_symptoms \
-D mls_num_sens=16 -D mls_num_cats=1024 -D mcs_num_cats=1024
IFERROR = $(POLICY_ROOT)/support/iferror.m4
EMPTY_GEN_DEF = /dev/null
$(OUTDIR)/generated_definitions.conf:
@mkdir -p $(OUTDIR)
@touch $@
$(OUTDIR)/all_interfaces.conf: $(M4SUPPORT) $(MODDIR)/$(MOD).if $(IFERROR)
@mkdir -p $(OUTDIR)
@echo 'divert(-1)' > $@
$(M4) $(M4PARAM) $(M4SUPPORT) $(MODDIR)/$(MOD).if $(IFERROR) >> $(OUTDIR)/all_interfaces.tmp
$(SED) -e 's/dollarsstar/$$*/g' $(OUTDIR)/all_interfaces.tmp >> $@
@echo 'divert' >> $@
$(OUTDIR)/$(MOD).mod: $(M4SUPPORT) $(OUTDIR)/generated_definitions.conf \
$(OUTDIR)/all_interfaces.conf $(MODDIR)/$(MOD).te
@mkdir -p $(OUTDIR)
$(M4) $(M4PARAM) -s $(M4SUPPORT) $(EMPTY_GEN_DEF) \
$(OUTDIR)/all_interfaces.conf $(MODDIR)/$(MOD).te > $(OUTDIR)/$(MOD).tmp
$(CHECKMODULE) $(CHECKMODULE_OPTS) -o $@ $(OUTDIR)/$(MOD).tmp
$(OUTDIR)/$(MOD).mod.fc: $(M4SUPPORT) $(MODDIR)/$(MOD).fc
@mkdir -p $(OUTDIR)
$(M4) $(M4PARAM) $(M4SUPPORT) $(MODDIR)/$(MOD).fc > $@
$(OUTDIR)/$(MOD).pp: $(OUTDIR)/$(MOD).mod $(OUTDIR)/$(MOD).mod.fc
@mkdir -p $(OUTDIR)
$(SEMOD_PKG) -o $@ -m $(OUTDIR)/$(MOD).mod -f $(OUTDIR)/$(MOD).mod.fc
.PHONY: all
all: $(OUTDIR)/$(MOD).pp

View file

@ -1,733 +0,0 @@
#!/bin/sh
#
# Bad-data tests for the M4 preprocessing stage of modular policy builds:
# .if -> all_interfaces.conf, .te + interfaces -> .tmp, .fc -> .mod.fc
#
# Mirrors Rules.modular invocations; uses plain "module name 1.0;" fixtures so tests
# do not depend on generated_definitions.conf.
#
set -u
BASEDIR=$(CDPATH= cd -- "$(dirname "$0")" && pwd)
POLICY_ROOT=${SELINUX_POLICY_SRC:-}
if [ -z "${POLICY_ROOT}" ] || [ ! -f "${POLICY_ROOT}/Rules.modular" ]; then
echo "SELINUX_POLICY_SRC must point to a selinux-policy source checkout" >&2
exit 1
fi
FIXTURES="${BASEDIR}/fixtures"
OUTDIR=$(mktemp -d "${TMPDIR:-/tmp}/selinux-policy-bad-data.XXXXXX")
PASS=0
FAIL=0
M4=${M4:-m4}
CHECKMODULE=${CHECKMODULE:-checkmodule}
MAKE=${MAKE:-make}
SEMODULE_PACKAGE=${SEMODULE_PACKAGE:-semodule_package}
M4SUPPORT="${POLICY_ROOT}/support/divert.m4 \
${POLICY_ROOT}/policy/support/misc_macros.spt \
${POLICY_ROOT}/policy/support/mls_mcs_macros.spt \
${POLICY_ROOT}/policy/support/loadable_module.spt \
${POLICY_ROOT}/policy/support/obj_perm_sets.spt \
${POLICY_ROOT}/support/undivert.m4"
M4PARAM="-D enable_mcs -D distro_redhat -D hide_broken_symptoms -D mls_num_sens=16 -D mls_num_cats=1024 -D mcs_num_cats=1024"
IFERROR="${POLICY_ROOT}/support/iferror.m4"
EMPTY_GEN_DEF=/dev/null
cleanup() {
rm -rf "${OUTDIR}"
}
trap cleanup EXIT
die() {
echo "FAIL: $*" >&2
FAIL=$((FAIL + 1))
}
pass() {
echo "==== $*"
PASS=$((PASS + 1))
echo ""
}
# Build all_interfaces.conf from one or more .if files (Rules.modular lines 138-143).
build_all_interfaces() {
out="$1"
shift
echo 'divert(-1)' >"${out}"
# shellcheck disable=SC2086
${M4} ${M4PARAM} ${M4SUPPORT} "$@" "${IFERROR}" >"${out}.tmp" 2>"${out}.err"
rc=$?
sed -e 's/dollarsstar/$*/g' "${out}.tmp" >>"${out}"
echo 'divert' >>"${out}"
return "${rc}"
}
# Expand a module .te with interfaces (Rules.modular line 73-76; /dev/null for gen defs).
expand_module_te() {
interfaces="$1"
te="$2"
out="$3"
# shellcheck disable=SC2086
${M4} ${M4PARAM} -s ${M4SUPPORT} "${EMPTY_GEN_DEF}" "${interfaces}" "${te}" \
>"${out}" 2>"${out}.err"
rc=$?
return "${rc}"
}
# Expand file_contexts (Rules.modular line 79-81).
expand_fc() {
fc="$1"
out="$2"
# shellcheck disable=SC2086
${M4} ${M4PARAM} ${M4SUPPORT} "${fc}" >"${out}" 2>"${out}.err"
rc=$?
return "${rc}"
}
mod_name_from_te() {
sed -n 's/^module[[:space:]]\+\([^[:space:]]\+\).*/\1/p' "$1" | head -1
}
expect_m4_interfaces_fail() {
desc="$1"
name="$2"
shift 2
echo "==== NEGATIVE (expect M4 interface build failure): ${desc}"
out="${OUTDIR}/${name}.interfaces"
rm -f "${out}" "${out}.tmp" "${out}.err"
build_all_interfaces "${out}" "$@"
rc=$?
if [ "${rc}" -eq 0 ]; then
die "${desc}: expected non-zero M4 exit, got rc=0"
return 0
fi
pass "${desc} (M4 failed as expected, rc=${rc})"
}
expect_m4_interfaces_fail_unreadable() {
desc="unreadable .if file"
name="unreadable_if"
if_path="${OUTDIR}/unreadable.if"
out="${OUTDIR}/${name}.interfaces"
echo "==== NEGATIVE (expect M4 interface build failure): ${desc}"
if [ "$(id -u)" -eq 0 ]; then
echo "SKIP: root can read mode 000 files; unreadable check is non-root only"
PASS=$((PASS + 1))
echo ""
return 0
fi
rm -f "${out}" "${out}.tmp" "${out}.err"
cp "${FIXTURES}/interfaces/good.if" "${if_path}"
chmod 000 "${if_path}"
build_all_interfaces "${out}" "${if_path}"
rc=$?
if [ "${rc}" -eq 0 ]; then
die "${desc}: expected non-zero M4 exit, got rc=0"
return 0
fi
pass "${desc} (M4 failed as expected, rc=${rc})"
}
expect_m4_interfaces_pass() {
desc="$1"
name="$2"
shift 2
echo "==== POSITIVE (expect M4 interface build success): ${desc}"
out="${OUTDIR}/${name}.interfaces"
rm -f "${out}" "${out}.tmp" "${out}.err"
build_all_interfaces "${out}" "$@"
rc=$?
if [ "${rc}" -ne 0 ]; then
echo "stderr:" >&2
cat "${out}.err" >&2
die "${desc}: expected M4 success, got rc=${rc}"
return 1
fi
if [ ! -s "${out}" ]; then
die "${desc}: expected non-empty ${out}"
return 1
fi
pass "${desc} (M4 succeeded)"
}
expect_expand_pass() {
desc="$1"
name="$2"
interfaces="$3"
te="$4"
echo "==== POSITIVE (expect module M4 expand success): ${desc}"
tmp="${OUTDIR}/${name}.tmp"
rm -f "${tmp}" "${tmp}.err"
expand_module_te "${interfaces}" "${te}" "${tmp}"
rc=$?
if [ "${rc}" -ne 0 ]; then
cat "${tmp}.err" >&2
die "${desc}: expected M4 success, got rc=${rc}"
return 1
fi
if [ ! -s "${tmp}" ]; then
die "${desc}: expected non-empty ${tmp}"
return 1
fi
pass "${desc} (M4 expand succeeded)"
}
expect_expand_fail() {
desc="$1"
name="$2"
interfaces="$3"
te="$4"
echo "==== NEGATIVE (expect module M4 expand failure): ${desc}"
tmp="${OUTDIR}/${name}.tmp"
rm -f "${tmp}" "${tmp}.err"
expand_module_te "${interfaces}" "${te}" "${tmp}"
rc=$?
if [ "${rc}" -eq 0 ]; then
die "${desc}: expected M4 failure, got rc=0"
return 1
fi
pass "${desc} (M4 expand failed as expected, rc=${rc})"
}
# Missing-.te check via make-primary/Makefile (harness mirror of Rules.modular
# .te→.mod dependency), not the standard selinux-policy build / Rules.modular.
expect_make_missing_te_fail() {
desc="Make fails when module .te is missing (harness Makefile, modular-style deps)"
name="b31_missing_te"
mod="bad_missing_te"
moddir="${OUTDIR}/${name}_mod"
makedir="${BASEDIR}/make-primary"
out="${moddir}/out"
log="${OUTDIR}/${name}.log"
echo "==== NEGATIVE (expect Makefile failure): ${desc}"
rm -rf "${moddir}"
mkdir -p "${moddir}"
cp "${FIXTURES}/make_primary/${mod}.if" "${moddir}/${mod}.if"
cp "${FIXTURES}/make_primary/${mod}.fc" "${moddir}/${mod}.fc"
rm -f "${log}"
${MAKE} -f "${makedir}/Makefile" \
POLICY_ROOT="${POLICY_ROOT}" \
MODDIR="${moddir}" \
OUTDIR="${out}" \
MOD="${mod}" \
"${out}/${mod}.pp" >"${log}" 2>&1
rc=$?
if [ "${rc}" -eq 0 ]; then
cat "${log}" >&2
die "${desc}: expected non-zero make exit, got rc=0"
return 1
fi
if [ -f "${out}/${mod}.mod" ] || [ -f "${out}/${mod}.pp" ]; then
die "${desc}: did not expect ${out}/${mod}.mod or .pp"
return 1
fi
if ! grep -Eq "${mod}\\.te|No rule to make target|No such file" "${log}"; then
echo "FAIL: make log did not mention missing ${mod}.te" >&2
cat "${log}" >&2
FAIL=$((FAIL + 1))
return 0
fi
pass "${desc} (make failed as expected, rc=${rc})"
}
expect_m4_package_e2e_deferred() {
desc="$1"
name="$2"
fc="$3"
interfaces="$4"
te="$5"
modfc="${OUTDIR}/${name}.mod.fc"
pp="${OUTDIR}/${name}.pp"
tmp="${OUTDIR}/${name}.tmp"
modname=$(mod_name_from_te "${te}")
e2e_dir="${OUTDIR}/${name}_build"
goodmod="${e2e_dir}/${modname}.mod"
stderr="${OUTDIR}/${name}.err"
echo "==== DOCUMENT (M4 to semodule_package E2E; validate in sefcontext_compile): ${desc}"
if ! command -v "${SEMODULE_PACKAGE}" >/dev/null 2>&1; then
die "${desc}: ${SEMODULE_PACKAGE} not found on PATH"
return 1
fi
rm -rf "${e2e_dir}"
mkdir -p "${e2e_dir}"
rm -f "${modfc}" "${modfc}.err" "${pp}" "${stderr}" "${tmp}" "${tmp}.err"
expand_module_te "${interfaces}" "${te}" "${tmp}"
rc=$?
if [ "${rc}" -ne 0 ]; then
cat "${tmp}.err" >&2
die "${desc}: expected M4 success for good .te, got rc=${rc}"
return 1
fi
"${CHECKMODULE}" -M -m -o "${goodmod}" "${tmp}" 2>"${stderr}"
rc=$?
if [ "${rc}" -ne 0 ]; then
cat "${stderr}" >&2
die "${desc}: expected checkmodule success for good .mod, got rc=${rc}"
return 1
fi
expand_fc "${fc}" "${modfc}"
rc=$?
if [ "${rc}" -ne 0 ]; then
cat "${modfc}.err" >&2
die "${desc}: expected M4 success for bad .fc, got rc=${rc}"
return 1
fi
rm -f "${pp}"
"${SEMODULE_PACKAGE}" -o "${pp}" -m "${goodmod}" -f "${modfc}" 2>"${stderr}"
rc=$?
if [ "${rc}" -ne 0 ]; then
cat "${stderr}" >&2
die "${desc}: expected semodule_package exit 0 at packaging stage, got rc=${rc}"
return 1
fi
if [ ! -s "${pp}" ]; then
die "${desc}: expected non-empty ${pp}"
return 1
fi
pass "${desc} (M4 to package exit 0; labeling validation deferred to sefcontext_compile)"
}
expect_expand_then_checkmodule_pass() {
desc="$1"
name="$2"
interfaces="$3"
te="$4"
expect_expand_pass "${desc}" "${name}" "${interfaces}" "${te}" || return 1
tmp="${OUTDIR}/${name}.tmp"
modname=$(mod_name_from_te "${te}")
outmod="${OUTDIR}/${modname}.mod"
stderr="${OUTDIR}/${modname}.err"
echo "==== POSITIVE (expect checkmodule success after M4): ${desc}"
rm -f "${outmod}"
"${CHECKMODULE}" -M -m -o "${outmod}" "${tmp}" 2>"${stderr}"
rc=$?
if [ "${rc}" -ne 0 ]; then
cat "${stderr}" >&2
die "${desc}: expected checkmodule success, got rc=${rc}"
return 1
fi
if [ ! -s "${outmod}" ]; then
die "${desc}: expected non-empty ${outmod}"
return 1
fi
pass "${desc} (checkmodule succeeded)"
}
expect_expand_then_checkmodule_fail() {
desc="$1"
name="$2"
interfaces="$3"
te="$4"
expect_expand_pass "${desc} (M4 stage)" "${name}" "${interfaces}" "${te}" || return 1
tmp="${OUTDIR}/${name}.tmp"
modname=$(mod_name_from_te "${te}")
outmod="${OUTDIR}/${modname}.mod"
stderr="${OUTDIR}/${modname}.err"
echo "==== NEGATIVE (expect checkmodule failure after M4): ${desc}"
rm -f "${outmod}"
"${CHECKMODULE}" -M -m -o "${outmod}" "${tmp}" 2>"${stderr}"
rc=$?
if [ "${rc}" -eq 0 ]; then
die "${desc}: expected checkmodule failure, got rc=0"
return 1
fi
pass "${desc} (checkmodule failed as expected, rc=${rc})"
}
expect_expand_then_checkmodule_pass_document() {
desc="$1"
name="$2"
interfaces="$3"
te="$4"
note="$5"
expect_expand_pass "${desc}" "${name}" "${interfaces}" "${te}" || return 1
tmp="${OUTDIR}/${name}.tmp"
modname=$(mod_name_from_te "${te}")
outmod="${OUTDIR}/${modname}.mod"
stderr="${OUTDIR}/${modname}.err"
echo "==== DOCUMENT (${note}): ${desc}"
rm -f "${outmod}"
"${CHECKMODULE}" -M -m -o "${outmod}" "${tmp}" 2>"${stderr}"
rc=$?
if [ "${rc}" -ne 0 ]; then
cat "${stderr}" >&2
die "${desc}: expected checkmodule success, got rc=${rc}"
return 1
fi
if [ ! -s "${outmod}" ]; then
die "${desc}: expected non-empty ${outmod}"
return 1
fi
pass "${desc} (M4 and checkmodule succeeded; ${note})"
}
expect_fc_m4_pass() {
desc="$1"
name="$2"
fc="$3"
echo "==== POSITIVE (expect .fc M4 pass): ${desc}"
out="${OUTDIR}/${name}.mod.fc"
rm -f "${out}" "${out}.err"
expand_fc "${fc}" "${out}"
rc=$?
if [ "${rc}" -ne 0 ]; then
cat "${out}.err" >&2
die "${desc}: expected M4 success, got rc=${rc}"
return 1
fi
if [ ! -s "${out}" ]; then
die "${desc}: expected non-empty ${out}"
return 1
fi
pass "${desc} (M4 produced output; labeling validation is post-.mod)"
}
expect_fc_m4_pass_deferred() {
desc="$1"
name="$2"
fc="$3"
allow_empty="${4:-0}"
echo "==== DOCUMENT (M4 accepts input; validate in sefcontext_compile): ${desc}"
out="${OUTDIR}/${name}.mod.fc"
rm -f "${out}" "${out}.err"
expand_fc "${fc}" "${out}"
rc=$?
if [ "${rc}" -ne 0 ]; then
cat "${out}.err" >&2
die "${desc}: expected M4 success at preprocessing stage, got rc=${rc}"
return 1
fi
if [ "${allow_empty}" -eq 0 ] && [ ! -s "${out}" ]; then
die "${desc}: expected non-empty ${out} at preprocessing stage"
return 1
fi
pass "${desc} (M4 exit 0; labeling validation deferred to sefcontext_compile)"
}
expect_fc_m4_fail() {
desc="$1"
name="$2"
fc="$3"
echo "==== NEGATIVE (expect .fc M4 failure): ${desc}"
out="${OUTDIR}/${name}.mod.fc"
rm -f "${out}" "${out}.err"
expand_fc "${fc}" "${out}"
rc=$?
if [ "${rc}" -eq 0 ]; then
die "${desc}: expected M4 failure, got rc=0"
return 1
fi
pass "${desc} (M4 failed as expected, rc=${rc})"
}
expect_fc_m4_fail_unreadable() {
desc="unreadable .fc file"
fc="${OUTDIR}/unreadable.fc"
out="${OUTDIR}/unreadable_fc.mod.fc"
echo "==== NEGATIVE (expect .fc M4 failure): ${desc}"
if [ "$(id -u)" -eq 0 ]; then
echo "SKIP: root can read mode 000 files; unreadable check is non-root only"
PASS=$((PASS + 1))
echo ""
return 0
fi
rm -f "${out}" "${out}.err"
cp "${FIXTURES}/file_contexts/good.fc" "${fc}"
chmod 000 "${fc}"
expand_fc "${fc}" "${out}"
rc=$?
if [ "${rc}" -eq 0 ]; then
die "${desc}: expected non-zero M4 exit, got rc=0"
return 0
fi
pass "${desc} (M4 failed as expected, rc=${rc})"
}
# Ephemeral path fixtures for interface and file_context tests.
ln -sf /nonexistent/test_good.if "${OUTDIR}/broken_symlink.if"
ln -sf /nonexistent/test_good.mod.fc "${OUTDIR}/broken_symlink.fc"
printf '' > "${OUTDIR}/empty.fc"
# --- .if -> all_interfaces.conf ---
expect_m4_interfaces_fail \
"unclosed interface definition" \
b1_unclosed \
"${FIXTURES}/interfaces/bad_unclosed.if"
expect_m4_interfaces_fail \
"broken M4 syntax in interface file" \
b1_m4_syntax \
"${FIXTURES}/interfaces/bad_m4_syntax.if"
expect_m4_interfaces_fail \
"duplicate interface definition" \
b1_duplicate \
"${FIXTURES}/interfaces/bad_duplicate.if"
expect_m4_interfaces_fail \
"broken gen_require block in interface file" \
b1_bad_gen_if \
"${FIXTURES}/interfaces/bad_gen_if_build.if"
expect_m4_interfaces_fail \
"empty interface template name" \
b1_empty_ifname \
"${FIXTURES}/interfaces/bad_empty_ifname.if"
expect_m4_interfaces_fail \
"missing .if path" \
b1_missing \
"${OUTDIR}/does_not_exist.if"
expect_m4_interfaces_fail \
"directory instead of .if file" \
b1_directory \
"${BASEDIR}"
expect_m4_interfaces_fail \
"broken symlink for .if" \
b1_symlink \
"${OUTDIR}/broken_symlink.if"
expect_m4_interfaces_fail_unreadable
expect_m4_interfaces_pass \
"control good interface" \
b1_good \
"${FIXTURES}/interfaces/good.if"
# --- .te + M4 with interfaces ---
GOOD_IF="${OUTDIR}/b1_good.interfaces"
expect_expand_then_checkmodule_pass \
"control good .if + .te through M4 and checkmodule" \
b2_good \
"${GOOD_IF}" \
"${FIXTURES}/modules/te_good.te"
expect_expand_fail \
"missing .te path at M4 expand" \
b2_missing_te \
"${GOOD_IF}" \
"${OUTDIR}/does_not_exist.te"
expect_make_missing_te_fail
NEEDS_ARG_IF="${OUTDIR}/needs_arg.interfaces"
build_all_interfaces "${NEEDS_ARG_IF}" "${FIXTURES}/interfaces/needs_arg.if"
expect_expand_then_checkmodule_fail \
"interface called with too few arguments" \
b2_few_args \
"${NEEDS_ARG_IF}" \
"${FIXTURES}/modules/te_few_args.te"
expect_expand_then_checkmodule_pass_document \
"interface called with too many arguments" \
b2_many_args \
"${NEEDS_ARG_IF}" \
"${FIXTURES}/modules/te_many_args.te" \
"extra interface args are ignored by M4"
# --- M4 OK, bad expanded TE -> checkmodule fails ---
TRUNC_IF="${OUTDIR}/bad_trunc.interfaces"
build_all_interfaces "${TRUNC_IF}" "${FIXTURES}/interfaces/bad_trunc.if"
expect_expand_then_checkmodule_fail \
"truncated allow from interface expansion" \
b5_trunc \
"${TRUNC_IF}" \
"${FIXTURES}/modules/te_trunc.te"
GEN_REQ_IF="${OUTDIR}/bad_gen_require.interfaces"
build_all_interfaces "${GEN_REQ_IF}" "${FIXTURES}/interfaces/bad_gen_require.if"
expect_expand_then_checkmodule_fail \
"broken gen_require expanded from interface" \
b5_gen_require \
"${GEN_REQ_IF}" \
"${FIXTURES}/modules/te_gen_require.te"
UNKNOWN_TYPE_IF="${OUTDIR}/bad_unknown_type.interfaces"
build_all_interfaces "${UNKNOWN_TYPE_IF}" \
"${FIXTURES}/interfaces/bad_unknown_type.if"
expect_expand_then_checkmodule_fail \
"unknown type from interface expansion" \
b5_unknown_type \
"${UNKNOWN_TYPE_IF}" \
"${FIXTURES}/modules/te_unknown_type.te"
UNKNOWN_PERM_IF="${OUTDIR}/bad_unknown_perm.interfaces"
build_all_interfaces "${UNKNOWN_PERM_IF}" \
"${FIXTURES}/interfaces/bad_unknown_perm.if"
expect_expand_then_checkmodule_fail \
"unknown permission from interface expansion" \
b5_unknown_perm \
"${UNKNOWN_PERM_IF}" \
"${FIXTURES}/modules/te_unknown_perm.te"
GARBAGE_IF="${OUTDIR}/bad_garbage.interfaces"
build_all_interfaces "${GARBAGE_IF}" "${FIXTURES}/interfaces/bad_garbage.if"
expect_expand_then_checkmodule_fail \
"garbage token from interface expansion" \
b5_garbage \
"${GARBAGE_IF}" \
"${FIXTURES}/modules/te_garbage.te"
MODULE_LINE_IF="${OUTDIR}/bad_module_line.interfaces"
build_all_interfaces "${MODULE_LINE_IF}" \
"${FIXTURES}/interfaces/bad_module_line.if"
expect_expand_then_checkmodule_fail \
"invalid module line from interface expansion" \
b5_module_line \
"${MODULE_LINE_IF}" \
"${FIXTURES}/modules/te_bad_module_line.te"
# --- .fc -> M4 -> .mod.fc ---
expect_fc_m4_pass \
"control good .fc through M4" \
b3_good \
"${FIXTURES}/file_contexts/good.fc"
expect_fc_m4_pass_deferred \
"invalid context survives M4" \
b3_bad_context \
"${FIXTURES}/file_contexts/bad_context.fc"
expect_fc_m4_pass_deferred \
"wrong field count in .fc" \
b3_bad_fields \
"${FIXTURES}/file_contexts/bad_fields.fc"
expect_fc_m4_pass_deferred \
"empty .fc file" \
b3_empty_fc \
"${OUTDIR}/empty.fc" \
1
expect_fc_m4_pass_deferred \
"path-only line without context in .fc" \
b3_no_context \
"${FIXTURES}/file_contexts/bad_no_context.fc"
expect_fc_m4_pass_deferred \
"invalid regex metacharacters in .fc path" \
b3_bad_regex \
"${FIXTURES}/file_contexts/bad_regex.fc"
expect_fc_m4_fail \
"missing .fc path" \
b3_missing \
"${OUTDIR}/does_not_exist.fc"
expect_fc_m4_fail \
"directory instead of .fc file" \
b3_directory \
"${BASEDIR}"
expect_fc_m4_fail \
"broken symlink for .fc" \
b3_symlink \
"${OUTDIR}/broken_symlink.fc"
expect_fc_m4_fail_unreadable
expect_fc_m4_fail \
"broken M4 syntax in .fc" \
b3_bad_m4 \
"${FIXTURES}/file_contexts/bad_m4_syntax.fc"
# --- M4-expanded .mod.fc through semodule_package (E2E; labeling deferred) ---
expect_m4_package_e2e_deferred \
"bad M4-expanded .mod.fc packaged with good .mod (M4 to package E2E)" \
b6_e2e \
"${FIXTURES}/file_contexts/bad_context.fc" \
"${GOOD_IF}" \
"${FIXTURES}/modules/te_good.te"
echo "========================================"
echo "Results: ${PASS} passed, ${FAIL} failed"
if [ "${FAIL}" -ne 0 ]; then
exit 1
fi
exit 0

View file

@ -14,7 +14,16 @@ PACKAGE="selinux-policy"
POLICY_GIT="${SELINUX_POLICY_GIT:-https://github.com/fedora-selinux/selinux-policy.git}"
POLICY_DIR="${SELINUX_POLICY_SRC:-}"
# Prefer explicit override; otherwise pick the host's default selinux-policy branch.
BASEDIR="$(cd "$(dirname "$0")" && pwd)"
FIXTURES="${BASEDIR}/fixtures"
M4=${M4:-m4}
CHECKMODULE=${CHECKMODULE:-checkmodule}
SEMODULE_PACKAGE=${SEMODULE_PACKAGE:-semodule_package}
# Prefer explicit override; otherwise pick a clone branch for local/fallback use.
# Fedora Rawhide may report VERSION_ID=rawhide or a numeric ID ahead of any fN
# branch (e.g. 45 while only rawhide/f44 exist). Clone falls back to rawhide.
if [ -n "${SELINUX_POLICY_BRANCH:-}" ]; then
POLICY_BRANCH="${SELINUX_POLICY_BRANCH}"
else
@ -22,39 +31,348 @@ else
# shellcheck disable=SC1090
. "${os_release}"
if rlIsOSLike fedora; then
# e.g. Fedora 43 -> f43 (matches fedora-selinux/selinux-policy branches)
POLICY_BRANCH="f${VERSION_ID}"
fedora_rel=""
if [[ "${VERSION_ID}" =~ ^[0-9]+$ ]]; then
fedora_rel="${VERSION_ID}"
elif [[ "${REDHAT_BUGZILLA_PRODUCT_VERSION:-}" =~ ^[0-9]+$ ]]; then
fedora_rel="${REDHAT_BUGZILLA_PRODUCT_VERSION}"
elif [[ "${REDHAT_SUPPORT_PRODUCT_VERSION:-}" =~ ^[0-9]+$ ]]; then
fedora_rel="${REDHAT_SUPPORT_PRODUCT_VERSION}"
fi
if [ -n "${fedora_rel}" ]; then
POLICY_BRANCH="f${fedora_rel}"
else
POLICY_BRANCH="rawhide"
fi
else
# e.g. CentOS Stream / RHEL 10.x -> c10s
VERSION="${VERSION_ID%.*}"
POLICY_BRANCH="c${VERSION}s"
fi
fi
DEVEL_SUPPORT="/usr/share/selinux/devel/include/support"
# Configure M4SUPPORT/IFERROR from a selinux-policy git checkout layout.
setup_m4_from_git_tree() {
local root="$1"
M4SUPPORT="${root}/support/divert.m4 \
${root}/policy/support/misc_macros.spt \
${root}/policy/support/mls_mcs_macros.spt \
${root}/policy/support/loadable_module.spt \
${root}/policy/support/obj_perm_sets.spt \
${root}/support/undivert.m4"
IFERROR="${root}/support/iferror.m4"
POLICY_ROOT="${root}"
}
# Configure M4SUPPORT from installed selinux-policy-devel (flat support/).
setup_m4_from_devel() {
local support="$1"
M4SUPPORT="${support}/divert.m4 \
${support}/misc_macros.spt \
${support}/mls_mcs_macros.spt \
${support}/loadable_module.spt \
${support}/obj_perm_sets.spt \
${support}/undivert.m4"
# iferror.m4 is build-tree only; recreate the upstream one-liner when absent.
if [ -f "${support}/iferror.m4" ]; then
IFERROR="${support}/iferror.m4"
else
IFERROR="${OUTDIR}/iferror.m4"
cat >"${IFERROR}" <<'EOF'
ifdef(`__if_error',`m4exit(1)')
EOF
fi
POLICY_ROOT="${support}"
}
# Build all_interfaces.conf from one or more .if files (usual modular M4 recipe).
build_all_interfaces() {
local out="$1"
shift
echo 'divert(-1)' >"${out}"
# shellcheck disable=SC2086
${M4} ${M4PARAM} ${M4SUPPORT} "$@" "${IFERROR}" >"${out}.tmp" 2>"${out}.err"
local rc=$?
sed -e 's/dollarsstar/$*/g' "${out}.tmp" >>"${out}"
echo 'divert' >>"${out}"
return "${rc}"
}
# Expand a module .te with interfaces (usual modular M4 recipe; /dev/null for gen defs).
expand_module_te() {
local interfaces="$1"
local te="$2"
local out="$3"
# shellcheck disable=SC2086
${M4} ${M4PARAM} -s ${M4SUPPORT} "${EMPTY_GEN_DEF}" "${interfaces}" "${te}" \
>"${out}" 2>"${out}.err"
return $?
}
# Expand file_contexts (usual modular M4 recipe for .fc -> .mod.fc).
expand_fc() {
local fc="$1"
local out="$2"
# shellcheck disable=SC2086
${M4} ${M4PARAM} ${M4SUPPORT} "${fc}" >"${out}" 2>"${out}.err"
return $?
}
rlJournalStart
rlPhaseStartSetup
rlAssertRpm checkpolicy
rlRun "command -v m4"
rlRun "command -v checkmodule"
rlRun "command -v make"
rlRun "command -v semodule_package"
if [ -z "${POLICY_DIR}" ]; then
OUTDIR=$(mktemp -d "${TMPDIR:-/var/tmp}/selinux-policy-m4-bad-data.XXXXXX")
rlRun "test -d ${OUTDIR}"
CLONED_POLICY_DIR=""
# Macro source priority (Petr): explicit checkout > installed devel > git clone.
if [ -n "${POLICY_DIR}" ]; then
rlAssertExists "${POLICY_DIR}/Rules.modular"
setup_m4_from_git_tree "${POLICY_DIR}"
rlLog "Using SELINUX_POLICY_SRC=${POLICY_DIR}"
elif [ -f "${DEVEL_SUPPORT}/loadable_module.spt" ]; then
rlAssertRpm selinux-policy-devel
setup_m4_from_devel "${DEVEL_SUPPORT}"
rlLog "Using selinux-policy-devel support macros from ${DEVEL_SUPPORT}"
else
CLONED_POLICY_DIR="$(mktemp -d /var/tmp/selinux-policy-src.XXXXXX)"
rlLog "Cloning ${POLICY_GIT} branch ${POLICY_BRANCH}"
POLICY_DIR="$(mktemp -d /var/tmp/selinux-policy-src.XXXXXX)"
rlRun "git clone --depth=1 -b ${POLICY_BRANCH} ${POLICY_GIT} ${POLICY_DIR}"
if rlIsOSLike fedora && [ "${POLICY_BRANCH}" != "rawhide" ]; then
# fN may not exist yet on Rawhide (e.g. f45); allow failure then retry.
rlRun "git clone --depth=1 -b ${POLICY_BRANCH} ${POLICY_GIT} ${CLONED_POLICY_DIR}" 0-255
if [ ! -f "${CLONED_POLICY_DIR}/Rules.modular" ]; then
rlLog "Branch ${POLICY_BRANCH} unavailable; falling back to rawhide"
rlRun "rm -rf ${CLONED_POLICY_DIR}"
CLONED_POLICY_DIR="$(mktemp -d /var/tmp/selinux-policy-src.XXXXXX)"
rlRun "git clone --depth=1 -b rawhide ${POLICY_GIT} ${CLONED_POLICY_DIR}"
fi
else
rlRun "git clone --depth=1 -b ${POLICY_BRANCH} ${POLICY_GIT} ${CLONED_POLICY_DIR}"
fi
rlAssertExists "${CLONED_POLICY_DIR}/Rules.modular"
setup_m4_from_git_tree "${CLONED_POLICY_DIR}"
fi
rlAssertExists "${POLICY_DIR}/Rules.modular"
rlRun "chmod +x run.sh"
# shellcheck disable=SC2086
rlRun "test -f $(echo ${M4SUPPORT} | awk '{print $1}')"
rlRun "test -f ${IFERROR}"
M4PARAM="-D enable_mcs -D distro_redhat -D hide_broken_symptoms -D mls_num_sens=16 -D mls_num_cats=1024 -D mcs_num_cats=1024"
EMPTY_GEN_DEF=/dev/null
rlRun "ln -sf /nonexistent/test_good.if ${OUTDIR}/broken_symlink.if"
rlRun "ln -sf /nonexistent/test_good.mod.fc ${OUTDIR}/broken_symlink.fc"
rlRun "printf '' > ${OUTDIR}/empty.fc"
rlPhaseEnd
rlPhaseStartTest "M4 bad-data preprocessing"
rlRun "SELINUX_POLICY_SRC=${POLICY_DIR} ./run.sh"
# --- .if -> all_interfaces.conf ---
rlPhaseStartTest "unclosed interface definition"
rlRun "build_all_interfaces ${OUTDIR}/b1_unclosed.interfaces ${FIXTURES}/interfaces/bad_unclosed.if" 1-255
rlPhaseEnd
rlPhaseStartTest "broken M4 syntax in interface file"
rlRun "build_all_interfaces ${OUTDIR}/b1_m4_syntax.interfaces ${FIXTURES}/interfaces/bad_m4_syntax.if" 1-255
rlPhaseEnd
rlPhaseStartTest "duplicate interface definition"
rlRun "build_all_interfaces ${OUTDIR}/b1_duplicate.interfaces ${FIXTURES}/interfaces/bad_duplicate.if" 1-255
rlPhaseEnd
rlPhaseStartTest "broken gen_require block in interface file"
rlRun "build_all_interfaces ${OUTDIR}/b1_bad_gen_if.interfaces ${FIXTURES}/interfaces/bad_gen_if_build.if" 1-255
rlPhaseEnd
rlPhaseStartTest "empty interface template name"
rlRun "build_all_interfaces ${OUTDIR}/b1_empty_ifname.interfaces ${FIXTURES}/interfaces/bad_empty_ifname.if" 1-255
rlPhaseEnd
rlPhaseStartTest "missing .if path"
rlRun "build_all_interfaces ${OUTDIR}/b1_missing.interfaces ${OUTDIR}/does_not_exist.if" 1-255
rlPhaseEnd
rlPhaseStartTest "directory instead of .if file"
rlRun "build_all_interfaces ${OUTDIR}/b1_directory.interfaces ${BASEDIR}" 1-255
rlPhaseEnd
rlPhaseStartTest "broken symlink for .if"
rlRun "build_all_interfaces ${OUTDIR}/b1_symlink.interfaces ${OUTDIR}/broken_symlink.if" 1-255
rlPhaseEnd
rlPhaseStartTest "unreadable .if file"
if [ "$(id -u)" -eq 0 ]; then
rlLog "SKIP: root can read mode 000 files; unreadable check is non-root only"
else
rlRun "cp ${FIXTURES}/interfaces/good.if ${OUTDIR}/unreadable.if"
rlRun "chmod 000 ${OUTDIR}/unreadable.if"
rlRun "build_all_interfaces ${OUTDIR}/unreadable_if.interfaces ${OUTDIR}/unreadable.if" 1-255
fi
rlPhaseEnd
rlPhaseStartTest "control good interface"
rlRun "build_all_interfaces ${OUTDIR}/b1_good.interfaces ${FIXTURES}/interfaces/good.if"
rlRun "test -s ${OUTDIR}/b1_good.interfaces"
rlPhaseEnd
GOOD_IF="${OUTDIR}/b1_good.interfaces"
# --- .te + M4 with interfaces ---
rlPhaseStartTest "control good .if + .te through M4 and checkmodule"
rlRun "expand_module_te ${GOOD_IF} ${FIXTURES}/modules/te_good.te ${OUTDIR}/b2_good.tmp"
rlRun "test -s ${OUTDIR}/b2_good.tmp"
rlRun "${CHECKMODULE} -M -m -o ${OUTDIR}/te_good.mod ${OUTDIR}/b2_good.tmp"
rlRun "test -s ${OUTDIR}/te_good.mod"
rlPhaseEnd
rlPhaseStartTest "missing .te path at M4 expand"
rlRun "expand_module_te ${GOOD_IF} ${OUTDIR}/does_not_exist.te ${OUTDIR}/b2_missing_te.tmp" 1-255
rlPhaseEnd
NEEDS_ARG_IF="${OUTDIR}/needs_arg.interfaces"
rlPhaseStartTest "interface called with too few arguments"
rlRun "build_all_interfaces ${NEEDS_ARG_IF} ${FIXTURES}/interfaces/needs_arg.if"
rlRun "expand_module_te ${NEEDS_ARG_IF} ${FIXTURES}/modules/te_few_args.te ${OUTDIR}/b2_few_args.tmp"
rlRun "test -s ${OUTDIR}/b2_few_args.tmp"
rlRun "${CHECKMODULE} -M -m -o ${OUTDIR}/te_few.mod ${OUTDIR}/b2_few_args.tmp" 1-255
rlPhaseEnd
rlPhaseStartTest "interface called with too many arguments"
rlLog "DOCUMENT: extra interface args are ignored by M4"
rlRun "test -s ${NEEDS_ARG_IF}"
rlRun "expand_module_te ${NEEDS_ARG_IF} ${FIXTURES}/modules/te_many_args.te ${OUTDIR}/b2_many_args.tmp"
rlRun "test -s ${OUTDIR}/b2_many_args.tmp"
rlRun "${CHECKMODULE} -M -m -o ${OUTDIR}/te_many.mod ${OUTDIR}/b2_many_args.tmp"
rlRun "test -s ${OUTDIR}/te_many.mod"
rlPhaseEnd
# --- M4 OK, bad expanded TE -> checkmodule fails ---
# bad_trunc.if is unclosed at the M4 interface stage (EOF in string), so it is
# rejected before module expand — same class of failure as other broken .if files.
rlPhaseStartTest "truncated allow from interface expansion"
rlRun "build_all_interfaces ${OUTDIR}/bad_trunc.interfaces ${FIXTURES}/interfaces/bad_trunc.if" 1-255
rlPhaseEnd
rlPhaseStartTest "broken gen_require expanded from interface"
rlRun "build_all_interfaces ${OUTDIR}/bad_gen_require.interfaces ${FIXTURES}/interfaces/bad_gen_require.if"
rlRun "expand_module_te ${OUTDIR}/bad_gen_require.interfaces ${FIXTURES}/modules/te_gen_require.te ${OUTDIR}/b5_gen_require.tmp"
rlRun "test -s ${OUTDIR}/b5_gen_require.tmp"
rlRun "${CHECKMODULE} -M -m -o ${OUTDIR}/te_gen_require.mod ${OUTDIR}/b5_gen_require.tmp" 1-255
rlPhaseEnd
rlPhaseStartTest "unknown type from interface expansion"
rlRun "build_all_interfaces ${OUTDIR}/bad_unknown_type.interfaces ${FIXTURES}/interfaces/bad_unknown_type.if"
rlRun "expand_module_te ${OUTDIR}/bad_unknown_type.interfaces ${FIXTURES}/modules/te_unknown_type.te ${OUTDIR}/b5_unknown_type.tmp"
rlRun "test -s ${OUTDIR}/b5_unknown_type.tmp"
rlRun "${CHECKMODULE} -M -m -o ${OUTDIR}/te_unknown_type.mod ${OUTDIR}/b5_unknown_type.tmp" 1-255
rlPhaseEnd
rlPhaseStartTest "unknown permission from interface expansion"
rlRun "build_all_interfaces ${OUTDIR}/bad_unknown_perm.interfaces ${FIXTURES}/interfaces/bad_unknown_perm.if"
rlRun "expand_module_te ${OUTDIR}/bad_unknown_perm.interfaces ${FIXTURES}/modules/te_unknown_perm.te ${OUTDIR}/b5_unknown_perm.tmp"
rlRun "test -s ${OUTDIR}/b5_unknown_perm.tmp"
rlRun "${CHECKMODULE} -M -m -o ${OUTDIR}/te_unknown_perm.mod ${OUTDIR}/b5_unknown_perm.tmp" 1-255
rlPhaseEnd
rlPhaseStartTest "garbage token from interface expansion"
rlRun "build_all_interfaces ${OUTDIR}/bad_garbage.interfaces ${FIXTURES}/interfaces/bad_garbage.if"
rlRun "expand_module_te ${OUTDIR}/bad_garbage.interfaces ${FIXTURES}/modules/te_garbage.te ${OUTDIR}/b5_garbage.tmp"
rlRun "test -s ${OUTDIR}/b5_garbage.tmp"
rlRun "${CHECKMODULE} -M -m -o ${OUTDIR}/te_garbage.mod ${OUTDIR}/b5_garbage.tmp" 1-255
rlPhaseEnd
rlPhaseStartTest "invalid module line from interface expansion"
rlRun "build_all_interfaces ${OUTDIR}/bad_module_line.interfaces ${FIXTURES}/interfaces/bad_module_line.if"
rlRun "expand_module_te ${OUTDIR}/bad_module_line.interfaces ${FIXTURES}/modules/te_bad_module_line.te ${OUTDIR}/b5_module_line.tmp"
rlRun "test -s ${OUTDIR}/b5_module_line.tmp"
rlRun "${CHECKMODULE} -M -m -o ${OUTDIR}/te_bad_mod.mod ${OUTDIR}/b5_module_line.tmp" 1-255
rlPhaseEnd
# --- .fc -> M4 -> .mod.fc ---
rlPhaseStartTest "control good .fc through M4"
rlRun "expand_fc ${FIXTURES}/file_contexts/good.fc ${OUTDIR}/b3_good.mod.fc"
rlRun "test -s ${OUTDIR}/b3_good.mod.fc"
rlPhaseEnd
rlPhaseStartTest "invalid context survives M4"
rlLog "DOCUMENT: labeling validation deferred to sefcontext_compile"
rlRun "expand_fc ${FIXTURES}/file_contexts/bad_context.fc ${OUTDIR}/b3_bad_context.mod.fc"
rlRun "test -s ${OUTDIR}/b3_bad_context.mod.fc"
rlPhaseEnd
rlPhaseStartTest "wrong field count in .fc"
rlLog "DOCUMENT: labeling validation deferred to sefcontext_compile"
rlRun "expand_fc ${FIXTURES}/file_contexts/bad_fields.fc ${OUTDIR}/b3_bad_fields.mod.fc"
rlRun "test -s ${OUTDIR}/b3_bad_fields.mod.fc"
rlPhaseEnd
rlPhaseStartTest "empty .fc file"
rlLog "DOCUMENT: labeling validation deferred to sefcontext_compile"
rlRun "expand_fc ${OUTDIR}/empty.fc ${OUTDIR}/b3_empty_fc.mod.fc"
rlPhaseEnd
rlPhaseStartTest "path-only line without context in .fc"
rlLog "DOCUMENT: labeling validation deferred to sefcontext_compile"
rlRun "expand_fc ${FIXTURES}/file_contexts/bad_no_context.fc ${OUTDIR}/b3_no_context.mod.fc"
rlRun "test -s ${OUTDIR}/b3_no_context.mod.fc"
rlPhaseEnd
rlPhaseStartTest "invalid regex metacharacters in .fc path"
rlLog "DOCUMENT: labeling validation deferred to sefcontext_compile"
rlRun "expand_fc ${FIXTURES}/file_contexts/bad_regex.fc ${OUTDIR}/b3_bad_regex.mod.fc"
rlRun "test -s ${OUTDIR}/b3_bad_regex.mod.fc"
rlPhaseEnd
rlPhaseStartTest "missing .fc path"
rlRun "expand_fc ${OUTDIR}/does_not_exist.fc ${OUTDIR}/b3_missing.mod.fc" 1-255
rlPhaseEnd
rlPhaseStartTest "directory instead of .fc file"
rlRun "expand_fc ${BASEDIR} ${OUTDIR}/b3_directory.mod.fc" 1-255
rlPhaseEnd
rlPhaseStartTest "broken symlink for .fc"
rlRun "expand_fc ${OUTDIR}/broken_symlink.fc ${OUTDIR}/b3_symlink.mod.fc" 1-255
rlPhaseEnd
rlPhaseStartTest "unreadable .fc file"
if [ "$(id -u)" -eq 0 ]; then
rlLog "SKIP: root can read mode 000 files; unreadable check is non-root only"
else
rlRun "cp ${FIXTURES}/file_contexts/good.fc ${OUTDIR}/unreadable.fc"
rlRun "chmod 000 ${OUTDIR}/unreadable.fc"
rlRun "expand_fc ${OUTDIR}/unreadable.fc ${OUTDIR}/unreadable_fc.mod.fc" 1-255
fi
rlPhaseEnd
rlPhaseStartTest "broken M4 syntax in .fc"
rlRun "expand_fc ${FIXTURES}/file_contexts/bad_m4_syntax.fc ${OUTDIR}/b3_bad_m4.mod.fc" 1-255
rlPhaseEnd
# --- M4-expanded .mod.fc through semodule_package (E2E; labeling deferred) ---
rlPhaseStartTest "bad M4-expanded .mod.fc packaged with good .mod"
rlLog "DOCUMENT: M4 to package E2E; labeling validation deferred to sefcontext_compile"
rlRun "mkdir -p ${OUTDIR}/b6_e2e_build"
rlRun "expand_module_te ${GOOD_IF} ${FIXTURES}/modules/te_good.te ${OUTDIR}/b6_e2e.tmp"
rlRun "${CHECKMODULE} -M -m -o ${OUTDIR}/b6_e2e_build/te_good.mod ${OUTDIR}/b6_e2e.tmp"
rlRun "expand_fc ${FIXTURES}/file_contexts/bad_context.fc ${OUTDIR}/b6_e2e.mod.fc"
rlRun "${SEMODULE_PACKAGE} -o ${OUTDIR}/b6_e2e.pp -m ${OUTDIR}/b6_e2e_build/te_good.mod -f ${OUTDIR}/b6_e2e.mod.fc"
rlRun "test -s ${OUTDIR}/b6_e2e.pp"
rlPhaseEnd
rlPhaseStartCleanup
if [[ "${POLICY_DIR}" == /var/tmp/selinux-policy-src.* ]]; then
rlRun "rm -rf ${POLICY_DIR}"
if [ -n "${OUTDIR:-}" ] && [ -d "${OUTDIR}" ]; then
rlRun "rm -rf ${OUTDIR}"
fi
if [ -n "${CLONED_POLICY_DIR:-}" ] && [ -d "${CLONED_POLICY_DIR}" ]; then
rlRun "rm -rf ${CLONED_POLICY_DIR}"
fi
rlPhaseEnd
rlJournalPrintText