Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

html/template: CL 274450 (in go1.16beta1) causes panic #43295

Closed
stapelberg opened this issue Dec 20, 2020 · 11 comments
Closed

html/template: CL 274450 (in go1.16beta1) causes panic #43295

stapelberg opened this issue Dec 20, 2020 · 11 comments
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. release-blocker
Milestone

Comments

@stapelberg
Copy link
Contributor

What version of Go are you using (go version)?

$ go version
go version go1.16beta1 linux/amd64

Does this issue reproduce with the latest release?

Reproduces with Go tip

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/michael/.cache/go-build"
GOENV="/home/michael/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/michael/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/michael/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/michael/sdk/go1.16beta1"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/michael/sdk/go1.16beta1/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.16beta1"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/michael/upstream-go/src/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build3134978388=/tmp/go-build -gno-record-gcc-switches"

What did you do?

As soon as I try to evaluate this template:

	  {{ if .EEPROM }}
	  <tr>
	    <th>EEPROM<br>(SHA256)</th>
	    <td>{{ shortenSHA256 .EEPROM.PieepromSHA256 }}<br>{{ shortenSHA256 .EEPROM.VL805SHA256 }}</td>
	  </tr>
	  {{ end }}

where shortenSHA256 is defined as such:

	commonTmpls := template.New("root").Funcs(map[string]interface{}{
		"shortenSHA256": func(hash string) string {
			if len(hash) > 10 {
				return hash[:10]
			}
			return hash
		},
	})

and EEPROM is of type

type eepromVersion struct {
	PieepromSHA256 string // pieeprom.sig
	VL805SHA256    string // vl805.sig
}

I get a panic at runtime:

fatal error: sync: RUnlock of unlocked RWMutex

goroutine 43 [running]:
runtime.throw(0x3cd01c, 0x21)
	/home/michael/sdk/go1.16beta1/src/runtime/panic.go:1112 +0x54 fp=0x400004a740 sp=0x400004a710 pc=0x42654
sync.throw(0x3cd01c, 0x21)
	/home/michael/sdk/go1.16beta1/src/runtime/panic.go:1098 +0x30 fp=0x400004a760 sp=0x400004a740 pc=0x71110
sync.(*RWMutex).rUnlockSlow(0x40000acf00, 0xffffffff)
	/home/michael/sdk/go1.16beta1/src/sync/rwmutex.go:93 +0x40 fp=0x400004a790 sp=0x400004a760 pc=0x80a10
sync.(*RWMutex).RUnlock(...)
	/home/michael/sdk/go1.16beta1/src/sync/rwmutex.go:83
html/template.(*Template).wrapFuncs.func1(0x400009e3d8, 0x1, 0x1, 0x0, 0x0, 0x0)
	/home/michael/sdk/go1.16beta1/src/html/template/template.go:400 +0x1a4 fp=0x400004a810 sp=0x400004a790 pc=0x2c1e14
reflect.callReflect(0x4000091e30, 0x400004a998, 0x400004a970)
	/home/michael/sdk/go1.16beta1/src/reflect/value.go:565 +0x260 fp=0x400004a950 sp=0x400004a810 pc=0xe7530
reflect.makeFuncStub(0x400019c050, 0x40, 0x0, 0x0, 0x400004abc8, 0xe61e4, 0x4000091d10, 0x4000091e30, 0x40000b68a0, 0x1000000020, ...)
	/home/michael/sdk/go1.16beta1/src/reflect/asm_arm64.s:20 +0x30 fp=0x400004a990 sp=0x400004a950 pc=0xf0140
runtime.call32(0x4000091d10, 0x4000091e30, 0x40000b68a0)
	/home/michael/sdk/go1.16beta1/src/runtime/asm_arm64.s:432 +0x74 fp=0x400004a9c0 sp=0x400004a990 pc=0x73ba4
reflect.Value.call(0x3473c0, 0x4000091e30, 0x13, 0x3bb64c, 0x4, 0x400009e3c0, 0x1, 0x1, 0x400004ac70, 0x1, ...)
	/home/michael/sdk/go1.16beta1/src/reflect/value.go:476 +0x644 fp=0x400004abd0 sp=0x400004a9c0 pc=0xe6a24
