mirror of
https://github.com/golang/go
synced 2024-11-14 17:30:29 -07:00
runtime: properly frame panic values in tracebacks
This CL causes the printing of panic values to ensure that all newlines in the output are immediately followed by a tab, so that there is no way for a maliciously crafted panic value to fool a program attempting to parse the traceback into thinking that the panic value is in fact a goroutine stack. See https://github.com/golang/go/issues/64590#issuecomment-1932675696 + release note Updates #64590 Updates #63455 Change-Id: I5142acb777383c0c122779d984e73879567dc627 Reviewed-on: https://go-review.googlesource.com/c/go/+/581215 Auto-Submit: Alan Donovan <adonovan@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Pratt <mpratt@google.com>
This commit is contained in:
parent
4513f1a1c1
commit
69e75c8581
@ -1 +1,7 @@
|
||||
## Runtime {#runtime}
|
||||
|
||||
The traceback printed by the runtime after an unhandled panic or other
|
||||
fatal error now indents the second and subsequent lines of the error
|
||||
message (for example, the argument to panic) by a single tab, so that
|
||||
it can be unambiguously distinguished from the stack trace of the
|
||||
first goroutine. See [#64590](/issue/64590) for discussion.
|
||||
|
@ -966,11 +966,11 @@ func TestPanicWhilePanicking(t *testing.T) {
|
||||
Func string
|
||||
}{
|
||||
{
|
||||
"panic while printing panic value: important error message",
|
||||
"panic while printing panic value: important multi-line\n\terror message",
|
||||
"ErrorPanic",
|
||||
},
|
||||
{
|
||||
"panic while printing panic value: important stringer message",
|
||||
"panic while printing panic value: important multi-line\n\tstringer message",
|
||||
"StringerPanic",
|
||||
},
|
||||
{
|
||||
@ -986,7 +986,7 @@ func TestPanicWhilePanicking(t *testing.T) {
|
||||
"CircularPanic",
|
||||
},
|
||||
{
|
||||
"important string message",
|
||||
"important multi-line\n\tstring message",
|
||||
"StringPanic",
|
||||
},
|
||||
{
|
||||
|
@ -211,11 +211,16 @@ type stringer interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
// printany prints an argument passed to panic.
|
||||
// printpanicval prints an argument passed to panic.
|
||||
// If panic is called with a value that has a String or Error method,
|
||||
// it has already been converted into a string by preprintpanics.
|
||||
func printany(i any) {
|
||||
switch v := i.(type) {
|
||||
//
|
||||
// To ensure that the traceback can be unambiguously parsed even when
|
||||
// the panic value contains "\ngoroutine" and other stack-like
|
||||
// strings, newlines in the string representation of v are replaced by
|
||||
// "\n\t".
|
||||
func printpanicval(v any) {
|
||||
switch v := v.(type) {
|
||||
case nil:
|
||||
print("nil")
|
||||
case bool:
|
||||
@ -251,19 +256,22 @@ func printany(i any) {
|
||||
case complex128:
|
||||
print(v)
|
||||
case string:
|
||||
print(v)
|
||||
printindented(v)
|
||||
default:
|
||||
printanycustomtype(i)
|
||||
printanycustomtype(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Invariant: each newline in the string representation is followed by a tab.
|
||||
func printanycustomtype(i any) {
|
||||
eface := efaceOf(&i)
|
||||
typestring := toRType(eface._type).string()
|
||||
|
||||
switch eface._type.Kind_ {
|
||||
case abi.String:
|
||||
print(typestring, `("`, *(*string)(eface.data), `")`)
|
||||
print(typestring, `("`)
|
||||
printindented(*(*string)(eface.data))
|
||||
print(`")`)
|
||||
case abi.Bool:
|
||||
print(typestring, "(", *(*bool)(eface.data), ")")
|
||||
case abi.Int:
|
||||
@ -301,6 +309,21 @@ func printanycustomtype(i any) {
|
||||
}
|
||||
}
|
||||
|
||||
// printindented prints s, replacing "\n" with "\n\t".
|
||||
func printindented(s string) {
|
||||
for {
|
||||
i := bytealg.IndexByteString(s, '\n')
|
||||
if i < 0 {
|
||||
break
|
||||
}
|
||||
i += len("\n")
|
||||
print(s[:i])
|
||||
print("\t")
|
||||
s = s[i:]
|
||||
}
|
||||
print(s)
|
||||
}
|
||||
|
||||
// panicwrap generates a panic for a call to a wrapped value method
|
||||
// with a nil pointer receiver.
|
||||
//
|
||||
|
@ -656,7 +656,7 @@ func printpanics(p *_panic) {
|
||||
return
|
||||
}
|
||||
print("panic: ")
|
||||
printany(p.arg)
|
||||
printpanicval(p.arg)
|
||||
if p.recovered {
|
||||
print(" [recovered]")
|
||||
}
|
||||
@ -718,20 +718,20 @@ func gopanic(e any) {
|
||||
gp := getg()
|
||||
if gp.m.curg != gp {
|
||||
print("panic: ")
|
||||
printany(e)
|
||||
printpanicval(e)
|
||||
print("\n")
|
||||
throw("panic on system stack")
|
||||
}
|
||||
|
||||
if gp.m.mallocing != 0 {
|
||||
print("panic: ")
|
||||
printany(e)
|
||||
printpanicval(e)
|
||||
print("\n")
|
||||
throw("panic during malloc")
|
||||
}
|
||||
if gp.m.preemptoff != "" {
|
||||
print("panic: ")
|
||||
printany(e)
|
||||
printpanicval(e)
|
||||
print("\n")
|
||||
print("preempt off reason: ")
|
||||
print(gp.m.preemptoff)
|
||||
@ -740,7 +740,7 @@ func gopanic(e any) {
|
||||
}
|
||||
if gp.m.locks != 0 {
|
||||
print("panic: ")
|
||||
printany(e)
|
||||
printpanicval(e)
|
||||
print("\n")
|
||||
throw("panic holding locks")
|
||||
}
|
||||
@ -1015,7 +1015,9 @@ func throw(s string) {
|
||||
// Everything throw does should be recursively nosplit so it
|
||||
// can be called even when it's unsafe to grow the stack.
|
||||
systemstack(func() {
|
||||
print("fatal error: ", s, "\n")
|
||||
print("fatal error: ")
|
||||
printpanicval(s)
|
||||
print("\n")
|
||||
})
|
||||
|
||||
fatalthrow(throwTypeRuntime)
|
||||
@ -1034,7 +1036,9 @@ func fatal(s string) {
|
||||
// Everything fatal does should be recursively nosplit so it
|
||||
// can be called even when it's unsafe to grow the stack.
|
||||
systemstack(func() {
|
||||
print("fatal error: ", s, "\n")
|
||||
print("fatal error: ")
|
||||
printpanicval(s)
|
||||
print("\n")
|
||||
})
|
||||
|
||||
fatalthrow(throwTypeUser)
|
||||
|
@ -27,7 +27,7 @@ func TestPanicWithDirectlyPrintableCustomTypes(t *testing.T) {
|
||||
{"panicCustomInt16", `panic: main.MyInt16(93)`},
|
||||
{"panicCustomInt32", `panic: main.MyInt32(93)`},
|
||||
{"panicCustomInt64", `panic: main.MyInt64(93)`},
|
||||
{"panicCustomString", `panic: main.MyString("Panic")`},
|
||||
{"panicCustomString", `panic: main.MyString("Panic` + "\n\t" + `line two")`},
|
||||
{"panicCustomUint", `panic: main.MyUint(93)`},
|
||||
{"panicCustomUint8", `panic: main.MyUint8(93)`},
|
||||
{"panicCustomUint16", `panic: main.MyUint16(93)`},
|
||||
|
6
src/runtime/testdata/testprog/crash.go
vendored
6
src/runtime/testdata/testprog/crash.go
vendored
@ -77,7 +77,7 @@ func DoublePanic() {
|
||||
type exampleError struct{}
|
||||
|
||||
func (e exampleError) Error() string {
|
||||
panic("important error message")
|
||||
panic("important multi-line\nerror message")
|
||||
}
|
||||
|
||||
func ErrorPanic() {
|
||||
@ -97,7 +97,7 @@ func DoubleErrorPanic() {
|
||||
type exampleStringer struct{}
|
||||
|
||||
func (s exampleStringer) String() string {
|
||||
panic("important stringer message")
|
||||
panic("important multi-line\nstringer message")
|
||||
}
|
||||
|
||||
func StringerPanic() {
|
||||
@ -115,7 +115,7 @@ func DoubleStringerPanic() {
|
||||
}
|
||||
|
||||
func StringPanic() {
|
||||
panic("important string message")
|
||||
panic("important multi-line\nstring message")
|
||||
}
|
||||
|
||||
func NilPanic() {
|
||||
|
2
src/runtime/testdata/testprog/panicprint.go
vendored
2
src/runtime/testdata/testprog/panicprint.go
vendored
@ -31,7 +31,7 @@ func panicCustomComplex128() {
|
||||
}
|
||||
|
||||
func panicCustomString() {
|
||||
panic(MyString("Panic"))
|
||||
panic(MyString("Panic\nline two"))
|
||||
}
|
||||
|
||||
func panicCustomBool() {
|
||||
|
@ -762,7 +762,8 @@ func parseRunningTests(out []byte) (runningTests []string, ok bool) {
|
||||
inRunningTests := false
|
||||
for _, line := range strings.Split(string(out), "\n") {
|
||||
if inRunningTests {
|
||||
if trimmed, ok := strings.CutPrefix(line, "\t"); ok {
|
||||
// Package testing adds one tab, the panic printer adds another.
|
||||
if trimmed, ok := strings.CutPrefix(line, "\t\t"); ok {
|
||||
if name, _, ok := strings.Cut(trimmed, " "); ok {
|
||||
runningTests = append(runningTests, name)
|
||||
continue
|
||||
|
Loading…
Reference in New Issue
Block a user