]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: properly compute whether PC is inside vDSO pages
authorJason A. Donenfeld <Jason@zx2c4.com>
Wed, 25 Sep 2024 23:14:18 +0000 (01:14 +0200)
committerGopher Robot <gobot@golang.org>
Thu, 26 Sep 2024 16:51:09 +0000 (16:51 +0000)
The old calculation just looked whether PC was within a page of a vDSO
symbol. This doesn't work because the vDSO .text might span two whole
pages, with trampolines and such redirecting PC around between them.

This manifests itself with the new vDSO getrandom() function, where on
PowerPC, the trampoline is quite far away from the actual C function it
jumps into. The effect is that the signal handler doesn't know it's
interrupting a vDSO call and forgets to restore g to R30, resulting in a
crash.

Fix this by storing the start and end of the LOAD section from the
program headers. We could be more specific and parse out the .text
section, but PT_LOAD is good enough and appears to work well.

Change-Id: I3cf16955177eedb51e28b3b1a0191b32c3327a42
Reviewed-on: https://go-review.googlesource.com/c/go/+/616015
Auto-Submit: Jason Donenfeld <Jason@zx2c4.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
src/runtime/vdso_linux.go

index 4523615711614906f5b8d4fb1f2b2c81d5c9ca91..72b17ce4ac4efa61e2094ca32447d8cee53e1229 100644 (file)
@@ -97,6 +97,8 @@ type vdsoInfo struct {
        verdef *elfVerdef
 }
 
+var vdsoLoadStart, vdsoLoadEnd uintptr
+
 // see vdso_linux_*.go for vdsoSymbolKeys[] and vdso*Sym vars
 
 func vdsoInitFromSysinfoEhdr(info *vdsoInfo, hdr *elfEhdr) {
@@ -116,6 +118,8 @@ func vdsoInitFromSysinfoEhdr(info *vdsoInfo, hdr *elfEhdr) {
                        if !foundVaddr {
                                foundVaddr = true
                                info.loadOffset = info.loadAddr + uintptr(pt.p_offset-pt.p_vaddr)
+                               vdsoLoadStart = info.loadOffset
+                               vdsoLoadEnd = info.loadOffset + uintptr(pt.p_memsz)
                        }
 
                case _PT_DYNAMIC:
@@ -285,11 +289,5 @@ func vdsoauxv(tag, val uintptr) {
 //
 //go:nosplit
 func inVDSOPage(pc uintptr) bool {
-       for _, k := range vdsoSymbolKeys {
-               if *k.ptr != 0 {
-                       page := *k.ptr &^ (physPageSize - 1)
-                       return pc >= page && pc < page+physPageSize
-               }
-       }
-       return false
+       return pc >= vdsoLoadStart && pc < vdsoLoadEnd
 }