reflect.Value.Call(0x3473c0, 0x4000091e30, 0x13, 0x400009e3c0, 0x1, 0x1, 0x0, 0x29969c, 0x400004acc8)
	/home/michael/sdk/go1.16beta1/src/reflect/value.go:337 +0x84 fp=0x400004ac50 sp=0x400004abd0 pc=0xe61e4
text/template.safeCall(0x3473c0, 0x4000091e30, 0x13, 0x400009e3c0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, ...)
	/home/michael/sdk/go1.16beta1/src/text/template/funcs.go:365 +0x84 fp=0x400004acd0 sp=0x400004ac50 pc=0x29ebf4
text/template.(*state).evalCall(0x400004b440, 0x3a8880, 0x4000093860, 0x99, 0x3473c0, 0x4000091e30, 0x13, 0x440430, 0x400011eab0, 0x40000cf0b8, ...)
	/home/michael/sdk/go1.16beta1/src/text/template/exec.go:725 +0x4f8 fp=0x400004ae00 sp=0x400004acd0 pc=0x299b18
text/template.(*state).evalFunction(0x400004b440, 0x3a8880, 0x4000093860, 0x99, 0x400011eae0, 0x440430, 0x400011eab0, 0x400013e0e0, 0x2, 0x2, ...)
	/home/michael/sdk/go1.16beta1/src/text/template/exec.go:580 +0xfc fp=0x400004aef0 sp=0x400004ae00 pc=0x298b1c
text/template.(*state).evalCommand(0x400004b440, 0x3a8880, 0x4000093860, 0x99, 0x400011eab0, 0x357f20, 0x6c2560, 0x99, 0x400004b0d8, 0x29722c, ...)
	/home/michael/sdk/go1.16beta1/src/text/template/exec.go:467 +0xd4 fp=0x400004afc0 sp=0x400004aef0 pc=0x2976b4
text/template.(*state).evalPipeline(0x400004b440, 0x3a8880, 0x4000093860, 0x99, 0x40001164e0, 0x0, 0x0, 0x99)
	/home/michael/sdk/go1.16beta1/src/text/template/exec.go:436 +0xec fp=0x400004b0c0 sp=0x400004afc0 pc=0x29722c
text/template.(*state).walk(0x400004b440, 0x3a8880, 0x4000093860, 0x99, 0x440310, 0x400011eb40)
	/home/michael/sdk/go1.16beta1/src/text/template/exec.go:255 +0x2e0 fp=0x400004b150 sp=0x400004b0c0 pc=0x295fa0
text/template.(*state).walk(0x400004b440, 0x3a8880, 0x4000093860, 0x99, 0x4405e0, 0x400011ea50)
	/home/michael/sdk/go1.16beta1/src/text/template/exec.go:264 +0x108 fp=0x400004b1e0 sp=0x400004b150 pc=0x295dc8
text/template.(*state).walkIfOrWith(0x400004b440, 0xa, 0x3a8880, 0x4000093860, 0x99, 0x4000116480, 0x400011ea50, 0x0)
	/home/michael/sdk/go1.16beta1/src/text/template/exec.go:294 +0x174 fp=0x400004b2a0 sp=0x400004b1e0 pc=0x296294
text/template.(*state).walk(0x400004b440, 0x3a8880, 0x4000093860, 0x99, 0x440598, 0x400013c300)
	/home/michael/sdk/go1.16beta1/src/text/template/exec.go:261 +0x218 fp=0x400004b330 sp=0x400004b2a0 pc=0x295ed8
text/template.(*state).walk(0x400004b440, 0x3a8880, 0x4000093860, 0x99, 0x4405e0, 0x400011e480)
	/home/michael/sdk/go1.16beta1/src/text/template/exec.go:264 +0x108 fp=0x400004b3c0 sp=0x400004b330 pc=0x295dc8
text/template.(*state).walkTemplate(0x400004b630, 0x3a8880, 0x4000093860, 0x99, 0x400013c380)
	/home/michael/sdk/go1.16beta1/src/text/template/exec.go:418 +0x1b4 fp=0x400004b490 sp=0x400004b3c0 pc=0x297004
text/template.(*state).walk(0x400004b630, 0x3a8880, 0x4000093860, 0x99, 0x440790, 0x400013c380)
	/home/michael/sdk/go1.16beta1/src/text/template/exec.go:269 +0x1b0 fp=0x400004b520 sp=0x400004b490 pc=0x295e70
