Commit 765f2bf0 authored by Borislav Petkov's avatar Borislav Petkov Committed by Andrew Morton

scripts/decodecode: improve faulting line determination

There are cases where the IP pointer in a Code: line in an oops doesn't
point at the beginning of an instruction:

Code: 0f bd c2 e9 a0 cd b5 e4 48 0f bd c2 e9 97 cd b5 e4 0f 1f 80 00 00 00 00 \
	  e9 8b cd b5 e4 0f 1f 00 66 0f a3 d0 e9 7f cd b5 e4 0f 1f <80> 00 00 00 \
	  00 0f a3 d0 e9 70 cd b5 e4 48 0f a3 d0 e9 67 cd b5

  e9 7f cd b5 e4          jmp    0xffffffffe4b5cda8
  0f 1f 80 00 00 00 00    nopl   0x0(%rax)
	^^

and the current way of determining the faulting instruction line doesn't
work because disassembled instructions are counted from the IP byte to
the end and when that thing points in the middle, the trailing bytes can
be interpreted as different insns:

  Code starting with the faulting instruction
  ===========================================
     0:   80 00 00                addb   $0x0,(%rax)
     3:   00 00                   add    %al,(%rax)

whereas, this is part of

0f 1f 80 00 00 00 00    nopl   0x0(%rax)

     5:   0f a3 d0                bt     %edx,%eax
     ...

leading to:

  1d:   0f 1f 00                nopl   (%rax)
  20:   66 0f a3 d0             bt     %dx,%ax
  24:*  e9 7f cd b5 e4          jmp    0xffffffffe4b5cda8               <-- trapping instruction
  29:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)
  30:   0f a3 d0                bt     %edx,%eax

which is the wrong faulting instruction.

Change the way the faulting line number is determined by matching the
opcode bytes from the beginning, leading to correct output:

  1d:   0f 1f 00                nopl   (%rax)
  20:   66 0f a3 d0             bt     %dx,%ax
  24:   e9 7f cd b5 e4          jmp    0xffffffffe4b5cda8
  29:*  0f 1f 80 00 00 00 00    nopl   0x0(%rax)                <-- trapping instruction
  30:   0f a3 d0                bt     %edx,%eax

While at it, make decodecode use bash as the interpreter - that thing
should be present on everything by now. It simplifies the code a lot
too.

Link: https://lkml.kernel.org/r/20220808085928.29840-1-bp@alien8.deSigned-off-by: default avatarBorislav Petkov <bp@suse.de>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent 9f25f357
#!/bin/sh #!/bin/bash
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
# Disassemble the Code: line in Linux oopses # Disassemble the Code: line in Linux oopses
# usage: decodecode < oops.file # usage: decodecode < oops.file
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
# AFLAGS=--32 decodecode < 386.oops # AFLAGS=--32 decodecode < 386.oops
# PC=hex - the PC (program counter) the oops points to # PC=hex - the PC (program counter) the oops points to
faultlinenum=1
cleanup() { cleanup() {
rm -f $T $T.s $T.o $T.oo $T.aa $T.dis rm -f $T $T.s $T.o $T.oo $T.aa $T.dis
exit 1 exit 1
...@@ -102,28 +104,125 @@ disas() { ...@@ -102,28 +104,125 @@ disas() {
grep -v "/tmp\|Disassembly\|\.text\|^$" > $t.dis 2>&1 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"
# return if opcode bytes do not match @opline anymore
if ! echo $opline | grep -q "$substr";
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" "\<"` marker=`expr index "$code" "\<"`
if [ $marker -eq 0 ]; then if [ $marker -eq 0 ]; then
marker=`expr index "$code" "\("` marker=`expr index "$code" "\("`
fi fi
touch $T.oo touch $T.oo
if [ $marker -ne 0 ]; then if [ $marker -ne 0 ]; then
# 2 opcode bytes and a single space # How many bytes to subtract from the program counter
pc_sub=$(( $marker / 3 )) # 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 All code >> $T.oo
echo ======== >> $T.oo echo ======== >> $T.oo
beforemark=`echo "$code"` beforemark=`echo "$code"`
echo -n " .$type 0x" > $T.s echo -n " .$type 0x" > $T.s
echo $beforemark | sed -e 's/ /,0x/g; s/[<>()]//g' >> $T.s echo $beforemark | sed -e 's/ /,0x/g; s/[<>()]//g' >> $T.s
disas $T $pc_sub disas $T $pc_sub
cat $T.dis >> $T.oo cat $T.dis >> $T.oo
rm -f $T.o $T.s $T.dis
# and fix code at-and-after marker get_faultlinenum "$code" "$T.dis" $pc_sub
faultlinenum=$?
# and fix code at-and-after marker
code=`echo "$code" | cut -c$((${marker} + 1))-` code=`echo "$code" | cut -c$((${marker} + 1))-`
rm -f $T.o $T.s $T.dis
fi fi
echo Code starting with the faulting instruction > $T.aa echo Code starting with the faulting instruction > $T.aa
echo =========================================== >> $T.aa echo =========================================== >> $T.aa
code=`echo $code | sed -e 's/\r//;s/ [<(]/ /;s/[>)] / /;s/ /,0x/g; s/[>)]$//'` code=`echo $code | sed -e 's/\r//;s/ [<(]/ /;s/[>)] / /;s/ /,0x/g; s/[>)]$//'`
...@@ -132,15 +231,6 @@ echo $code >> $T.s ...@@ -132,15 +231,6 @@ echo $code >> $T.s
disas $T 0 disas $T 0
cat $T.dis >> $T.aa cat $T.dis >> $T.aa
# (lines of whole $T.oo) - (lines of $T.aa, i.e. "Code starting") + 3,
# i.e. the title + the "===..=" line (sed is counting from 1, 0 address is
# special)
faultlinenum=$(( $(wc -l $T.oo | cut -d" " -f1) - \
$(wc -l $T.aa | cut -d" " -f1) + 3))
faultline=`cat $T.dis | head -1 | cut -d":" -f2-`
faultline=`echo "$faultline" | sed -e 's/\[/\\\[/g; s/\]/\\\]/g'`
cat $T.oo | sed -e "${faultlinenum}s/^\([^:]*:\)\(.*\)/\1\*\2\t\t<-- trapping instruction/" cat $T.oo | sed -e "${faultlinenum}s/^\([^:]*:\)\(.*\)/\1\*\2\t\t<-- trapping instruction/"
echo echo
cat $T.aa cat $T.aa
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment