Initialize module and dependencies
This commit is contained in:
246
vendor/golang.org/x/vuln/internal/goversion/read.go
generated
vendored
Normal file
246
vendor/golang.org/x/vuln/internal/goversion/read.go
generated
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package goversion reports the Go version used to build program executables.
|
||||
//
|
||||
// This is a copy of rsc.io/goversion/version. We renamed the package to goversion
|
||||
// to differentiate between version package in standard library that we also use.
|
||||
package goversion
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Version is the information reported by ReadExe.
|
||||
type Version struct {
|
||||
Release string // Go version (runtime.Version in the program)
|
||||
ModuleInfo string // program's module information
|
||||
BoringCrypto bool // program uses BoringCrypto
|
||||
StandardCrypto bool // program uses standard crypto (replaced by BoringCrypto)
|
||||
FIPSOnly bool // program imports "crypto/tls/fipsonly"
|
||||
}
|
||||
|
||||
// ReadExe reports information about the Go version used to build
|
||||
// the program executable named by file.
|
||||
func ReadExe(file string) (Version, error) {
|
||||
var v Version
|
||||
f, err := openExe(file)
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
defer f.Close()
|
||||
isGo := false
|
||||
for _, name := range f.SectionNames() {
|
||||
if name == ".note.go.buildid" {
|
||||
isGo = true
|
||||
}
|
||||
}
|
||||
syms, symsErr := f.Symbols()
|
||||
isGccgo := false
|
||||
for _, sym := range syms {
|
||||
name := sym.Name
|
||||
if name == "runtime.main" || name == "main.main" {
|
||||
isGo = true
|
||||
}
|
||||
if strings.HasPrefix(name, "runtime.") && strings.HasSuffix(name, "$descriptor") {
|
||||
isGccgo = true
|
||||
}
|
||||
if name == "runtime.buildVersion" {
|
||||
isGo = true
|
||||
release, err := readBuildVersion(f, sym.Addr, sym.Size)
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
v.Release = release
|
||||
}
|
||||
// Note: Using strings.HasPrefix because Go 1.17+ adds ".abi0" to many of these symbols.
|
||||
if strings.Contains(name, "_Cfunc__goboringcrypto_") || strings.HasPrefix(name, "crypto/internal/boring/sig.BoringCrypto") {
|
||||
v.BoringCrypto = true
|
||||
}
|
||||
if strings.HasPrefix(name, "crypto/internal/boring/sig.FIPSOnly") {
|
||||
v.FIPSOnly = true
|
||||
}
|
||||
for _, re := range standardCryptoNames {
|
||||
if re.MatchString(name) {
|
||||
v.StandardCrypto = true
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(name, "crypto/internal/boring/sig.StandardCrypto") {
|
||||
v.StandardCrypto = true
|
||||
}
|
||||
}
|
||||
|
||||
if DebugMatch {
|
||||
v.Release = ""
|
||||
}
|
||||
if err := findModuleInfo(&v, f); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if v.Release == "" {
|
||||
g, release := readBuildVersionX86Asm(f)
|
||||
if g {
|
||||
isGo = true
|
||||
v.Release = release
|
||||
if err := findCryptoSigs(&v, f); err != nil {
|
||||
return v, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if isGccgo && v.Release == "" {
|
||||
isGo = true
|
||||
v.Release = "gccgo (version unknown)"
|
||||
}
|
||||
if !isGo && symsErr != nil {
|
||||
return v, symsErr
|
||||
}
|
||||
|
||||
if !isGo {
|
||||
return v, errors.New("not a Go executable")
|
||||
}
|
||||
if v.Release == "" {
|
||||
v.Release = "unknown Go version"
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
var re = regexp.MustCompile
|
||||
|
||||
var standardCryptoNames = []*regexp.Regexp{
|
||||
re(`^crypto/sha1\.\(\*digest\)`),
|
||||
re(`^crypto/sha256\.\(\*digest\)`),
|
||||
re(`^crypto/rand\.\(\*devReader\)`),
|
||||
re(`^crypto/rsa\.encrypt(\.abi.)?$`),
|
||||
re(`^crypto/rsa\.decrypt(\.abi.)?$`),
|
||||
}
|
||||
|
||||
func readBuildVersion(f exe, addr, size uint64) (string, error) {
|
||||
if size == 0 {
|
||||
size = uint64(f.AddrSize() * 2)
|
||||
}
|
||||
if size != 8 && size != 16 {
|
||||
return "", fmt.Errorf("invalid size for runtime.buildVersion")
|
||||
}
|
||||
data, err := f.ReadData(addr, size)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("reading runtime.buildVersion: %v", err)
|
||||
}
|
||||
|
||||
if size == 8 {
|
||||
addr = uint64(f.ByteOrder().Uint32(data))
|
||||
size = uint64(f.ByteOrder().Uint32(data[4:]))
|
||||
} else {
|
||||
addr = f.ByteOrder().Uint64(data)
|
||||
size = f.ByteOrder().Uint64(data[8:])
|
||||
}
|
||||
if size > 1000 {
|
||||
return "", fmt.Errorf("implausible string size %d for runtime.buildVersion", size)
|
||||
}
|
||||
|
||||
data, err = f.ReadData(addr, size)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("reading runtime.buildVersion string data: %v", err)
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
// Code signatures that indicate BoringCrypto or crypto/internal/fipsonly.
|
||||
// These are not byte literals in order to avoid the actual
|
||||
// byte signatures appearing in the goversion binary,
|
||||
// because on some systems you can't tell rodata from text.
|
||||
var (
|
||||
sigBoringCrypto, _ = hex.DecodeString("EB1DF448F44BF4B332F52813A3B450D441CC2485F001454E92101B1D2F1950C3")
|
||||
sigStandardCrypto, _ = hex.DecodeString("EB1DF448F44BF4BAEE4DFA9851CA56A91145E83E99C59CF911CB8E80DAF12FC3")
|
||||
sigFIPSOnly, _ = hex.DecodeString("EB1DF448F44BF4363CB9CE9D68047D31F28D325D5CA5873F5D80CAF6D6151BC3")
|
||||
)
|
||||
|
||||
func findCryptoSigs(v *Version, f exe) error {
|
||||
const maxSigLen = 1 << 10
|
||||
start, end := f.TextRange()
|
||||
for addr := start; addr < end; {
|
||||
size := uint64(1 << 20)
|
||||
if end-addr < size {
|
||||
size = end - addr
|
||||
}
|
||||
data, err := f.ReadData(addr, size)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading text: %v", err)
|
||||
}
|
||||
if haveSig(data, sigBoringCrypto) {
|
||||
v.BoringCrypto = true
|
||||
}
|
||||
if haveSig(data, sigFIPSOnly) {
|
||||
v.FIPSOnly = true
|
||||
}
|
||||
if haveSig(data, sigStandardCrypto) {
|
||||
v.StandardCrypto = true
|
||||
}
|
||||
if addr+size < end {
|
||||
size -= maxSigLen
|
||||
}
|
||||
addr += size
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func haveSig(data, sig []byte) bool {
|
||||
const align = 16
|
||||
for {
|
||||
i := bytes.Index(data, sig)
|
||||
if i < 0 {
|
||||
return false
|
||||
}
|
||||
if i&(align-1) == 0 {
|
||||
return true
|
||||
}
|
||||
// Found unaligned match; unexpected but
|
||||
// skip to next aligned boundary and keep searching.
|
||||
data = data[(i+align-1)&^(align-1):]
|
||||
}
|
||||
}
|
||||
|
||||
func findModuleInfo(v *Version, f exe) error {
|
||||
const maxModInfo = 128 << 10
|
||||
start, end := f.RODataRange()
|
||||
for addr := start; addr < end; {
|
||||
size := uint64(4 << 20)
|
||||
if end-addr < size {
|
||||
size = end - addr
|
||||
}
|
||||
data, err := f.ReadData(addr, size)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading text: %v", err)
|
||||
}
|
||||
if haveModuleInfo(data, v) {
|
||||
return nil
|
||||
}
|
||||
if addr+size < end {
|
||||
size -= maxModInfo
|
||||
}
|
||||
addr += size
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
infoStart, _ = hex.DecodeString("3077af0c9274080241e1c107e6d618e6")
|
||||
infoEnd, _ = hex.DecodeString("f932433186182072008242104116d8f2")
|
||||
)
|
||||
|
||||
func haveModuleInfo(data []byte, v *Version) bool {
|
||||
i := bytes.Index(data, infoStart)
|
||||
if i < 0 {
|
||||
return false
|
||||
}
|
||||
j := bytes.Index(data[i:], infoEnd)
|
||||
if j < 0 {
|
||||
return false
|
||||
}
|
||||
v.ModuleInfo = string(data[i+len(infoStart) : i+j])
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user