text/template.(*state).walk(0x400004b630, 0x3a8880, 0x4000093860, 0x99, 0x4405e0, 0x400011f3e0)
	/home/michael/sdk/go1.16beta1/src/text/template/exec.go:264 +0x108 fp=0x400004b5b0 sp=0x400004b520 pc=0x295dc8
text/template.(*Template).execute(0x400013c340, 0x438420, 0x40001a38c0, 0x3a8880, 0x4000093860, 0x0, 0x0)
	/home/michael/sdk/go1.16beta1/src/text/template/exec.go:220 +0x148 fp=0x400004b690 sp=0x400004b5b0 pc=0x295af8
text/template.(*Template).Execute(...)
	/home/michael/sdk/go1.16beta1/src/text/template/exec.go:203
html/template.(*Template).Execute(0x400011ed80, 0x438420, 0x40001a38c0, 0x3a8880, 0x4000093860, 0x0, 0x0)
	/home/michael/sdk/go1.16beta1/src/html/template/template.go:139 +0xc4 fp=0x400004b6f0 sp=0x400004b690 pc=0x2bd4f4
github.com/gokrazy/gokrazy.initStatus.func10(0x43e2c0, 0x40001ba000, 0x4000196300)
	/home/michael/go/src/github.com/gokrazy/gokrazy/status.go:345 +0x464 fp=0x400004ba10 sp=0x400004b6f0 pc=0x306354
net/http.HandlerFunc.ServeHTTP(0x40001140a0, 0x43e2c0, 0x40001ba000, 0x4000196300)
	/home/michael/sdk/go1.16beta1/src/net/http/server.go:2069 +0x40 fp=0x400004ba40 sp=0x400004ba10 pc=0x249db0
net/http.(*ServeMux).ServeHTTP(0x693940, 0x43e2c0, 0x40001ba000, 0x4000196300)
	/home/michael/sdk/go1.16beta1/src/net/http/server.go:2448 +0x190 fp=0x400004baa0 sp=0x400004ba40 pc=0x24b730
github.com/gokrazy/gokrazy.authenticated(0x43e2c0, 0x40001ba000, 0x4000196300)
	/home/michael/go/src/github.com/gokrazy/gokrazy/authenticated.go:37 +0x3b0 fp=0x400004bb30 sp=0x400004baa0 pc=0x2fc1a0
net/http.HandlerFunc.ServeHTTP(0x3deb88, 0x43e2c0, 0x40001ba000, 0x4000196300)
	/home/michael/sdk/go1.16beta1/src/net/http/server.go:2069 +0x40 fp=0x400004bb60 sp=0x400004bb30 pc=0x249db0
net/http.serverHandler.ServeHTTP(0x400021e1c0, 0x43e2c0, 0x40001ba000, 0x4000196300)
	/home/michael/sdk/go1.16beta1/src/net/http/server.go:2887 +0xbc fp=0x400004bb90 sp=0x400004bb60 pc=0x24cc0c
net/http.(*conn).serve(0x40002023c0, 0x43ed00, 0x40000a2780)
	/home/michael/sdk/go1.16beta1/src/net/http/server.go:1952 +0x72c fp=0x400004bfc0 sp=0x400004bb90 pc=0x248bdc
runtime.goexit()
	/home/michael/sdk/go1.16beta1/src/runtime/asm_arm64.s:1130 +0x4 fp=0x400004bfc0 sp=0x400004bfc0 pc=0x75a34
created by net/http.(*Server).Serve
	/home/michael/sdk/go1.16beta1/src/net/http/server.go:3013 +0x318

The full panic is at https://gist.github.com/stapelberg/b804f2e0582b34b31ae069bfc36304ab in case it’s relevant.

I tracked this problem down to commit 5a4db10. Once I revert that commit, I no longer get the panic.

cc @ianlancetaylor

@ianlancetaylor
Copy link
Contributor

Can you tell us how we can recreate the problem ourselves? Thanks.

It looks like somehow html/template.Execute is running a function that is associated with a different nameSpace.

@ianlancetaylor ianlancetaylor changed the title html/template: commit 5a4db102b21489c39b3a654e06cc25155432a38a (in go1.16beta1) causes panic html/template: CL 274450 (in go1.16beta1) causes panic Dec 21, 2020
@ianlancetaylor ianlancetaylor added NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. release-blocker labels Dec 21, 2020
@ianlancetaylor ianlancetaylor added this to the Go1.16 milestone Dec 21, 2020
@choleraehyq
Copy link
Contributor

