Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de7123beea | ||
|
|
5abfd5beda | ||
|
|
fe78de3945 | ||
|
|
86acb268cd |
18
README.md
18
README.md
@@ -43,15 +43,15 @@ The Windows binary provided here also supports Windows 7 and Windows Server 2008
|
||||
|
||||
| OS | Architecture | Filename | SHA‑256 Hash |
|
||||
|----|--------------|----------|--------------|
|
||||
| **macOS** | Intel (amd64) | [go-legacy-win7-1.25.1-2.darwin_amd64.tar.gz](https://github.com/thongtech/go-legacy-win7/releases/download/v1.25.1-2/go-legacy-win7-1.25.1-2.darwin_amd64.tar.gz) | `e71c71a063f734d5154d16c33d55e92d332ddfa8487369c3f76b297056f739bc` |
|
||||
| macOS | Apple (ARM64) | [go-legacy-win7-1.25.1-2.darwin_arm64.tar.gz](https://github.com/thongtech/go-legacy-win7/releases/download/v1.25.1-2/go-legacy-win7-1.25.1-2.darwin_arm64.tar.gz) | `25b0f28307aeb20a8a9cdebb6f360de00db98bde0ef5b7cc7a75556c0dfdf488` |
|
||||
| **Linux** | x86 (386) | [go-legacy-win7-1.25.1-2.linux_386.tar.gz](https://github.com/thongtech/go-legacy-win7/releases/download/v1.25.1-2/go-legacy-win7-1.25.1-2.linux_386.tar.gz) | `a9751b2a55ef4d85aa8b113fac74e2a7ef564866ff5d30df479efd230455acfc` |
|
||||
| Linux | x64 (amd64) | [go-legacy-win7-1.25.1-2.linux_amd64.tar.gz](https://github.com/thongtech/go-legacy-win7/releases/download/v1.25.1-2/go-legacy-win7-1.25.1-2.linux_amd64.tar.gz) | `811c0c35afd64d290f74c5fa1ffaf203032cc9f5ca264a9b5405d4e51a952e67` |
|
||||
| Linux | ARM (32‑bit) | [go-legacy-win7-1.25.1-2.linux_arm.tar.gz](https://github.com/thongtech/go-legacy-win7/releases/download/v1.25.1-2/go-legacy-win7-1.25.1-2.linux_arm.tar.gz) | `d18912582ddae96dd38c0a37b0a4b6e9ad94035f4768c4b0fa24f5122d549edb` |
|
||||
| Linux | ARM64 | [go-legacy-win7-1.25.1-2.linux_arm64.tar.gz](https://github.com/thongtech/go-legacy-win7/releases/download/v1.25.1-2/go-legacy-win7-1.25.1-2.linux_arm64.tar.gz) | `4b55dc9898af4fccbb5ce31da3de78e881c6ba2883d87b7d59f810b1a971f450` |
|
||||
| **Windows** | x86 (386) | [go-legacy-win7-1.25.1-2.windows_386.zip](https://github.com/thongtech/go-legacy-win7/releases/download/v1.25.1-2/go-legacy-win7-1.25.1-2.windows_386.zip) | `caee0d6e4e323d98e63fcaa42bc563986fe47b3f4f0dc76aee0d1b7fd7ee9ac3` |
|
||||
| Windows | x64 (amd64) | [go-legacy-win7-1.25.1-2.windows_amd64.zip](https://github.com/thongtech/go-legacy-win7/releases/download/v1.25.1-2/go-legacy-win7-1.25.1-2.windows_amd64.zip) | `4a6eaf54116e20aeb1c777551deafdea3c8c3d82c869441703b68e063b93e808` |
|
||||
| Windows | ARM64 | [go-legacy-win7-1.25.1-2.windows_arm64.zip](https://github.com/thongtech/go-legacy-win7/releases/download/v1.25.1-2/go-legacy-win7-1.25.1-2.windows_arm64.zip) | `b4563f51d9459040fb09f291ffa4d1dea0962f66b961a36d6d102245f081fabe` |
|
||||
| **macOS** | Intel (amd64) | [go-legacy-win7-1.25.3-1.darwin_amd64.tar.gz](https://github.com/thongtech/go-legacy-win7/releases/download/v1.25.3-1/go-legacy-win7-1.25.3-1.darwin_amd64.tar.gz) | `34cec7b1bc140232b6d8c34fa787cca3dda47a6112a9e30588584edab7ec2ec5` |
|
||||
| macOS | Apple (ARM64) | [go-legacy-win7-1.25.3-1.darwin_arm64.tar.gz](https://github.com/thongtech/go-legacy-win7/releases/download/v1.25.3-1/go-legacy-win7-1.25.3-1.darwin_arm64.tar.gz) | `7e483748d46d8c882dea431509996b238f2db4c7a0c419f7aeffb1f219374526` |
|
||||
| **Linux** | x86 (386) | [go-legacy-win7-1.25.3-1.linux_386.tar.gz](https://github.com/thongtech/go-legacy-win7/releases/download/v1.25.3-1/go-legacy-win7-1.25.3-1.linux_386.tar.gz) | `0e30bc6240dfa8d4c7f21061ab567617980bdf26f35f0f5c45182302d24df7d1` |
|
||||
| Linux | x64 (amd64) | [go-legacy-win7-1.25.3-1.linux_amd64.tar.gz](https://github.com/thongtech/go-legacy-win7/releases/download/v1.25.3-1/go-legacy-win7-1.25.3-1.linux_amd64.tar.gz) | `92f08e1966d0662adb97badbf04046f2ef897d3167eb0ec60d35e7b547a49c5c` |
|
||||
| Linux | ARM (32‑bit) | [go-legacy-win7-1.25.3-1.linux_arm.tar.gz](https://github.com/thongtech/go-legacy-win7/releases/download/v1.25.3-1/go-legacy-win7-1.25.3-1.linux_arm.tar.gz) | `25eeade4dabc34e9c367a75944430000635bdcb3d2c1b32422b85ca7ee667033` |
|
||||
| Linux | ARM64 | [go-legacy-win7-1.25.3-1.linux_arm64.tar.gz](https://github.com/thongtech/go-legacy-win7/releases/download/v1.25.3-1/go-legacy-win7-1.25.3-1.linux_arm64.tar.gz) | `aeee3a9fd4f6a865e5a120b8f1840ebfebb61b8e2916a87153a6cea6c14416f7` |
|
||||
| **Windows** | x86 (386) | [go-legacy-win7-1.25.3-1.windows_386.zip](https://github.com/thongtech/go-legacy-win7/releases/download/v1.25.3-1/go-legacy-win7-1.25.3-1.windows_386.zip) | `f49c9f799ce962752677c6957f4885e6fa96f379d5c6ca933d88870a7fc53d9a` |
|
||||
| Windows | x64 (amd64) | [go-legacy-win7-1.25.3-1.windows_amd64.zip](https://github.com/thongtech/go-legacy-win7/releases/download/v1.25.3-1/go-legacy-win7-1.25.3-1.windows_amd64.zip) | `d6d3abf8cb0bae7d6c82cf815b17071f3f5beddbfc6cc11f8d6fb4218aaf0446` |
|
||||
| Windows | ARM64 | [go-legacy-win7-1.25.3-1.windows_arm64.zip](https://github.com/thongtech/go-legacy-win7/releases/download/v1.25.3-1/go-legacy-win7-1.25.3-1.windows_arm64.zip) | `da59144b5d014afe66c27b4584bae416470d97db7b0f427693dd1b8f0ece4566` |
|
||||
|
||||
### Before you begin
|
||||
To avoid PATH/GOROOT conflicts and mixed toolchains, uninstall any existing Go installation first.
|
||||
|
||||
4
VERSION
4
VERSION
@@ -1,2 +1,2 @@
|
||||
go1.25.2
|
||||
time 2025-10-02T18:00:14Z
|
||||
go1.25.4
|
||||
time 2025-10-31T13:24:27Z
|
||||
|
||||
@@ -49,9 +49,6 @@ type pkgReader struct {
|
||||
// but bitwise inverted so we can detect if we're missing the entry
|
||||
// or not.
|
||||
newindex []index
|
||||
|
||||
// indicates whether the data is reading during reshaping.
|
||||
reshaping bool
|
||||
}
|
||||
|
||||
func newPkgReader(pr pkgbits.PkgDecoder) *pkgReader {
|
||||
@@ -119,10 +116,6 @@ type reader struct {
|
||||
// find parameters/results.
|
||||
funarghack bool
|
||||
|
||||
// reshaping is used during reading exprReshape code, preventing
|
||||
// the reader from shapifying the re-shaped type.
|
||||
reshaping bool
|
||||
|
||||
// methodSym is the name of method's name, if reading a method.
|
||||
// It's nil if reading a normal function or closure body.
|
||||
methodSym *types.Sym
|
||||
@@ -937,8 +930,19 @@ func shapify(targ *types.Type, basic bool) *types.Type {
|
||||
// types, and discarding struct field names and tags. However, we'll
|
||||
// need to start tracking how type parameters are actually used to
|
||||
// implement some of these optimizations.
|
||||
pointerShaping := basic && targ.IsPtr() && !targ.Elem().NotInHeap()
|
||||
// The exception is when the type parameter is a pointer to a type
|
||||
// which `Type.HasShape()` returns true, but `Type.IsShape()` returns
|
||||
// false, like `*[]go.shape.T`. This is because the type parameter is
|
||||
// used to instantiate a generic function inside another generic function.
|
||||
// In this case, we want to keep the targ as-is, otherwise, we may lose the
|
||||
// original type after `*[]go.shape.T` is shapified to `*go.shape.uint8`.
|
||||
// See issue #54535, #71184.
|
||||
if pointerShaping && !targ.Elem().IsShape() && targ.Elem().HasShape() {
|
||||
return targ
|
||||
}
|
||||
under := targ.Underlying()
|
||||
if basic && targ.IsPtr() && !targ.Elem().NotInHeap() {
|
||||
if pointerShaping {
|
||||
under = types.NewPtr(types.Types[types.TUINT8])
|
||||
}
|
||||
|
||||
@@ -1014,25 +1018,7 @@ func (pr *pkgReader) objDictIdx(sym *types.Sym, idx index, implicits, explicits
|
||||
// arguments.
|
||||
for i, targ := range dict.targs {
|
||||
basic := r.Bool()
|
||||
isPointerShape := basic && targ.IsPtr() && !targ.Elem().NotInHeap()
|
||||
// We should not do shapify during the reshaping process, see #71184.
|
||||
// However, this only matters for shapify a pointer type, which will
|
||||
// lose the original underlying type.
|
||||
//
|
||||
// Example with a pointer type:
|
||||
//
|
||||
// - First, shapifying *[]T -> *uint8
|
||||
// - During the reshaping process, *uint8 is shapified to *go.shape.uint8
|
||||
// - This ends up with a different type with the original *[]T
|
||||
//
|
||||
// For a non-pointer type:
|
||||
//
|
||||
// - int -> go.shape.int
|
||||
// - go.shape.int -> go.shape.int
|
||||
//
|
||||
// We always end up with the identical type.
|
||||
canShapify := !pr.reshaping || !isPointerShape
|
||||
if dict.shaped && canShapify {
|
||||
if dict.shaped {
|
||||
dict.targs[i] = shapify(targ, basic)
|
||||
}
|
||||
}
|
||||
@@ -2470,10 +2456,7 @@ func (r *reader) expr() (res ir.Node) {
|
||||
|
||||
case exprReshape:
|
||||
typ := r.typ()
|
||||
old := r.reshaping
|
||||
r.reshaping = true
|
||||
x := r.expr()
|
||||
r.reshaping = old
|
||||
|
||||
if types.IdenticalStrict(x.Type(), typ) {
|
||||
return x
|
||||
@@ -2596,10 +2579,7 @@ func (r *reader) funcInst(pos src.XPos) (wrapperFn, baseFn, dictPtr ir.Node) {
|
||||
info := r.dict.subdicts[idx]
|
||||
explicits := r.p.typListIdx(info.explicits, r.dict)
|
||||
|
||||
old := r.p.reshaping
|
||||
r.p.reshaping = r.reshaping
|
||||
baseFn = r.p.objIdx(info.idx, implicits, explicits, true).(*ir.Name)
|
||||
r.p.reshaping = old
|
||||
|
||||
// TODO(mdempsky): Is there a more robust way to get the
|
||||
// dictionary pointer type here?
|
||||
|
||||
@@ -2555,7 +2555,7 @@ func rewriteStructStore(v *Value) *Value {
|
||||
|
||||
// isDirectType reports whether v represents a type
|
||||
// (a *runtime._type) whose value is stored directly in an
|
||||
// interface (i.e., is pointer or pointer-like).
|
||||
// interface (i.e., is pointer or pointer-like) and is comparable.
|
||||
func isDirectType(v *Value) bool {
|
||||
return isDirectType1(v)
|
||||
}
|
||||
@@ -2571,7 +2571,8 @@ func isDirectType1(v *Value) bool {
|
||||
return false
|
||||
}
|
||||
if ti, ok := (*lsym.Extra).(*obj.TypeInfo); ok {
|
||||
return types.IsDirectIface(ti.Type.(*types.Type))
|
||||
t := ti.Type.(*types.Type)
|
||||
return types.IsDirectIface(t) && types.IsComparable(t)
|
||||
}
|
||||
}
|
||||
return false
|
||||
@@ -2588,7 +2589,7 @@ func isDirectType2(v *Value) bool {
|
||||
|
||||
// isDirectIface reports whether v represents an itab
|
||||
// (a *runtime._itab) for a type whose value is stored directly
|
||||
// in an interface (i.e., is pointer or pointer-like).
|
||||
// in an interface (i.e., is pointer or pointer-like) and is comparable.
|
||||
func isDirectIface(v *Value) bool {
|
||||
return isDirectIface1(v, 9)
|
||||
}
|
||||
@@ -2607,7 +2608,8 @@ func isDirectIface1(v *Value, depth int) bool {
|
||||
return false
|
||||
}
|
||||
if ii, ok := (*lsym.Extra).(*obj.ItabInfo); ok {
|
||||
return types.IsDirectIface(ii.Type.(*types.Type))
|
||||
t := ii.Type.(*types.Type)
|
||||
return types.IsDirectIface(t) && types.IsComparable(t)
|
||||
}
|
||||
case OpConstNil:
|
||||
// We can treat this as direct, because if the itab is
|
||||
|
||||
78
src/cmd/compile/testdata/script/issue75461.txt
vendored
Normal file
78
src/cmd/compile/testdata/script/issue75461.txt
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
go build main.go
|
||||
! stdout .
|
||||
! stderr .
|
||||
|
||||
-- main.go --
|
||||
package main
|
||||
|
||||
import (
|
||||
"demo/registry"
|
||||
)
|
||||
|
||||
func main() {
|
||||
_ = registry.NewUserRegistry()
|
||||
}
|
||||
|
||||
-- go.mod --
|
||||
module demo
|
||||
|
||||
go 1.24
|
||||
|
||||
-- model/user.go --
|
||||
package model
|
||||
|
||||
type User struct {
|
||||
ID int
|
||||
}
|
||||
|
||||
func (c *User) String() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
-- ordered/map.go --
|
||||
package ordered
|
||||
|
||||
type OrderedMap[K comparable, V any] struct {
|
||||
m map[K]V
|
||||
}
|
||||
|
||||
func New[K comparable, V any](options ...any) *OrderedMap[K, V] {
|
||||
orderedMap := &OrderedMap[K, V]{}
|
||||
return orderedMap
|
||||
}
|
||||
|
||||
-- registry/user.go --
|
||||
package registry
|
||||
|
||||
import (
|
||||
"demo/model"
|
||||
"demo/ordered"
|
||||
)
|
||||
|
||||
type baseRegistry = Registry[model.User, *model.User]
|
||||
|
||||
type UserRegistry struct {
|
||||
*baseRegistry
|
||||
}
|
||||
|
||||
type Registry[T any, P PStringer[T]] struct {
|
||||
m *ordered.OrderedMap[string, P]
|
||||
}
|
||||
|
||||
type PStringer[T any] interface {
|
||||
*T
|
||||
String() string
|
||||
}
|
||||
|
||||
func NewRegistry[T any, P PStringer[T]]() *Registry[T, P] {
|
||||
r := &Registry[T, P]{
|
||||
m: ordered.New[string, P](),
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func NewUserRegistry() *UserRegistry {
|
||||
return &UserRegistry{
|
||||
baseRegistry: NewRegistry[model.User](),
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (amd64 || arm64 || mips || mipsle || mips64 || mips64le || ppc64 || ppc64le || riscv64) && !purego
|
||||
//go:build (amd64 || arm64 || ppc64 || ppc64le || riscv64) && !purego
|
||||
|
||||
package subtle
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (!amd64 && !arm64 && !loong64 && !mips && !mipsle && !mips64 && !mips64le && !ppc64 && !ppc64le && !riscv64) || purego
|
||||
//go:build (!amd64 && !arm64 && !loong64 && !ppc64 && !ppc64le && !riscv64) || purego
|
||||
|
||||
package subtle
|
||||
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
// Copyright 2025 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.
|
||||
|
||||
//go:build (mips64 || mips64le) && !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func xorBytes(dst, a, b *byte, n int)
|
||||
TEXT ·xorBytes(SB), NOSPLIT|NOFRAME, $0
|
||||
MOVV dst+0(FP), R1
|
||||
MOVV a+8(FP), R2
|
||||
MOVV b+16(FP), R3
|
||||
MOVV n+24(FP), R4
|
||||
|
||||
xor_64_check:
|
||||
SGTU $64, R4, R5 // R5 = 1 if (64 > R4)
|
||||
BNE R5, xor_32_check
|
||||
xor_64:
|
||||
MOVV (R2), R6
|
||||
MOVV 8(R2), R7
|
||||
MOVV 16(R2), R8
|
||||
MOVV 24(R2), R9
|
||||
MOVV (R3), R10
|
||||
MOVV 8(R3), R11
|
||||
MOVV 16(R3), R12
|
||||
MOVV 24(R3), R13
|
||||
XOR R6, R10
|
||||
XOR R7, R11
|
||||
XOR R8, R12
|
||||
XOR R9, R13
|
||||
MOVV R10, (R1)
|
||||
MOVV R11, 8(R1)
|
||||
MOVV R12, 16(R1)
|
||||
MOVV R13, 24(R1)
|
||||
MOVV 32(R2), R6
|
||||
MOVV 40(R2), R7
|
||||
MOVV 48(R2), R8
|
||||
MOVV 56(R2), R9
|
||||
MOVV 32(R3), R10
|
||||
MOVV 40(R3), R11
|
||||
MOVV 48(R3), R12
|
||||
MOVV 56(R3), R13
|
||||
XOR R6, R10
|
||||
XOR R7, R11
|
||||
XOR R8, R12
|
||||
XOR R9, R13
|
||||
MOVV R10, 32(R1)
|
||||
MOVV R11, 40(R1)
|
||||
MOVV R12, 48(R1)
|
||||
MOVV R13, 56(R1)
|
||||
ADDV $64, R2
|
||||
ADDV $64, R3
|
||||
ADDV $64, R1
|
||||
SUBV $64, R4
|
||||
SGTU $64, R4, R5
|
||||
BEQ R0, R5, xor_64
|
||||
BEQ R0, R4, end
|
||||
|
||||
xor_32_check:
|
||||
SGTU $32, R4, R5
|
||||
BNE R5, xor_16_check
|
||||
xor_32:
|
||||
MOVV (R2), R6
|
||||
MOVV 8(R2), R7
|
||||
MOVV 16(R2), R8
|
||||
MOVV 24(R2), R9
|
||||
MOVV (R3), R10
|
||||
MOVV 8(R3), R11
|
||||
MOVV 16(R3), R12
|
||||
MOVV 24(R3), R13
|
||||
XOR R6, R10
|
||||
XOR R7, R11
|
||||
XOR R8, R12
|
||||
XOR R9, R13
|
||||
MOVV R10, (R1)
|
||||
MOVV R11, 8(R1)
|
||||
MOVV R12, 16(R1)
|
||||
MOVV R13, 24(R1)
|
||||
ADDV $32, R2
|
||||
ADDV $32, R3
|
||||
ADDV $32, R1
|
||||
SUBV $32, R4
|
||||
BEQ R0, R4, end
|
||||
|
||||
xor_16_check:
|
||||
SGTU $16, R4, R5
|
||||
BNE R5, xor_8_check
|
||||
xor_16:
|
||||
MOVV (R2), R6
|
||||
MOVV 8(R2), R7
|
||||
MOVV (R3), R8
|
||||
MOVV 8(R3), R9
|
||||
XOR R6, R8
|
||||
XOR R7, R9
|
||||
MOVV R8, (R1)
|
||||
MOVV R9, 8(R1)
|
||||
ADDV $16, R2
|
||||
ADDV $16, R3
|
||||
ADDV $16, R1
|
||||
SUBV $16, R4
|
||||
BEQ R0, R4, end
|
||||
|
||||
xor_8_check:
|
||||
SGTU $8, R4, R5
|
||||
BNE R5, xor_4_check
|
||||
xor_8:
|
||||
MOVV (R2), R6
|
||||
MOVV (R3), R7
|
||||
XOR R6, R7
|
||||
MOVV R7, (R1)
|
||||
ADDV $8, R1
|
||||
ADDV $8, R2
|
||||
ADDV $8, R3
|
||||
SUBV $8, R4
|
||||
BEQ R0, R4, end
|
||||
|
||||
xor_4_check:
|
||||
SGTU $4, R4, R5
|
||||
BNE R5, xor_2_check
|
||||
xor_4:
|
||||
MOVW (R2), R6
|
||||
MOVW (R3), R7
|
||||
XOR R6, R7
|
||||
MOVW R7, (R1)
|
||||
ADDV $4, R2
|
||||
ADDV $4, R3
|
||||
ADDV $4, R1
|
||||
SUBV $4, R4
|
||||
BEQ R0, R4, end
|
||||
|
||||
xor_2_check:
|
||||
SGTU $2, R4, R5
|
||||
BNE R5, xor_1
|
||||
xor_2:
|
||||
MOVH (R2), R6
|
||||
MOVH (R3), R7
|
||||
XOR R6, R7
|
||||
MOVH R7, (R1)
|
||||
ADDV $2, R2
|
||||
ADDV $2, R3
|
||||
ADDV $2, R1
|
||||
SUBV $2, R4
|
||||
BEQ R0, R4, end
|
||||
|
||||
xor_1:
|
||||
MOVB (R2), R6
|
||||
MOVB (R3), R7
|
||||
XOR R6, R7
|
||||
MOVB R7, (R1)
|
||||
|
||||
end:
|
||||
RET
|
||||
@@ -1,212 +0,0 @@
|
||||
// Copyright 2025 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.
|
||||
|
||||
//go:build (mips || mipsle) && !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func xorBytes(dst, a, b *byte, n int)
|
||||
TEXT ·xorBytes(SB), NOSPLIT|NOFRAME, $0
|
||||
MOVW dst+0(FP), R1
|
||||
MOVW a+4(FP), R2
|
||||
MOVW b+8(FP), R3
|
||||
MOVW n+12(FP), R4
|
||||
|
||||
SGTU $64, R4, R5 // R5 = 1 if (64 > R4)
|
||||
BNE R5, xor_32_check
|
||||
xor_64:
|
||||
MOVW (R2), R6
|
||||
MOVW 4(R2), R7
|
||||
MOVW 8(R2), R8
|
||||
MOVW 12(R2), R9
|
||||
MOVW (R3), R10
|
||||
MOVW 4(R3), R11
|
||||
MOVW 8(R3), R12
|
||||
MOVW 12(R3), R13
|
||||
XOR R6, R10
|
||||
XOR R7, R11
|
||||
XOR R8, R12
|
||||
XOR R9, R13
|
||||
MOVW R10, (R1)
|
||||
MOVW R11, 4(R1)
|
||||
MOVW R12, 8(R1)
|
||||
MOVW R13, 12(R1)
|
||||
MOVW 16(R2), R6
|
||||
MOVW 20(R2), R7
|
||||
MOVW 24(R2), R8
|
||||
MOVW 28(R2), R9
|
||||
MOVW 16(R3), R10
|
||||
MOVW 20(R3), R11
|
||||
MOVW 24(R3), R12
|
||||
MOVW 28(R3), R13
|
||||
XOR R6, R10
|
||||
XOR R7, R11
|
||||
XOR R8, R12
|
||||
XOR R9, R13
|
||||
MOVW R10, 16(R1)
|
||||
MOVW R11, 20(R1)
|
||||
MOVW R12, 24(R1)
|
||||
MOVW R13, 28(R1)
|
||||
MOVW 32(R2), R6
|
||||
MOVW 36(R2), R7
|
||||
MOVW 40(R2), R8
|
||||
MOVW 44(R2), R9
|
||||
MOVW 32(R3), R10
|
||||
MOVW 36(R3), R11
|
||||
MOVW 40(R3), R12
|
||||
MOVW 44(R3), R13
|
||||
XOR R6, R10
|
||||
XOR R7, R11
|
||||
XOR R8, R12
|
||||
XOR R9, R13
|
||||
MOVW R10, 32(R1)
|
||||
MOVW R11, 36(R1)
|
||||
MOVW R12, 40(R1)
|
||||
MOVW R13, 44(R1)
|
||||
MOVW 48(R2), R6
|
||||
MOVW 52(R2), R7
|
||||
MOVW 56(R2), R8
|
||||
MOVW 60(R2), R9
|
||||
MOVW 48(R3), R10
|
||||
MOVW 52(R3), R11
|
||||
MOVW 56(R3), R12
|
||||
MOVW 60(R3), R13
|
||||
XOR R6, R10
|
||||
XOR R7, R11
|
||||
XOR R8, R12
|
||||
XOR R9, R13
|
||||
MOVW R10, 48(R1)
|
||||
MOVW R11, 52(R1)
|
||||
MOVW R12, 56(R1)
|
||||
MOVW R13, 60(R1)
|
||||
ADD $64, R2
|
||||
ADD $64, R3
|
||||
ADD $64, R1
|
||||
SUB $64, R4
|
||||
SGTU $64, R4, R5
|
||||
BEQ R0, R5, xor_64
|
||||
BEQ R0, R4, end
|
||||
|
||||
xor_32_check:
|
||||
SGTU $32, R4, R5
|
||||
BNE R5, xor_16_check
|
||||
xor_32:
|
||||
MOVW (R2), R6
|
||||
MOVW 4(R2), R7
|
||||
MOVW 8(R2), R8
|
||||
MOVW 12(R2), R9
|
||||
MOVW (R3), R10
|
||||
MOVW 4(R3), R11
|
||||
MOVW 8(R3), R12
|
||||
MOVW 12(R3), R13
|
||||
XOR R6, R10
|
||||
XOR R7, R11
|
||||
XOR R8, R12
|
||||
XOR R9, R13
|
||||
MOVW R10, (R1)
|
||||
MOVW R11, 4(R1)
|
||||
MOVW R12, 8(R1)
|
||||
MOVW R13, 12(R1)
|
||||
MOVW 16(R2), R6
|
||||
MOVW 20(R2), R7
|
||||
MOVW 24(R2), R8
|
||||
MOVW 28(R2), R9
|
||||
MOVW 16(R3), R10
|
||||
MOVW 20(R3), R11
|
||||
MOVW 24(R3), R12
|
||||
MOVW 28(R3), R13
|
||||
XOR R6, R10
|
||||
XOR R7, R11
|
||||
XOR R8, R12
|
||||
XOR R9, R13
|
||||
MOVW R10, 16(R1)
|
||||
MOVW R11, 20(R1)
|
||||
MOVW R12, 24(R1)
|
||||
MOVW R13, 28(R1)
|
||||
ADD $32, R2
|
||||
ADD $32, R3
|
||||
ADD $32, R1
|
||||
SUB $32, R4
|
||||
BEQ R0, R4, end
|
||||
|
||||
xor_16_check:
|
||||
SGTU $16, R4, R5
|
||||
BNE R5, xor_8_check
|
||||
xor_16:
|
||||
MOVW (R2), R6
|
||||
MOVW 4(R2), R7
|
||||
MOVW 8(R2), R8
|
||||
MOVW 12(R2), R9
|
||||
MOVW (R3), R10
|
||||
MOVW 4(R3), R11
|
||||
MOVW 8(R3), R12
|
||||
MOVW 12(R3), R13
|
||||
XOR R6, R10
|
||||
XOR R7, R11
|
||||
XOR R8, R12
|
||||
XOR R9, R13
|
||||
MOVW R10, (R1)
|
||||
MOVW R11, 4(R1)
|
||||
MOVW R12, 8(R1)
|
||||
MOVW R13, 12(R1)
|
||||
ADD $16, R2
|
||||
ADD $16, R3
|
||||
ADD $16, R1
|
||||
SUB $16, R4
|
||||
BEQ R0, R4, end
|
||||
|
||||
xor_8_check:
|
||||
SGTU $8, R4, R5
|
||||
BNE R5, xor_4_check
|
||||
xor_8:
|
||||
MOVW (R2), R6
|
||||
MOVW 4(R2), R7
|
||||
MOVW (R3), R8
|
||||
MOVW 4(R3), R9
|
||||
XOR R6, R8
|
||||
XOR R7, R9
|
||||
MOVW R8, (R1)
|
||||
MOVW R9, 4(R1)
|
||||
ADD $8, R1
|
||||
ADD $8, R2
|
||||
ADD $8, R3
|
||||
SUB $8, R4
|
||||
BEQ R0, R4, end
|
||||
|
||||
xor_4_check:
|
||||
SGTU $4, R4, R5
|
||||
BNE R5, xor_2_check
|
||||
xor_4:
|
||||
MOVW (R2), R6
|
||||
MOVW (R3), R7
|
||||
XOR R6, R7
|
||||
MOVW R7, (R1)
|
||||
ADD $4, R2
|
||||
ADD $4, R3
|
||||
ADD $4, R1
|
||||
SUB $4, R4
|
||||
BEQ R0, R4, end
|
||||
|
||||
xor_2_check:
|
||||
SGTU $2, R4, R5
|
||||
BNE R5, xor_1
|
||||
xor_2:
|
||||
MOVH (R2), R6
|
||||
MOVH (R3), R7
|
||||
XOR R6, R7
|
||||
MOVH R7, (R1)
|
||||
ADD $2, R2
|
||||
ADD $2, R3
|
||||
ADD $2, R1
|
||||
SUB $2, R4
|
||||
BEQ R0, R4, end
|
||||
|
||||
xor_1:
|
||||
MOVB (R2), R6
|
||||
MOVB (R3), R7
|
||||
XOR R6, R7
|
||||
MOVB R7, (R1)
|
||||
|
||||
end:
|
||||
RET
|
||||
@@ -1456,7 +1456,63 @@ var nameConstraintsTests = []nameConstraintsTest{
|
||||
expectedError: "incompatible key usage",
|
||||
},
|
||||
|
||||
// #77: if several EKUs are requested, satisfying any of them is sufficient.
|
||||
// An invalid DNS SAN should be detected only at validation time so
|
||||
// that we can process CA certificates in the wild that have invalid SANs.
|
||||
// See https://github.com/golang/go/issues/23995
|
||||
|
||||
// #77: an invalid DNS or mail SAN will not be detected if name constraint
|
||||
// checking is not triggered.
|
||||
{
|
||||
roots: make([]constraintsSpec, 1),
|
||||
intermediates: [][]constraintsSpec{
|
||||
{
|
||||
{},
|
||||
},
|
||||
},
|
||||
leaf: leafSpec{
|
||||
sans: []string{"dns:this is invalid", "email:this @ is invalid"},
|
||||
},
|
||||
},
|
||||
|
||||
// #78: an invalid DNS SAN will be detected if any name constraint checking
|
||||
// is triggered.
|
||||
{
|
||||
roots: []constraintsSpec{
|
||||
{
|
||||
bad: []string{"uri:"},
|
||||
},
|
||||
},
|
||||
intermediates: [][]constraintsSpec{
|
||||
{
|
||||
{},
|
||||
},
|
||||
},
|
||||
leaf: leafSpec{
|
||||
sans: []string{"dns:this is invalid"},
|
||||
},
|
||||
expectedError: "cannot parse dnsName",
|
||||
},
|
||||
|
||||
// #79: an invalid email SAN will be detected if any name constraint
|
||||
// checking is triggered.
|
||||
{
|
||||
roots: []constraintsSpec{
|
||||
{
|
||||
bad: []string{"uri:"},
|
||||
},
|
||||
},
|
||||
intermediates: [][]constraintsSpec{
|
||||
{
|
||||
{},
|
||||
},
|
||||
},
|
||||
leaf: leafSpec{
|
||||
sans: []string{"email:this @ is invalid"},
|
||||
},
|
||||
expectedError: "cannot parse rfc822Name",
|
||||
},
|
||||
|
||||
// #80: if several EKUs are requested, satisfying any of them is sufficient.
|
||||
{
|
||||
roots: make([]constraintsSpec, 1),
|
||||
intermediates: [][]constraintsSpec{
|
||||
@@ -1471,7 +1527,7 @@ var nameConstraintsTests = []nameConstraintsTest{
|
||||
requestedEKUs: []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageEmailProtection},
|
||||
},
|
||||
|
||||
// #78: EKUs that are not asserted in VerifyOpts are not required to be
|
||||
// #81: EKUs that are not asserted in VerifyOpts are not required to be
|
||||
// nested.
|
||||
{
|
||||
roots: make([]constraintsSpec, 1),
|
||||
@@ -1490,7 +1546,7 @@ var nameConstraintsTests = []nameConstraintsTest{
|
||||
},
|
||||
},
|
||||
|
||||
// #79: a certificate without SANs and CN is accepted in a constrained chain.
|
||||
// #82: a certificate without SANs and CN is accepted in a constrained chain.
|
||||
{
|
||||
roots: []constraintsSpec{
|
||||
{
|
||||
@@ -1507,7 +1563,7 @@ var nameConstraintsTests = []nameConstraintsTest{
|
||||
},
|
||||
},
|
||||
|
||||
// #80: a certificate without SANs and with a CN that does not parse as a
|
||||
// #83: a certificate without SANs and with a CN that does not parse as a
|
||||
// hostname is accepted in a constrained chain.
|
||||
{
|
||||
roots: []constraintsSpec{
|
||||
@@ -1526,7 +1582,7 @@ var nameConstraintsTests = []nameConstraintsTest{
|
||||
},
|
||||
},
|
||||
|
||||
// #81: a certificate with SANs and CN is accepted in a constrained chain.
|
||||
// #84: a certificate with SANs and CN is accepted in a constrained chain.
|
||||
{
|
||||
roots: []constraintsSpec{
|
||||
{
|
||||
@@ -1544,7 +1600,14 @@ var nameConstraintsTests = []nameConstraintsTest{
|
||||
},
|
||||
},
|
||||
|
||||
// #82: URIs with IPv6 addresses with zones and ports are rejected
|
||||
// #85: .example.com is an invalid DNS name, it should not match the
|
||||
// constraint example.com.
|
||||
{
|
||||
roots: []constraintsSpec{{ok: []string{"dns:example.com"}}},
|
||||
leaf: leafSpec{sans: []string{"dns:.example.com"}},
|
||||
expectedError: "cannot parse dnsName \".example.com\"",
|
||||
},
|
||||
// #86: URIs with IPv6 addresses with zones and ports are rejected
|
||||
{
|
||||
roots: []constraintsSpec{
|
||||
{
|
||||
|
||||
@@ -413,14 +413,10 @@ func parseSANExtension(der cryptobyte.String) (dnsNames, emailAddresses []string
|
||||
if err := isIA5String(email); err != nil {
|
||||
return errors.New("x509: SAN rfc822Name is malformed")
|
||||
}
|
||||
parsed, ok := parseRFC2821Mailbox(email)
|
||||
if !ok || (ok && !domainNameValid(parsed.domain, false)) {
|
||||
return errors.New("x509: SAN rfc822Name is malformed")
|
||||
}
|
||||
emailAddresses = append(emailAddresses, email)
|
||||
case nameTypeDNS:
|
||||
name := string(data)
|
||||
if err := isIA5String(name); err != nil || (err == nil && !domainNameValid(name, false)) {
|
||||
if err := isIA5String(name); err != nil {
|
||||
return errors.New("x509: SAN dNSName is malformed")
|
||||
}
|
||||
dnsNames = append(dnsNames, string(name))
|
||||
@@ -430,9 +426,12 @@ func parseSANExtension(der cryptobyte.String) (dnsNames, emailAddresses []string
|
||||
return errors.New("x509: SAN uniformResourceIdentifier is malformed")
|
||||
}
|
||||
uri, err := url.Parse(uriStr)
|
||||
if err != nil || (err == nil && uri.Host != "" && !domainNameValid(uri.Host, false)) {
|
||||
if err != nil {
|
||||
return fmt.Errorf("x509: cannot parse URI %q: %s", uriStr, err)
|
||||
}
|
||||
if len(uri.Host) > 0 && !domainNameValid(uri.Host, false) {
|
||||
return fmt.Errorf("x509: cannot parse URI %q: invalid domain", uriStr)
|
||||
}
|
||||
uris = append(uris, uri)
|
||||
case nameTypeIP:
|
||||
switch len(data) {
|
||||
@@ -1296,36 +1295,58 @@ func ParseRevocationList(der []byte) (*RevocationList, error) {
|
||||
return rl, nil
|
||||
}
|
||||
|
||||
// domainNameValid does minimal domain name validity checking. In particular it
|
||||
// enforces the following properties:
|
||||
// - names cannot have the trailing period
|
||||
// - names can only have a leading period if constraint is true
|
||||
// - names must be <= 253 characters
|
||||
// - names cannot have empty labels
|
||||
// - names cannot labels that are longer than 63 characters
|
||||
//
|
||||
// Note that this does not enforce the LDH requirements for domain names.
|
||||
// domainNameValid is an alloc-less version of the checks that
|
||||
// domainToReverseLabels does.
|
||||
func domainNameValid(s string, constraint bool) bool {
|
||||
if len(s) == 0 && constraint {
|
||||
// TODO(#75835): This function omits a number of checks which we
|
||||
// really should be doing to enforce that domain names are valid names per
|
||||
// RFC 1034. We previously enabled these checks, but this broke a
|
||||
// significant number of certificates we previously considered valid, and we
|
||||
// happily create via CreateCertificate (et al). We should enable these
|
||||
// checks, but will need to gate them behind a GODEBUG.
|
||||
//
|
||||
// I have left the checks we previously enabled, noted with "TODO(#75835)" so
|
||||
// that we can easily re-enable them once we unbreak everyone.
|
||||
|
||||
// TODO(#75835): this should only be true for constraints.
|
||||
if len(s) == 0 {
|
||||
return true
|
||||
}
|
||||
if len(s) == 0 || (!constraint && s[0] == '.') || s[len(s)-1] == '.' || len(s) > 253 {
|
||||
|
||||
// Do not allow trailing period (FQDN format is not allowed in SANs or
|
||||
// constraints).
|
||||
if s[len(s)-1] == '.' {
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO(#75835): domains must have at least one label, cannot have
|
||||
// a leading empty label, and cannot be longer than 253 characters.
|
||||
// if len(s) == 0 || (!constraint && s[0] == '.') || len(s) > 253 {
|
||||
// return false
|
||||
// }
|
||||
|
||||
lastDot := -1
|
||||
if constraint && s[0] == '.' {
|
||||
s = s[1:]
|
||||
}
|
||||
|
||||
for i := 0; i <= len(s); i++ {
|
||||
if i < len(s) && (s[i] < 33 || s[i] > 126) {
|
||||
// Invalid character.
|
||||
return false
|
||||
}
|
||||
if i == len(s) || s[i] == '.' {
|
||||
labelLen := i
|
||||
if lastDot >= 0 {
|
||||
labelLen -= lastDot + 1
|
||||
}
|
||||
if labelLen == 0 || labelLen > 63 {
|
||||
if labelLen == 0 {
|
||||
return false
|
||||
}
|
||||
// TODO(#75835): labels cannot be longer than 63 characters.
|
||||
// if labelLen > 63 {
|
||||
// return false
|
||||
// }
|
||||
lastDot = i
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
package x509
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
"os"
|
||||
@@ -260,7 +263,31 @@ func TestDomainNameValid(t *testing.T) {
|
||||
constraint bool
|
||||
valid bool
|
||||
}{
|
||||
{"empty name, name", "", false, false},
|
||||
// TODO(#75835): these tests are for stricter name validation, which we
|
||||
// had to disable. Once we reenable these strict checks, behind a
|
||||
// GODEBUG, we should add them back in.
|
||||
// {"empty name, name", "", false, false},
|
||||
// {"254 char label, name", strings.Repeat("a.a", 84) + "aaa", false, false},
|
||||
// {"254 char label, constraint", strings.Repeat("a.a", 84) + "aaa", true, false},
|
||||
// {"253 char label, name", strings.Repeat("a.a", 84) + "aa", false, false},
|
||||
// {"253 char label, constraint", strings.Repeat("a.a", 84) + "aa", true, false},
|
||||
// {"64 char single label, name", strings.Repeat("a", 64), false, false},
|
||||
// {"64 char single label, constraint", strings.Repeat("a", 64), true, false},
|
||||
// {"64 char label, name", "a." + strings.Repeat("a", 64), false, false},
|
||||
// {"64 char label, constraint", "a." + strings.Repeat("a", 64), true, false},
|
||||
|
||||
// TODO(#75835): these are the inverse of the tests above, they should be removed
|
||||
// once the strict checking is enabled.
|
||||
{"254 char label, name", strings.Repeat("a.a", 84) + "aaa", false, true},
|
||||
{"254 char label, constraint", strings.Repeat("a.a", 84) + "aaa", true, true},
|
||||
{"253 char label, name", strings.Repeat("a.a", 84) + "aa", false, true},
|
||||
{"253 char label, constraint", strings.Repeat("a.a", 84) + "aa", true, true},
|
||||
{"64 char single label, name", strings.Repeat("a", 64), false, true},
|
||||
{"64 char single label, constraint", strings.Repeat("a", 64), true, true},
|
||||
{"64 char label, name", "a." + strings.Repeat("a", 64), false, true},
|
||||
{"64 char label, constraint", "a." + strings.Repeat("a", 64), true, true},
|
||||
|
||||
// Check we properly enforce properties of domain names.
|
||||
{"empty name, constraint", "", true, true},
|
||||
{"empty label, name", "a..a", false, false},
|
||||
{"empty label, constraint", "a..a", true, false},
|
||||
@@ -274,23 +301,60 @@ func TestDomainNameValid(t *testing.T) {
|
||||
{"trailing period, constraint", "a.", true, false},
|
||||
{"bare label, name", "a", false, true},
|
||||
{"bare label, constraint", "a", true, true},
|
||||
{"254 char label, name", strings.Repeat("a.a", 84) + "aaa", false, false},
|
||||
{"254 char label, constraint", strings.Repeat("a.a", 84) + "aaa", true, false},
|
||||
{"253 char label, name", strings.Repeat("a.a", 84) + "aa", false, false},
|
||||
{"253 char label, constraint", strings.Repeat("a.a", 84) + "aa", true, false},
|
||||
{"64 char single label, name", strings.Repeat("a", 64), false, false},
|
||||
{"64 char single label, constraint", strings.Repeat("a", 64), true, false},
|
||||
{"63 char single label, name", strings.Repeat("a", 63), false, true},
|
||||
{"63 char single label, constraint", strings.Repeat("a", 63), true, true},
|
||||
{"64 char label, name", "a." + strings.Repeat("a", 64), false, false},
|
||||
{"64 char label, constraint", "a." + strings.Repeat("a", 64), true, false},
|
||||
{"63 char label, name", "a." + strings.Repeat("a", 63), false, true},
|
||||
{"63 char label, constraint", "a." + strings.Repeat("a", 63), true, true},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if tc.valid != domainNameValid(tc.dnsName, tc.constraint) {
|
||||
valid := domainNameValid(tc.dnsName, tc.constraint)
|
||||
if tc.valid != valid {
|
||||
t.Errorf("domainNameValid(%q, %t) = %v; want %v", tc.dnsName, tc.constraint, !tc.valid, tc.valid)
|
||||
}
|
||||
// Also check that we enforce the same properties as domainToReverseLabels
|
||||
trimmedName := tc.dnsName
|
||||
if tc.constraint && len(trimmedName) > 1 && trimmedName[0] == '.' {
|
||||
trimmedName = trimmedName[1:]
|
||||
}
|
||||
_, revValid := domainToReverseLabels(trimmedName)
|
||||
if valid != revValid {
|
||||
t.Errorf("domainNameValid(%q, %t) = %t != domainToReverseLabels(%q) = %t", tc.dnsName, tc.constraint, valid, trimmedName, revValid)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundtripWeirdSANs(t *testing.T) {
|
||||
// TODO(#75835): check that certificates we create with CreateCertificate that have malformed SAN values
|
||||
// can be parsed by ParseCertificate. We should eventually restrict this, but for now we have to maintain
|
||||
// this property as people have been relying on it.
|
||||
k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
badNames := []string{
|
||||
"baredomain",
|
||||
"baredomain.",
|
||||
strings.Repeat("a", 255),
|
||||
strings.Repeat("a", 65) + ".com",
|
||||
}
|
||||
tmpl := &Certificate{
|
||||
EmailAddresses: badNames,
|
||||
DNSNames: badNames,
|
||||
}
|
||||
b, err := CreateCertificate(rand.Reader, tmpl, tmpl, &k.PublicKey, k)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = ParseCertificate(b)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't roundtrip certificate: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzDomainNameValid(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data string) {
|
||||
domainNameValid(data, false)
|
||||
domainNameValid(data, true)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -429,7 +429,7 @@ func domainToReverseLabels(domain string) (reverseLabels []string, ok bool) {
|
||||
return reverseLabels, true
|
||||
}
|
||||
|
||||
func matchEmailConstraint(mailbox rfc2821Mailbox, constraint string) (bool, error) {
|
||||
func matchEmailConstraint(mailbox rfc2821Mailbox, constraint string, reversedDomainsCache map[string][]string, reversedConstraintsCache map[string][]string) (bool, error) {
|
||||
// If the constraint contains an @, then it specifies an exact mailbox
|
||||
// name.
|
||||
if strings.Contains(constraint, "@") {
|
||||
@@ -442,10 +442,10 @@ func matchEmailConstraint(mailbox rfc2821Mailbox, constraint string) (bool, erro
|
||||
|
||||
// Otherwise the constraint is like a DNS constraint of the domain part
|
||||
// of the mailbox.
|
||||
return matchDomainConstraint(mailbox.domain, constraint)
|
||||
return matchDomainConstraint(mailbox.domain, constraint, reversedDomainsCache, reversedConstraintsCache)
|
||||
}
|
||||
|
||||
func matchURIConstraint(uri *url.URL, constraint string) (bool, error) {
|
||||
func matchURIConstraint(uri *url.URL, constraint string, reversedDomainsCache map[string][]string, reversedConstraintsCache map[string][]string) (bool, error) {
|
||||
// From RFC 5280, Section 4.2.1.10:
|
||||
// “a uniformResourceIdentifier that does not include an authority
|
||||
// component with a host name specified as a fully qualified domain
|
||||
@@ -474,7 +474,7 @@ func matchURIConstraint(uri *url.URL, constraint string) (bool, error) {
|
||||
return false, fmt.Errorf("URI with IP (%q) cannot be matched against constraints", uri.String())
|
||||
}
|
||||
|
||||
return matchDomainConstraint(host, constraint)
|
||||
return matchDomainConstraint(host, constraint, reversedDomainsCache, reversedConstraintsCache)
|
||||
}
|
||||
|
||||
func matchIPConstraint(ip net.IP, constraint *net.IPNet) (bool, error) {
|
||||
@@ -491,16 +491,21 @@ func matchIPConstraint(ip net.IP, constraint *net.IPNet) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func matchDomainConstraint(domain, constraint string) (bool, error) {
|
||||
func matchDomainConstraint(domain, constraint string, reversedDomainsCache map[string][]string, reversedConstraintsCache map[string][]string) (bool, error) {
|
||||
// The meaning of zero length constraints is not specified, but this
|
||||
// code follows NSS and accepts them as matching everything.
|
||||
if len(constraint) == 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
domainLabels, ok := domainToReverseLabels(domain)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("x509: internal error: cannot parse domain %q", domain)
|
||||
domainLabels, found := reversedDomainsCache[domain]
|
||||
if !found {
|
||||
var ok bool
|
||||
domainLabels, ok = domainToReverseLabels(domain)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("x509: internal error: cannot parse domain %q", domain)
|
||||
}
|
||||
reversedDomainsCache[domain] = domainLabels
|
||||
}
|
||||
|
||||
// RFC 5280 says that a leading period in a domain name means that at
|
||||
@@ -514,9 +519,14 @@ func matchDomainConstraint(domain, constraint string) (bool, error) {
|
||||
constraint = constraint[1:]
|
||||
}
|
||||
|
||||
constraintLabels, ok := domainToReverseLabels(constraint)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("x509: internal error: cannot parse domain %q", constraint)
|
||||
constraintLabels, found := reversedConstraintsCache[constraint]
|
||||
if !found {
|
||||
var ok bool
|
||||
constraintLabels, ok = domainToReverseLabels(constraint)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("x509: internal error: cannot parse domain %q", constraint)
|
||||
}
|
||||
reversedConstraintsCache[constraint] = constraintLabels
|
||||
}
|
||||
|
||||
if len(domainLabels) < len(constraintLabels) ||
|
||||
@@ -637,6 +647,19 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
|
||||
}
|
||||
}
|
||||
|
||||
// Each time we do constraint checking, we need to check the constraints in
|
||||
// the current certificate against all of the names that preceded it. We
|
||||
// reverse these names using domainToReverseLabels, which is a relatively
|
||||
// expensive operation. Since we check each name against each constraint,
|
||||
// this requires us to do N*C calls to domainToReverseLabels (where N is the
|
||||
// total number of names that preceed the certificate, and C is the total
|
||||
// number of constraints in the certificate). By caching the results of
|
||||
// calling domainToReverseLabels, we can reduce that to N+C calls at the
|
||||
// cost of keeping all of the parsed names and constraints in memory until
|
||||
// we return from isValid.
|
||||
reversedDomainsCache := map[string][]string{}
|
||||
reversedConstraintsCache := map[string][]string{}
|
||||
|
||||
if (certType == intermediateCertificate || certType == rootCertificate) &&
|
||||
c.hasNameConstraints() {
|
||||
toCheck := []*Certificate{}
|
||||
@@ -657,20 +680,20 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
|
||||
|
||||
if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "email address", name, mailbox,
|
||||
func(parsedName, constraint any) (bool, error) {
|
||||
return matchEmailConstraint(parsedName.(rfc2821Mailbox), constraint.(string))
|
||||
return matchEmailConstraint(parsedName.(rfc2821Mailbox), constraint.(string), reversedDomainsCache, reversedConstraintsCache)
|
||||
}, c.PermittedEmailAddresses, c.ExcludedEmailAddresses); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case nameTypeDNS:
|
||||
name := string(data)
|
||||
if _, ok := domainToReverseLabels(name); !ok {
|
||||
if !domainNameValid(name, false) {
|
||||
return fmt.Errorf("x509: cannot parse dnsName %q", name)
|
||||
}
|
||||
|
||||
if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "DNS name", name, name,
|
||||
func(parsedName, constraint any) (bool, error) {
|
||||
return matchDomainConstraint(parsedName.(string), constraint.(string))
|
||||
return matchDomainConstraint(parsedName.(string), constraint.(string), reversedDomainsCache, reversedConstraintsCache)
|
||||
}, c.PermittedDNSDomains, c.ExcludedDNSDomains); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -684,7 +707,7 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
|
||||
|
||||
if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "URI", name, uri,
|
||||
func(parsedName, constraint any) (bool, error) {
|
||||
return matchURIConstraint(parsedName.(*url.URL), constraint.(string))
|
||||
return matchURIConstraint(parsedName.(*url.URL), constraint.(string), reversedDomainsCache, reversedConstraintsCache)
|
||||
}, c.PermittedURIDomains, c.ExcludedURIDomains); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1352,7 +1352,7 @@ var nameConstraintTests = []struct {
|
||||
|
||||
func TestNameConstraints(t *testing.T) {
|
||||
for i, test := range nameConstraintTests {
|
||||
result, err := matchDomainConstraint(test.domain, test.constraint)
|
||||
result, err := matchDomainConstraint(test.domain, test.constraint, map[string][]string{}, map[string][]string{})
|
||||
|
||||
if err != nil && !test.expectError {
|
||||
t.Errorf("unexpected error for test #%d: domain=%s, constraint=%s, err=%s", i, test.domain, test.constraint, err)
|
||||
|
||||
@@ -91,7 +91,15 @@ func Decode(data []byte) (p *Block, rest []byte) {
|
||||
// the byte array, we'll accept the start string without it.
|
||||
rest = data
|
||||
|
||||
endTrailerIndex := 0
|
||||
for {
|
||||
// If we've already tried parsing a block, skip past the END we already
|
||||
// saw.
|
||||
if endTrailerIndex < 0 || endTrailerIndex > len(rest) {
|
||||
return nil, data
|
||||
}
|
||||
rest = rest[endTrailerIndex:]
|
||||
|
||||
// Find the first END line, and then find the last BEGIN line before
|
||||
// the end line. This lets us skip any repeated BEGIN lines that don't
|
||||
// have a matching END.
|
||||
@@ -99,10 +107,10 @@ func Decode(data []byte) (p *Block, rest []byte) {
|
||||
if endIndex < 0 {
|
||||
return nil, data
|
||||
}
|
||||
endTrailerIndex := endIndex + len(pemEnd)
|
||||
endTrailerIndex = endIndex + len(pemEnd)
|
||||
beginIndex := bytes.LastIndex(rest[:endIndex], pemStart[1:])
|
||||
if beginIndex < 0 || beginIndex > 0 && rest[beginIndex-1] != '\n' {
|
||||
return nil, data
|
||||
if beginIndex < 0 || (beginIndex > 0 && rest[beginIndex-1] != '\n') {
|
||||
continue
|
||||
}
|
||||
rest = rest[beginIndex+len(pemStart)-1:]
|
||||
endIndex -= beginIndex + len(pemStart) - 1
|
||||
@@ -111,11 +119,11 @@ func Decode(data []byte) (p *Block, rest []byte) {
|
||||
var typeLine []byte
|
||||
var consumed int
|
||||
typeLine, rest, consumed = getLine(rest)
|
||||
endIndex -= consumed
|
||||
endTrailerIndex -= consumed
|
||||
if !bytes.HasSuffix(typeLine, pemEndOfLine) {
|
||||
continue
|
||||
}
|
||||
endIndex -= consumed
|
||||
endTrailerIndex -= consumed
|
||||
typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)]
|
||||
|
||||
p = &Block{
|
||||
|
||||
@@ -639,3 +639,104 @@ func TestBadEncode(t *testing.T) {
|
||||
}
|
||||
|
||||
func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") }
|
||||
|
||||
func TestDecodeStrangeCases(t *testing.T) {
|
||||
sentinelType := "TEST BLOCK"
|
||||
sentinelBytes := []byte("hello")
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
pem string
|
||||
}{
|
||||
{
|
||||
name: "invalid section (not base64)",
|
||||
pem: `-----BEGIN COMMENT-----
|
||||
foo foo foo
|
||||
-----END COMMENT-----
|
||||
-----BEGIN TEST BLOCK-----
|
||||
aGVsbG8=
|
||||
-----END TEST BLOCK-----`,
|
||||
},
|
||||
{
|
||||
name: "leading garbage on block",
|
||||
pem: `foo foo foo-----BEGIN CERTIFICATE-----
|
||||
MCowBQYDK2VwAyEApVjJeLW5MoP6uR3+OeITokM+rBDng6dgl1vvhcy+wws=
|
||||
-----END PUBLIC KEY-----
|
||||
-----BEGIN TEST BLOCK-----
|
||||
aGVsbG8=
|
||||
-----END TEST BLOCK-----`,
|
||||
},
|
||||
{
|
||||
name: "leading garbage",
|
||||
pem: `foo foo foo
|
||||
-----BEGIN TEST BLOCK-----
|
||||
aGVsbG8=
|
||||
-----END TEST BLOCK-----`,
|
||||
},
|
||||
{
|
||||
name: "leading partial block",
|
||||
pem: `foo foo foo
|
||||
-----END COMMENT-----
|
||||
-----BEGIN TEST BLOCK-----
|
||||
aGVsbG8=
|
||||
-----END TEST BLOCK-----`,
|
||||
},
|
||||
{
|
||||
name: "multiple BEGIN",
|
||||
pem: `-----BEGIN TEST BLOCK-----
|
||||
-----BEGIN TEST BLOCK-----
|
||||
-----BEGIN TEST BLOCK-----
|
||||
aGVsbG8=
|
||||
-----END TEST BLOCK-----`,
|
||||
},
|
||||
{
|
||||
name: "multiple END",
|
||||
pem: `-----BEGIN TEST BLOCK-----
|
||||
aGVsbG8=
|
||||
-----END TEST BLOCK-----
|
||||
-----END TEST BLOCK-----
|
||||
-----END TEST BLOCK-----`,
|
||||
},
|
||||
{
|
||||
name: "leading malformed BEGIN",
|
||||
pem: `-----BEGIN PUBLIC KEY
|
||||
aGVsbG8=
|
||||
-----END PUBLIC KEY-----
|
||||
-----BEGIN TEST BLOCK-----
|
||||
aGVsbG8=
|
||||
-----END TEST BLOCK-----`,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
block, _ := Decode([]byte(tc.pem))
|
||||
if block == nil {
|
||||
t.Fatal("expected valid block")
|
||||
}
|
||||
if block.Type != sentinelType {
|
||||
t.Fatalf("unexpected block returned, got type %q, want type %q", block.Type, sentinelType)
|
||||
}
|
||||
if !bytes.Equal(block.Bytes, sentinelBytes) {
|
||||
t.Fatalf("unexpected block content, got %x, want %x", block.Bytes, sentinelBytes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestJustEnd(t *testing.T) {
|
||||
pemData := `
|
||||
-----END PUBLIC KEY-----`
|
||||
|
||||
block, _ := Decode([]byte(pemData))
|
||||
if block != nil {
|
||||
t.Fatal("unexpected block")
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzDecode(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
Decode(data)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMissingEndTrailer(t *testing.T) {
|
||||
Decode([]byte{0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20})
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ func Deleteat(dirfd syscall.Handle, name string, options uint32) error {
|
||||
var h syscall.Handle
|
||||
err := NtOpenFile(
|
||||
&h,
|
||||
SYNCHRONIZE|DELETE,
|
||||
SYNCHRONIZE|FILE_READ_ATTRIBUTES|DELETE,
|
||||
objAttrs,
|
||||
&IO_STATUS_BLOCK{},
|
||||
FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||
@@ -215,14 +215,22 @@ func Deleteat(dirfd syscall.Handle, name string, options uint32) error {
|
||||
}
|
||||
defer syscall.CloseHandle(h)
|
||||
|
||||
const (
|
||||
FileDispositionInformation = 13
|
||||
FileDispositionInformationEx = 64
|
||||
)
|
||||
if TestDeleteatFallback {
|
||||
return deleteatFallback(h)
|
||||
}
|
||||
|
||||
const FileDispositionInformationEx = 64
|
||||
|
||||
// First, attempt to delete the file using POSIX semantics
|
||||
// (which permit a file to be deleted while it is still open).
|
||||
// This matches the behavior of DeleteFileW.
|
||||
//
|
||||
// The following call uses features available on different Windows versions:
|
||||
// - FILE_DISPOSITION_INFORMATION_EX: Windows 10, version 1607 (aka RS1)
|
||||
// - FILE_DISPOSITION_POSIX_SEMANTICS: Windows 10, version 1607 (aka RS1)
|
||||
// - FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE: Windows 10, version 1809 (aka RS5)
|
||||
//
|
||||
// Also, some file systems, like FAT32, don't support POSIX semantics.
|
||||
err = NtSetInformationFile(
|
||||
h,
|
||||
&IO_STATUS_BLOCK{},
|
||||
@@ -241,28 +249,57 @@ func Deleteat(dirfd syscall.Handle, name string, options uint32) error {
|
||||
switch err {
|
||||
case nil:
|
||||
return nil
|
||||
case STATUS_CANNOT_DELETE, STATUS_DIRECTORY_NOT_EMPTY:
|
||||
case STATUS_INVALID_INFO_CLASS, // the operating system doesn't support FileDispositionInformationEx
|
||||
STATUS_INVALID_PARAMETER, // the operating system doesn't support one of the flags
|
||||
STATUS_NOT_SUPPORTED: // the file system doesn't support FILE_DISPOSITION_INFORMATION_EX or one of the flags
|
||||
return deleteatFallback(h)
|
||||
default:
|
||||
return err.(NTStatus).Errno()
|
||||
}
|
||||
}
|
||||
|
||||
// If the prior deletion failed, the filesystem either doesn't support
|
||||
// POSIX semantics (for example, FAT), or hasn't implemented
|
||||
// FILE_DISPOSITION_INFORMATION_EX.
|
||||
//
|
||||
// Try again.
|
||||
err = NtSetInformationFile(
|
||||
// TestDeleteatFallback should only be used for testing purposes.
|
||||
// When set, [Deleteat] uses the fallback path unconditionally.
|
||||
var TestDeleteatFallback bool
|
||||
|
||||
// deleteatFallback is a deleteat implementation that strives
|
||||
// for compatibility with older Windows versions and file systems
|
||||
// over performance.
|
||||
func deleteatFallback(h syscall.Handle) error {
|
||||
var data syscall.ByHandleFileInformation
|
||||
if err := syscall.GetFileInformationByHandle(h, &data); err == nil && data.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 {
|
||||
// Remove read-only attribute. Reopen the file, as it was previously open without FILE_WRITE_ATTRIBUTES access
|
||||
// in order to maximize compatibility in the happy path.
|
||||
wh, err := ReOpenFile(h,
|
||||
FILE_WRITE_ATTRIBUTES,
|
||||
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
|
||||
syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = SetFileInformationByHandle(
|
||||
wh,
|
||||
FileBasicInfo,
|
||||
unsafe.Pointer(&FILE_BASIC_INFO{
|
||||
FileAttributes: data.FileAttributes &^ FILE_ATTRIBUTE_READONLY,
|
||||
}),
|
||||
uint32(unsafe.Sizeof(FILE_BASIC_INFO{})),
|
||||
)
|
||||
syscall.CloseHandle(wh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return SetFileInformationByHandle(
|
||||
h,
|
||||
&IO_STATUS_BLOCK{},
|
||||
unsafe.Pointer(&FILE_DISPOSITION_INFORMATION{
|
||||
FileDispositionInfo,
|
||||
unsafe.Pointer(&FILE_DISPOSITION_INFO{
|
||||
DeleteFile: true,
|
||||
}),
|
||||
uint32(unsafe.Sizeof(FILE_DISPOSITION_INFORMATION{})),
|
||||
FileDispositionInformation,
|
||||
uint32(unsafe.Sizeof(FILE_DISPOSITION_INFO{})),
|
||||
)
|
||||
if st, ok := err.(NTStatus); ok {
|
||||
return st.Errno()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func Renameat(olddirfd syscall.Handle, oldpath string, newdirfd syscall.Handle, newpath string) error {
|
||||
|
||||
@@ -19,6 +19,7 @@ const (
|
||||
FileBasicInfo = 0 // FILE_BASIC_INFO
|
||||
FileStandardInfo = 1 // FILE_STANDARD_INFO
|
||||
FileNameInfo = 2 // FILE_NAME_INFO
|
||||
FileDispositionInfo = 4 // FILE_DISPOSITION_INFO
|
||||
FileStreamInfo = 7 // FILE_STREAM_INFO
|
||||
FileCompressionInfo = 8 // FILE_COMPRESSION_INFO
|
||||
FileAttributeTagInfo = 9 // FILE_ATTRIBUTE_TAG_INFO
|
||||
|
||||
@@ -529,6 +529,8 @@ const (
|
||||
//sys GetOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, done *uint32, wait bool) (err error)
|
||||
//sys CreateNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
|
||||
|
||||
//sys ReOpenFile(filehandle syscall.Handle, desiredAccess uint32, shareMode uint32, flagAndAttributes uint32) (handle syscall.Handle, err error)
|
||||
|
||||
// NTStatus corresponds with NTSTATUS, error values returned by ntdll.dll and
|
||||
// other native functions.
|
||||
type NTStatus uint32
|
||||
@@ -554,6 +556,9 @@ const (
|
||||
STATUS_NOT_A_DIRECTORY NTStatus = 0xC0000103
|
||||
STATUS_CANNOT_DELETE NTStatus = 0xC0000121
|
||||
STATUS_REPARSE_POINT_ENCOUNTERED NTStatus = 0xC000050B
|
||||
STATUS_NOT_SUPPORTED NTStatus = 0xC00000BB
|
||||
STATUS_INVALID_PARAMETER NTStatus = 0xC000000D
|
||||
STATUS_INVALID_INFO_CLASS NTStatus = 0xC0000003
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -199,6 +199,11 @@ const (
|
||||
FILE_OPEN_FOR_FREE_SPACE_QUERY = 0x00800000
|
||||
)
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_disposition_info
|
||||
type FILE_DISPOSITION_INFO struct {
|
||||
DeleteFile bool
|
||||
}
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_file_disposition_information
|
||||
type FILE_DISPOSITION_INFORMATION struct {
|
||||
DeleteFile bool
|
||||
|
||||
@@ -84,6 +84,7 @@ var (
|
||||
procModule32NextW = modkernel32.NewProc("Module32NextW")
|
||||
procMoveFileExW = modkernel32.NewProc("MoveFileExW")
|
||||
procMultiByteToWideChar = modkernel32.NewProc("MultiByteToWideChar")
|
||||
procReOpenFile = modkernel32.NewProc("ReOpenFile")
|
||||
procRtlLookupFunctionEntry = modkernel32.NewProc("RtlLookupFunctionEntry")
|
||||
procRtlVirtualUnwind = modkernel32.NewProc("RtlVirtualUnwind")
|
||||
procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle")
|
||||
@@ -430,6 +431,15 @@ func MultiByteToWideChar(codePage uint32, dwFlags uint32, str *byte, nstr int32,
|
||||
return
|
||||
}
|
||||
|
||||
func ReOpenFile(filehandle syscall.Handle, desiredAccess uint32, shareMode uint32, flagAndAttributes uint32) (handle syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(procReOpenFile.Addr(), 4, uintptr(filehandle), uintptr(desiredAccess), uintptr(shareMode), uintptr(flagAndAttributes), 0, 0)
|
||||
handle = syscall.Handle(r0)
|
||||
if handle == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func RtlLookupFunctionEntry(pc uintptr, baseAddress *uintptr, table unsafe.Pointer) (ret *RUNTIME_FUNCTION) {
|
||||
r0, _, _ := syscall.Syscall(procRtlLookupFunctionEntry.Addr(), 3, uintptr(pc), uintptr(unsafe.Pointer(baseAddress)), uintptr(table))
|
||||
ret = (*RUNTIME_FUNCTION)(unsafe.Pointer(r0))
|
||||
|
||||
@@ -673,13 +673,13 @@ func parseHost(host string) (string, error) {
|
||||
|
||||
// Per RFC 3986, only a host identified by a valid
|
||||
// IPv6 address can be enclosed by square brackets.
|
||||
// This excludes any IPv4 or IPv4-mapped addresses.
|
||||
// This excludes any IPv4, but notably not IPv4-mapped addresses.
|
||||
addr, err := netip.ParseAddr(unescapedHostname)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid host: %w", err)
|
||||
}
|
||||
if addr.Is4() || addr.Is4In6() {
|
||||
return "", errors.New("invalid IPv6 host")
|
||||
if addr.Is4() {
|
||||
return "", errors.New("invalid IP-literal")
|
||||
}
|
||||
return "[" + unescapedHostname + "]" + unescapedColonPort, nil
|
||||
} else if i := strings.LastIndex(host, ":"); i != -1 {
|
||||
|
||||
@@ -726,7 +726,7 @@ var parseRequestURLTests = []struct {
|
||||
{"https://[2001:db8::1]/path", true}, // compressed IPv6 address with path
|
||||
{"https://[fe80::1%25eth0]/path?query=1", true}, // link-local with zone, path, and query
|
||||
|
||||
{"https://[::ffff:192.0.2.1]", false},
|
||||
{"https://[::ffff:192.0.2.1]", true},
|
||||
{"https://[:1] ", false},
|
||||
{"https://[1:2:3:4:5:6:7:8:9]", false},
|
||||
{"https://[1::1::1]", false},
|
||||
@@ -1672,16 +1672,17 @@ func TestParseErrors(t *testing.T) {
|
||||
{"cache_object:foo/bar", true},
|
||||
{"cache_object/:foo/bar", false},
|
||||
|
||||
{"http://[192.168.0.1]/", true}, // IPv4 in brackets
|
||||
{"http://[192.168.0.1]:8080/", true}, // IPv4 in brackets with port
|
||||
{"http://[::ffff:192.168.0.1]/", true}, // IPv4-mapped IPv6 in brackets
|
||||
{"http://[::ffff:192.168.0.1]:8080/", true}, // IPv4-mapped IPv6 in brackets with port
|
||||
{"http://[::ffff:c0a8:1]/", true}, // IPv4-mapped IPv6 in brackets (hex)
|
||||
{"http://[not-an-ip]/", true}, // invalid IP string in brackets
|
||||
{"http://[fe80::1%foo]/", true}, // invalid zone format in brackets
|
||||
{"http://[fe80::1", true}, // missing closing bracket
|
||||
{"http://fe80::1]/", true}, // missing opening bracket
|
||||
{"http://[test.com]/", true}, // domain name in brackets
|
||||
{"http://[192.168.0.1]/", true}, // IPv4 in brackets
|
||||
{"http://[192.168.0.1]:8080/", true}, // IPv4 in brackets with port
|
||||
{"http://[::ffff:192.168.0.1]/", false}, // IPv4-mapped IPv6 in brackets
|
||||
{"http://[::ffff:192.168.0.1000]/", true}, // Out of range IPv4-mapped IPv6 in brackets
|
||||
{"http://[::ffff:192.168.0.1]:8080/", false}, // IPv4-mapped IPv6 in brackets with port
|
||||
{"http://[::ffff:c0a8:1]/", false}, // IPv4-mapped IPv6 in brackets (hex)
|
||||
{"http://[not-an-ip]/", true}, // invalid IP string in brackets
|
||||
{"http://[fe80::1%foo]/", true}, // invalid zone format in brackets
|
||||
{"http://[fe80::1", true}, // missing closing bracket
|
||||
{"http://fe80::1]/", true}, // missing opening bracket
|
||||
{"http://[test.com]/", true}, // domain name in brackets
|
||||
}
|
||||
for _, tt := range tests {
|
||||
u, err := Parse(tt.in)
|
||||
|
||||
@@ -236,6 +236,23 @@ func TestRemoveAllLongPathRelative(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveAllFallback(t *testing.T) {
|
||||
windows.TestDeleteatFallback = true
|
||||
t.Cleanup(func() { windows.TestDeleteatFallback = false })
|
||||
|
||||
dir := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(dir, "file1"), []byte{}, 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(dir, "file2"), []byte{}, 0400); err != nil { // read-only file
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := os.RemoveAll(dir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func testLongPathAbs(t *testing.T, target string) {
|
||||
t.Helper()
|
||||
testWalkFn := func(path string, info os.FileInfo, err error) error {
|
||||
|
||||
@@ -22,10 +22,17 @@ const (
|
||||
// On AMD64, virtual addresses are 48-bit (or 57-bit) sign-extended.
|
||||
// Other archs are 48-bit zero-extended.
|
||||
//
|
||||
// We use one extra bit to placate systems which simulate amd64 binaries on
|
||||
// an arm64 host. Allocated arm64 addresses could be as high as 1<<48-1,
|
||||
// which would be invalid if we assumed 48-bit sign-extended addresses.
|
||||
// See issue 69255.
|
||||
// (Note that this does not help the other way around, simluating arm64
|
||||
// on amd64, but we don't have that problem at the moment.)
|
||||
//
|
||||
// On s390x, virtual addresses are 64-bit. There's not much we
|
||||
// can do about this, so we just hope that the kernel doesn't
|
||||
// get to really high addresses and panic if it does.
|
||||
defaultAddrBits = 48
|
||||
defaultAddrBits = 48 + 1
|
||||
|
||||
// On AIX, 64-bit addresses are split into 36-bit segment number and 28-bit
|
||||
// offset in segment. Segment numbers in the range 0x0A0000000-0x0AFFFFFFF(LSA)
|
||||
|
||||
35
test/fixedbugs/issue76008.go
Normal file
35
test/fixedbugs/issue76008.go
Normal file
@@ -0,0 +1,35 @@
|
||||
// run
|
||||
|
||||
// Copyright 2025 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 main
|
||||
|
||||
import "runtime"
|
||||
|
||||
func main() {
|
||||
shouldPanic(func() {
|
||||
g = any(func() {}) == any(func() {})
|
||||
})
|
||||
shouldPanic(func() {
|
||||
g = any(map[int]int{}) == any(map[int]int{})
|
||||
})
|
||||
shouldPanic(func() {
|
||||
g = any([]int{}) == any([]int{})
|
||||
})
|
||||
}
|
||||
|
||||
var g bool
|
||||
|
||||
func shouldPanic(f func()) {
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err == nil {
|
||||
_, _, line, _ := runtime.Caller(2)
|
||||
println("did not panic at line", line+1)
|
||||
}
|
||||
}()
|
||||
|
||||
f()
|
||||
}
|
||||
Reference in New Issue
Block a user