345 lines
8.9 KiB
Bash
Executable File
345 lines
8.9 KiB
Bash
Executable File
#!/bin/bash
|
|
# test_lkm
|
|
# Test loadable kernel module(s) (LKM) from the given directory, recursively
|
|
# NOTE:
|
|
# - we ASSUME a file named Makefile will be present within the LKM dir
|
|
# - currently, there's no automated way to pass module parameters
|
|
# - nice bash colo[u]r values can be found here:
|
|
# https://gist.github.com/acook/1199000
|
|
|
|
# (c) Kaiwan NB, kaiwanTECH
|
|
# License: MIT
|
|
|
|
# Turn on Bash 'strict mode'! V useful
|
|
# "Convert many kinds of hidden, intermittent, or subtle bugs into immediate, glaringly obvious errors"
|
|
# ref: http://redsymbol.net/articles/unofficial-bash-strict-mode/
|
|
set -euo pipefail
|
|
|
|
name=$(basename $0)
|
|
ECHO_PFX=">>>"
|
|
|
|
DEBUG=0
|
|
decho()
|
|
{
|
|
[[ ${DEBUG} -eq 0 ]] && return
|
|
echo "$*"
|
|
}
|
|
|
|
QUIET=0
|
|
|
|
SUCCESS_MSG="^^^ SUCCESS ^^^"
|
|
WARNING_BUILD_MSG="
|
|
### WARNING(s) during build; must check ###
|
|
"
|
|
FAIL_MSG="
|
|
*** *** *** *** *** *** *** FAIL *** *** *** *** *** *** *** ***
|
|
"
|
|
MAKEFILE_FAIL_MSG="The Makefile doesn't have targets corresponding to the 'better' Makefile
|
|
(https://github.com/PacktPublishing/Linux-Kernel-Programming_2E/blob/main/ch6/foreach/prcs_showall/Makefile)"
|
|
|
|
failit()
|
|
{
|
|
# highlight the failure msg in red bg white fg!
|
|
echo -e "\e[1m\e[41m${FAIL_MSG} \e[0m" >&2
|
|
echo "message: $*" >&2
|
|
return 1
|
|
}
|
|
|
|
#----------------------------------------------------------------------
|
|
LINE="-------------------------------------------------------------------------"
|
|
CHECKPATCH=/lib/modules/$(uname -r)/build/scripts/checkpatch.pl
|
|
OPEN_IN_ED=0
|
|
ED=gedit
|
|
|
|
TMP_CHECKPATCH=/tmp/tmp_checkpatch
|
|
TMP_FLAWFINDER=/tmp/tmp_flawfinder
|
|
TMP_CPPCHECK=/tmp/tmp_cppcheck
|
|
TMP_SPARSE=/tmp/tmp_sparse
|
|
TMP_SGCC=/tmp/tmp_sgcc
|
|
|
|
# 'better' Makefile?
|
|
verify_makefile()
|
|
{
|
|
local okay=1
|
|
[[ ! -f Makefile ]] && {
|
|
echo "no Makefile, skipping..."
|
|
return 1
|
|
}
|
|
grep -w "^install:" Makefile >/dev/null || okay=0
|
|
grep -w "^code-style:" Makefile >/dev/null || okay=0
|
|
grep -w "^checkpatch:" Makefile >/dev/null || okay=0
|
|
grep -w "^sa:" Makefile >/dev/null || okay=0
|
|
grep -w "^sa_flawfinder:" Makefile >/dev/null || okay=0
|
|
grep -w "^sa_cppcheck:" Makefile >/dev/null || okay=0
|
|
grep -w "^sa_sparse:" Makefile >/dev/null || okay=0
|
|
grep -w "^help:" Makefile >/dev/null || okay=0
|
|
[[ ${okay} -eq 1 ]] && return 0 || return 2
|
|
}
|
|
|
|
lkm_static_analysis_checks()
|
|
{
|
|
make clean >/dev/null 2>&1
|
|
|
|
local SRC=$(ls *.c 2>/dev/null)
|
|
local SRC_NOEXT=${SRC::-2}
|
|
decho "SRC=${SRC} SRC_NOEXT=${SRC_NOEXT}"
|
|
|
|
chmod 0644 ${SRC} Makefile
|
|
[ ${OPEN_IN_ED} -eq 1 ] && ${ED} ${SRC} Makefile
|
|
|
|
#echo ${LINE}
|
|
grep -q "MODULE_LICENSE(.*)" ${SRC} || echo "${ECHO_PFX} *** NO license! ***" && \
|
|
echo "${ECHO_PFX} License present"
|
|
|
|
if [[ ${QUIET} -eq 1 ]] ; then
|
|
make checkpatch > ${TMP_CHECKPATCH} 2>&1 || true
|
|
else
|
|
make checkpatch 2>&1 |tee ${TMP_CHECKPATCH} || true
|
|
fi
|
|
local chkpatch_err=0
|
|
chkpatch_err=$(grep -w "^ERROR:" ${TMP_CHECKPATCH} |wc -l) || chkpatch_err=0
|
|
local chkpatch_warns=0
|
|
chkpatch_warns=$(grep -w "^WARNING:" ${TMP_CHECKPATCH} |wc -l) || chkpatch_warns=0
|
|
(
|
|
set +e # Bash strict mode side effects
|
|
grep "pr_fmt(.*)" ${SRC} >/dev/null
|
|
if [ $? -ne 0 ]; then
|
|
echo "${ECHO_PFX} ### pr_fmt() macro missing! ###"
|
|
else
|
|
echo "${ECHO_PFX} pr_fmt() macro present"
|
|
fi
|
|
set -e
|
|
echo "${ECHO_PFX} checkpatch: errors=${chkpatch_err} warnings=${chkpatch_warns}"
|
|
#echo ${LINE}
|
|
)
|
|
|
|
#--- --- Static analysis --- ---
|
|
echo "-------------------- Static analysis --------------------"
|
|
if [[ ${QUIET} -eq 1 ]] ; then
|
|
make sa_flawfinder > ${TMP_FLAWFINDER} 2>&1 || true
|
|
else
|
|
make sa_flawfinder 2>&1 | tee ${TMP_FLAWFINDER} || true
|
|
fi
|
|
local flawfinder_hits=0
|
|
flawfinder_hits=$(grep "^Hits =" ${TMP_FLAWFINDER} |cut -d'=' -f2) || flawfinder_hits=0
|
|
grep -q "No hits found" ${TMP_FLAWFINDER} && flawfinder_hits=0
|
|
echo "${ECHO_PFX} flawfinder_hits = ${flawfinder_hits}" # | tee -a ${RESFILE}
|
|
|
|
#--- cppcheck
|
|
if [[ ${QUIET} -eq 1 ]] ; then
|
|
make sa_cppcheck > ${TMP_CPPCHECK} 2>&1 || true
|
|
else
|
|
make sa_cppcheck 2>&1 | tee ${TMP_CPPCHECK} || true
|
|
fi
|
|
local cppcheck_hits=0
|
|
cppcheck_hits=$(grep "\^" ${TMP_CPPCHECK} |wc -l) || cppcheck_hits=0
|
|
echo "${ECHO_PFX} cppcheck_hits = ${cppcheck_hits}" #| tee -a ${RESFILE}
|
|
|
|
#--- sparse
|
|
if [[ ${QUIET} -eq 1 ]] ; then
|
|
make sa_sparse > ${TMP_SPARSE} 2>&1 || true
|
|
else
|
|
make sa_sparse 2>&1 | tee ${TMP_SPARSE} || true
|
|
fi
|
|
local sparse_err=0
|
|
sparse_err=$(grep -w "error:" ${TMP_SPARSE} |wc -l) || sparse_err=0
|
|
local sparse_warns=0
|
|
sparse_warns=$(grep -w "warning:" ${TMP_SPARSE} |wc -l) || sparse_warns=0
|
|
echo "${ECHO_PFX} sparse: errors=${sparse_err} warnings=${sparse_warns}" #| tee -a ${RESFILE}
|
|
|
|
#--- (static analysis) gcc
|
|
if [[ ${QUIET} -eq 1 ]] ; then
|
|
make sa_gcc > ${TMP_SGCC} 2>&1 || true
|
|
else
|
|
make sa_gcc 2>&1 | tee ${TMP_SGCC} || true
|
|
fi
|
|
local sgcc_err=0
|
|
sgcc_err=$(grep -w "error:" ${TMP_SGCC} |wc -l) || sgcc_err=0
|
|
local sgcc_warns=0
|
|
sgcc_warns=$(grep -w "warning:" ${TMP_SGCC} |wc -l) || sgcc_warns=0
|
|
echo "${ECHO_PFX} (static)gcc: errors=${sgcc_err} warnings=${sgcc_warns}" #| tee -a ${RESFILE}
|
|
#echo ${LINE}
|
|
|
|
rm -f ${TMP_CHECKPATCH} ${TMP_FLAWFINDER} ${TMP_CPPCHECK} ${TMP_SPARSE} ${TMP_SGCC}
|
|
|
|
[ 0 -eq 1 ] && {
|
|
echo "---Static analysis with Coccinelle---"
|
|
coccichk ${SRC}
|
|
echo ${LINE}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
|
build_lkm()
|
|
{
|
|
echo "----------------------- LKM Build -----------------------"
|
|
local TMP_BUILD=.tmp_build
|
|
if [[ ${QUIET} -eq 1 ]] ; then
|
|
decho "make clean ; make"
|
|
make clean >/dev/null 2>&1
|
|
make >${TMP_BUILD} 2>&1
|
|
else
|
|
make clean
|
|
make 2>&1 |tee ${TMP_BUILD}
|
|
fi
|
|
|
|
local make_err=0
|
|
make_err=$(grep -w "error:" ${TMP_BUILD} |wc -l) || make_err=0
|
|
local make_warns=0
|
|
make_warns=$(grep -w "warning:" ${TMP_BUILD} |wc -l) || make_warns=0
|
|
echo "${ECHO_PFX} make: errors=${make_err} warnings=${make_warns}" #| tee -a ${RESFILE}
|
|
[[ ${make_err} -ge 1 ]] && {
|
|
failit "LKM build failed"
|
|
echo ${LINE}
|
|
return 1
|
|
}
|
|
#echo ${LINE}
|
|
|
|
grep "warning:" ${TMP_BUILD} && echo -e "\e[1m\e[1;33m${WARNING_BUILD_MSG} \e[0m"
|
|
#cat ${TMP_BUILD}
|
|
#rm -f ${TMP_BUILD}
|
|
return 0
|
|
}
|
|
|
|
# No intelligence... simply performs insmod, rmmmod
|
|
# -no module params, checking of o/p
|
|
insmod_rmmod_lkm()
|
|
{
|
|
echo "-------------------- LKM insmod+rmmod -------------------"
|
|
[[ $(ls *.ko 2>/dev/null) ]] || {
|
|
make clean >/dev/null 2>&1
|
|
echo "<<< No LKM .ko found? (likely it's usermode code here); skipping... >>>"
|
|
return 2
|
|
}
|
|
local LKM_KO=$(basename $(ls *.ko))
|
|
local LKM=${LKM_KO::-3} # get rid of the trailing .ko
|
|
[[ -z "${LKM}" ]] && {
|
|
make clean >/dev/null 2>&1
|
|
failit "couldn't get module name"
|
|
return 1
|
|
}
|
|
sudo rmmod ${LKM} 2>/dev/null || true
|
|
sudo dmesg -C
|
|
sudo insmod ${LKM_KO} || {
|
|
make clean >/dev/null 2>&1
|
|
failit "insmod ${LKM_KO} failed"
|
|
[[ ${QUIET} -eq 0 ]] && sudo dmesg
|
|
return 1
|
|
}
|
|
lsmod|grep ${LKM}
|
|
sudo rmmod ${LKM} 2>/dev/null
|
|
[[ ${QUIET} -eq 1 ]] && sudo dmesg -C || sudo dmesg -c
|
|
make clean >/dev/null 2>&1
|
|
|
|
echo -e "\e[1m\e[42m${SUCCESS_MSG} \e[0m" >&2
|
|
#echo "${SUCCESS_MSG}"
|
|
return 0
|
|
}
|
|
|
|
# Parameter(s):
|
|
# $1 : directory containing the LKM
|
|
# Returns:
|
|
# 0 on success
|
|
# non-zero on failure
|
|
testlkm()
|
|
{
|
|
cd $1 || {
|
|
failit "cd to $1"
|
|
return 1
|
|
}
|
|
|
|
local BETTER_MAKEFILE_OK=1
|
|
verify_makefile
|
|
local _ret=$?
|
|
[[ ${_ret} -ne 0 ]] && {
|
|
echo "*** 'better' Makefile verification failed ***"
|
|
[[ ${_ret} -eq 2 ]] && echo "${MAKEFILE_FAIL_MSG}"
|
|
BETTER_MAKEFILE_OK=0
|
|
}
|
|
|
|
if [[ ${BETTER_MAKEFILE_OK} -eq 1 ]]; then
|
|
lkm_static_analysis_checks
|
|
[[ $? -ne 0 ]] && {
|
|
echo "*** lkm_static_analysis_checks() failed ***"
|
|
return 1
|
|
}
|
|
else
|
|
echo "${ECHO_PFX} No 'better' Makefile, so skipping LKM static analysis checks"
|
|
fi
|
|
|
|
build_lkm
|
|
[[ $? -ne 0 ]] && {
|
|
# echo "*** build_lkm() failed ***"
|
|
return 1
|
|
}
|
|
# Ok, the LKM build is fine; insmod & rmmod it now
|
|
insmod_rmmod_lkm
|
|
[[ $? -ne 0 ]] && {
|
|
# echo "*** insmod_rmmod_lkm() failed ***"
|
|
return 1
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
usage()
|
|
{
|
|
echo -n "Usage: ${name} [-q|-h] starting-dir
|
|
Parameters:
|
|
starting-dir : test all kernel modules starting from this directory, recursively (mandatory)
|
|
[Optional]:
|
|
[-q] : run in quiet mode (default: "
|
|
[[ ${QUIET} -eq 1 ]] && echo "ON)" || echo "OFF)"
|
|
echo " [-h] : show this help screen"
|
|
echo "Expects usage of the 'better' Makefile (but will work regardless)"
|
|
}
|
|
|
|
|
|
#--- 'main'
|
|
[[ $# -eq 0 ]] && {
|
|
usage ; exit 1
|
|
}
|
|
if [[ $# -eq 1 ]] ; then
|
|
[[ "$1" = "-h" ]] && {
|
|
usage ; exit 0
|
|
}
|
|
[[ "$1" = "-q" ]] && { # only '-q' isn't ok
|
|
usage ; exit 1
|
|
}
|
|
LKM_DIR=$1
|
|
elif [[ $# -eq 2 ]] ; then
|
|
[[ "$1" = "-q" ]] && QUIET=1
|
|
LKM_DIR=$2
|
|
fi
|
|
|
|
decho "LKM_DIR=${LKM_DIR}, quiet=${QUIET}"
|
|
[[ ${QUIET} -eq 0 ]] && {
|
|
echo "FYI, can pass -q to run in quiet mode"
|
|
ECHO_PFX="
|
|
${ECHO_PFX}"
|
|
}
|
|
|
|
i=1
|
|
TOP=$(pwd)
|
|
for KDIR in $(find ${LKM_DIR}/ -type d)
|
|
do
|
|
[[ "${KDIR}" == *".git"* || "${KDIR}" == "." ]] && continue || true
|
|
#--- special case!
|
|
[[ "${KDIR}" == *"3_lockdep"* || "${KDIR}" == "." ]] && continue || true
|
|
|
|
if [[ ! -f ${TOP}/${KDIR}/Makefile ]] ; then
|
|
[[ ${QUIET} -eq 0 ]] && echo "<no Makefile in ${KDIR}, skipping..>"
|
|
continue
|
|
fi
|
|
[[ $i -ne 1 ]] && echo
|
|
#echo -e "\e[1m\e[41m${FAIL_MSG} \e[0m" >&2
|
|
echo -e "\e[1m\e[43m====================== $i: LKM ${KDIR} ====================== \e[0m"
|
|
cd ${TOP}
|
|
testlkm ${KDIR} || true
|
|
let i=i+1
|
|
done
|
|
exit 0
|