summaryrefslogtreecommitdiffstats
path: root/scripts/decodecode
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2026-09-02 09:55:01 -0400
committerSteven Rostedt <rostedt@goodmis.org>2026-09-02 11:08:21 -0400
commit4617721c502b2ddaa4e324e86da4997edf738fa5 (patch)
tree350aa83b47da0b574a814795392b20b0bed83cbc /scripts/decodecode
downloadlinux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.tar.gz
linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.zip
ftrace: Synchronize the initialization of ftrace_opsgrafted
There's some internal state that ftrace_ops needs to have set, but since it can be declared outside of the ftrace.c code, it calls ftrace_ops_init() on the ops in every global function. The issue is that if two tasks call it on the same ops at the same time it is possible to have the initialization of one corrupt the initialization of the other call. Create a ops_mutex to use to synchronize every initialization of the ftrace_ops. The mutex is taken within checking the ftrace_ops flag that states it was initializied but the flag is checked again after the mutex has been taken. Checking first outside the mutex allows it to shortcut having to take the mutex. But then the check needs to be done again after the mute is taken in case of races. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260902095501.6b59af20@gandalf.local.home Fixes: f04f24fb7e48d ("ftrace, kprobes: Fix a deadlock on ftrace_regex_lock") Reported-by: sashiko-bot@kernel.org Close: https://lore.kernel.org/all/20260829025528.49A831F000E9@smtp.kernel.org/ Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'scripts/decodecode')
-rwxr-xr-xscripts/decodecode251
1 files changed, 251 insertions, 0 deletions
diff --git a/scripts/decodecode b/scripts/decodecode
new file mode 100755
index 000000000..01d25dc11
--- /dev/null
+++ b/scripts/decodecode
@@ -0,0 +1,251 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Disassemble the Code: line in Linux oopses
+# usage: decodecode < oops.file
+#
+# options: set env. variable AFLAGS=options to pass options to "as";
+# e.g., to decode an i386 oops on an x86_64 system, use:
+# AFLAGS=--32 decodecode < 386.oops
+# PC=hex - the PC (program counter) the oops points to
+
+faultlinenum=1
+
+cleanup() {
+ rm -f $T $T.s $T.o $T.oo $T.aa $T.dis
+}
+
+die() {
+ echo "$@"
+ exit 1
+}
+
+trap cleanup EXIT
+
+T=`mktemp` || die "cannot create temp file"
+code=
+cont=
+
+while read i ; do
+
+case "$i" in
+*Code:*)
+ code=$i
+ cont=yes
+ ;;
+*)
+ [ -n "$cont" ] && {
+ xdump="$(echo $i | grep '^[[:xdigit:]<>[:space:]]\+$')"
+ if [ -n "$xdump" ]; then
+ code="$code $xdump"
+ else
+ cont=
+ fi
+ }
+ ;;
+esac
+
+done
+
+if [ -z "$code" ]; then
+ rm $T
+ die "Code line not found"
+fi
+
+echo $code
+code=`echo $code | sed -e 's/.*Code: //'`
+
+width=`expr index "$code" ' '`
+width=$((($width-1)/2))
+case $width in
+1) type=byte ;;
+2) type=2byte ;;
+4) type=4byte ;;
+esac
+
+if [ -z "$ARCH" ]; then
+ case `uname -m` in
+ aarch64*) ARCH=arm64 ;;
+ arm*) ARCH=arm ;;
+ loongarch*) ARCH=loongarch ;;
+ esac
+fi
+
+# Params: (tmp_file, pc_sub)
+disas() {
+ t=$1
+ pc_sub=$2
+
+ ${CROSS_COMPILE}as $AFLAGS -o $t.o $t.s > /dev/null 2>&1
+
+ if [ "$ARCH" = "arm" ]; then
+ if [ $width -eq 2 ]; then
+ OBJDUMPFLAGS="-M force-thumb"
+ fi
+
+ ${CROSS_COMPILE}strip $t.o
+ fi
+
+ if [ "$ARCH" = "arm64" ]; then
+ if [ $width -eq 4 ]; then
+ type=inst
+ fi
+
+ ${CROSS_COMPILE}strip $t.o
+ fi
+
+ if [ "$ARCH" = "riscv" ]; then
+ OBJDUMPFLAGS="-M no-aliases --section=.text -D"
+ ${CROSS_COMPILE}strip $t.o
+ fi
+
+ if [ "$ARCH" = "loongarch" ]; then
+ ${CROSS_COMPILE}strip $t.o
+ fi
+
+ if [ $pc_sub -ne 0 ]; then
+ if [ $PC ]; then
+ adj_vma=$(( $PC - $pc_sub ))
+ OBJDUMPFLAGS="$OBJDUMPFLAGS --adjust-vma=$adj_vma"
+ fi
+ fi
+
+ ${CROSS_COMPILE}objdump $OBJDUMPFLAGS -S $t.o | \
+ grep -v "/tmp\|Disassembly\|\.text\|^$" > $t.dis 2>&1
+}
+
+# Match the maximum number of opcode bytes from @op_bytes contained within
+# @opline
+#
+# Params:
+# @op_bytes: The string of bytes from the Code: line
+# @opline: The disassembled line coming from objdump
+#
+# Returns:
+# The max number of opcode bytes from the beginning of @op_bytes which match
+# the opcode bytes in the objdump line.
+get_substr_opcode_bytes_num()
+{
+ local op_bytes=$1
+ local opline=$2
+
+ local retval=0
+ substr=""
+
+ for opc in $op_bytes;
+ do
+ substr+="$opc"
+
+ opcode="$substr"
+ if [ "$ARCH" = "riscv" ]; then
+ opcode=$(echo $opcode | tr ' ' '\n' | tac | tr -d '\n')
+ fi
+
+ # return if opcode bytes do not match @opline anymore
+ if ! echo $opline | grep -q "$opcode";
+ then
+ break
+ fi
+
+ # add trailing space
+ substr+=" "
+ retval=$((retval+1))
+ done
+
+ return $retval
+}
+
+# Return the line number in objdump output to where the IP marker in the Code:
+# line points to
+#
+# Params:
+# @all_code: code in bytes without the marker
+# @dis_file: disassembled file
+# @ip_byte: The byte to which the IP points to
+get_faultlinenum()
+{
+ local all_code="$1"
+ local dis_file="$2"
+
+ # num bytes including IP byte
+ local num_bytes_ip=$(( $3 + 1 * $width ))
+
+ # Add the two header lines (we're counting from 1).
+ local retval=3
+
+ # remove marker
+ all_code=$(echo $all_code | sed -e 's/[<>()]//g')
+
+ while read line
+ do
+ get_substr_opcode_bytes_num "$all_code" "$line"
+ ate_opcodes=$?
+
+ if ! (( $ate_opcodes )); then
+ continue
+ fi
+
+ num_bytes_ip=$((num_bytes_ip - ($ate_opcodes * $width) ))
+ if (( $num_bytes_ip <= 0 )); then
+ break
+ fi
+
+ # Delete matched opcode bytes from all_code. For that, compute
+ # how many chars those opcodes are represented by and include
+ # trailing space.
+ #
+ # a byte is 2 chars, ate_opcodes is also the number of trailing
+ # spaces
+ del_chars=$(( ($ate_opcodes * $width * 2) + $ate_opcodes ))
+
+ all_code=$(echo $all_code | sed -e "s!^.\{$del_chars\}!!")
+
+ let "retval+=1"
+
+ done < $dis_file
+
+ return $retval
+}
+
+marker=`expr index "$code" "\<"`
+if [ $marker -eq 0 ]; then
+ marker=`expr index "$code" "\("`
+fi
+
+touch $T.oo
+if [ $marker -ne 0 ]; then
+ # How many bytes to subtract from the program counter
+ # in order to get to the beginning virtual address of the
+ # Code:
+ pc_sub=$(( (($marker - 1) / (2 * $width + 1)) * $width ))
+ echo All code >> $T.oo
+ echo ======== >> $T.oo
+ beforemark=`echo "$code"`
+ echo -n " .$type 0x" > $T.s
+
+ echo $beforemark | sed -e 's/ /,0x/g; s/[<>()]//g' >> $T.s
+
+ disas $T $pc_sub
+
+ cat $T.dis >> $T.oo
+
+ get_faultlinenum "$code" "$T.dis" $pc_sub
+ faultlinenum=$?
+
+ # and fix code at-and-after marker
+ code=`echo "$code" | cut -c$((${marker} + 1))-`
+
+ rm -f $T.o $T.s $T.dis
+fi
+
+echo Code starting with the faulting instruction > $T.aa
+echo =========================================== >> $T.aa
+code=`echo $code | sed -e 's/\r//;s/ [<(]/ /;s/[>)] / /;s/ /,0x/g; s/[>)]$//'`
+echo -n " .$type 0x" > $T.s
+echo $code >> $T.s
+disas $T 0
+cat $T.dis >> $T.aa
+
+cat $T.oo | sed -e "${faultlinenum}s/^\([^:]*:\)\(.*\)/\1\*\2\t\t<-- trapping instruction/"
+echo
+cat $T.aa
+cleanup