Try to write a reproducer according to your description, but fail to reproduce.

package main

import (
	"bytes"
	"fmt"
	"html/template"
)

type eepromVersion struct {
	PieepromSHA256 string // pieeprom.sig
	VL805SHA256    string // vl805.sig
}

type A struct {
	EEPROM *eepromVersion
}

func main() {
	s := `
{{ if .EEPROM }}
	  <tr>
	    <th>EEPROM<br>(SHA256)</th>
	    <td>{{ shortenSHA256 .EEPROM.PieepromSHA256 }}<br>{{ shortenSHA256 .EEPROM.VL805SHA256 }}</td>
	  </tr>
	  {{ end }}
`
	e := eepromVersion{
		"pieeprom.sig",
		"vl805.sig",
	}
	a := A{
		EEPROM: &e,
	}
	commonTmpls, err := template.New("root").Funcs(map[string]interface{}{
		"shortenSHA256": func(hash string) string {
			if len(hash) > 10 {
				return hash[:10]
			}
			return hash
		},
	}).Parse(s)
	if err != nil {
		panic(err)
	}
	out := &bytes.Buffer{}
	if err := commonTmpls.Execute(out, a); err != nil {
		panic(err)
	}
	fmt.Println(out.String())
}

@stapelberg
Copy link
Contributor Author

I’ll try to extract a reproduction case today

@stapelberg
Copy link
Contributor Author

Got a standalone reproduction case:

package main

import (
	"bytes"
	"fmt"
	"html/template"
)

type eepromVersion struct {
	PieepromSHA256 string // pieeprom.sig
	VL805SHA256    string // vl805.sig
}

type A struct {
	EEPROM *eepromVersion
}

func main() {

	s := `
{{ if .EEPROM }}
	  <tr>
	    <th>EEPROM<br>(SHA256)</th>
	    <td>{{ shortenSHA256 .EEPROM.PieepromSHA256 }}<br>{{ shortenSHA256 .EEPROM.VL805SHA256 }}</td>
	  </tr>
	  {{ end }}
`
	e := eepromVersion{
		"pieeprom.sig",
		"vl805.sig",
	}
	a := A{
		EEPROM: &e,
	}
	commonTmpls := template.New("root").Funcs(map[string]interface{}{
		"shortenSHA256": func(hash string) string {
			return hash
		},
	})
	commonTmpls = template.Must(commonTmpls.New("header").Parse("head"))

	overviewTmpl := template.Must(template.Must(commonTmpls.Clone()).New("overview").Parse(s))
	out := &bytes.Buffer{}
	if err := overviewTmpl.Execute(out, a); err != nil {
		panic(err)
	}
	fmt.Println(out.String())
}

Note that parsing the header template, and calling the shortenSHA256 function are both required to reproduce the bug.

This also reproduces on linux/amd64.

@gopherbot
Copy link

Change https://golang.org/cl/279432 mentions this issue: html/template: inherit funcMap when template.New to rebuild wrapped functions when template.Clone

@gopherbot
Copy link

Change https://golang.org/cl/279492 mentions this issue: html/template: attach wrapped functions to namespace

@ianlancetaylor
Copy link
Contributor

@stapelberg Thanks for the reproduction case. I think that https://golang.org/cl/279492 fixes the problem. It does fix your test case, at least. When you have time, give it a try on your original code. Thanks.

@stapelberg
Copy link
Contributor Author

Thanks for the quick fix!

I just got a chance to test it in my original environment, and I hereby confirm that your https://golang.org/cl/279492 indeed does fix the problem for me.

@toothrot
Copy link
Contributor

toothrot commented Jan 7, 2021

Closing, this appears to be fixed. Please let me know if I am mistaken.

@toothrot toothrot closed this as completed Jan 7, 2021
@stapelberg
Copy link
Contributor Author

No, https://go-review.googlesource.com/c/go/+/279492/ has not yet been submitted. This isn’t fixed yet.

@toothrot toothrot reopened this Jan 7, 2021
@toothrot
Copy link
Contributor

toothrot commented Jan 7, 2021

Thanks, I was mistaken!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. release-blocker
Projects
None yet
Development

No branches or pull requests

6 participants
@toothrot @stapelberg @ianlancetaylor @gopherbot @choleraehyq and others