View
@@ -288,6 +288,18 @@ <h3 id="cmd_vet">Go vet</h3>
<code>-test</code> option has been removed; it was equivalent to <code>-all</code> <code>-shadow</code>.
</p>
<p id="vet_lostcancel">
The <code>vet</code> command also has a new check,
<code>-lostcancel</code>, which detects failure to call the
cancelation function returned by the <code>WithCancel</code>,
<code>WithTimeout</code>, and <code>WithDeadline</code> functions in
Go 1.7's new <code>context</code> package (see <a
href='#context'>below</a>).
Failure to call the function prevents the new <code>Context</code>
from being reclaimed until its parent is cancelled.
(The background context is never cancelled.)
</p>
<h3 id="cmd_dist">Go tool dist</h3>
<p>
@@ -770,6 +782,20 @@ <h3 id="minor_library_changes">Minor changes to the library</h3>
</dd>
</dl>
<dl id="math_rand"><dt><a href="/pkg/math/rand/">math/rand</a></dt>
<dd>
<p>
The
<a href="/pkg/math/rand/#Read"><code>Read</code></a> function and
<a href="/pkg/math/rand/#Rand"><code>Rand</code></a>'s
<a href="/pkg/math/rand/#Rand.Read"><code>Read</code></a> method
now produce a pseudo-random stream of bytes that is consistent and not
dependent on the size of the input buffer.
</p>
</dd>
</dl>
<dl id="mime_multipart"><dt><a href="/pkg/mime/multipart/">mime/multipart</a></dt>
<dd>
@@ -855,6 +881,16 @@ <h3 id="minor_library_changes">Minor changes to the library</h3>
status <code>005</code>, not just <code>5</code>.
</p>
<p>
The server implementation now correctly sends only one "Transfer-Encoding" header when "chunked"
is set explicitly, following <a href="https://tools.ietf.org/html/rfc7230#section-3.3.1">RFC 7230</a>.
</p>
<p>
In the server, a 200 status code is sent back by the timeout handler on an empty
response body, instead of sending back 0 as the status code.
</p>
<p>
In the client, the
<a href="/pkg/net/http/#Transport"><code>Transport</code></a> implementation passes the request context
@@ -1186,3 +1222,15 @@ <h3 id="minor_library_changes">Minor changes to the library</h3>
</p>
</dd>
</dl>
<dl id="unicode"><dt><a href="/pkg/unicode/">unicode</a></dt>
<dd>
<p>
The <a href="/pkg/unicode/"><code>unicode</code></a> package and associated
support throughout the system has been upgraded from version 8.0 to
<a href="http://www.unicode.org/versions/Unicode9.0.0/">Unicode 9.0</a>.
</p>
</dd>
</dl>
View
@@ -33,7 +33,7 @@ <h2 id="introduction">Introduction</h2>
</p>
<p>
The Go compilers support six instruction sets.
The Go compilers support seven instruction sets.
There are important differences in the quality of the compilers for the different
architectures.
</p>
@@ -43,15 +43,17 @@ <h2 id="introduction">Introduction</h2>
<code>amd64</code> (also known as <code>x86-64</code>)
</dt>
<dd>
A mature implementation. The compiler has an effective
optimizer (registerizer) and generates good code (although
<code>gccgo</code> can do noticeably better sometimes).
A mature implementation. New in 1.7 is its SSA-based back end
that generates compact, efficient code.
</dd>
<dt>
<code>386</code> (<code>x86</code> or <code>x86-32</code>)
</dt>
<dd>
Comparable to the <code>amd64</code> port.
Comparable to the <code>amd64</code> port, but does
not yet use the SSA-based back end. It has an effective
optimizer (registerizer) and generates good code (although
<code>gccgo</code> can do noticeably better sometimes).
</dd>
<dt>
<code>arm</code> (<code>ARM</code>)
@@ -77,6 +79,12 @@ <h2 id="introduction">Introduction</h2>
<dd>
Supports Linux binaries. New in 1.6 and not as well exercised as other ports.
</dd>
<dt>
<code>s390x</code> (IBM System z)
</dt>
<dd>
Supports Linux binaries. New in 1.7 and not as well exercised as other ports.
</dd>
</dl>
<p>
View
@@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
# Copyright 2012 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.
@@ -8,8 +8,8 @@
# Consult http://www.iana.org/time-zones for the latest versions.
# Versions to use.
CODE=2016d
DATA=2016d
CODE=2016f
DATA=2016f
set -e
rm -rf work
View
Binary file not shown.
View
@@ -0,0 +1,12 @@
// Copyright 2016 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
// void f(void *p, int x) {}
import "C"
func main() {
_ = C.f(1) // ERROR HERE
}
View
@@ -45,6 +45,7 @@ expect issue13129.go C.ushort
check issue13423.go
expect issue13635.go C.uchar C.schar C.ushort C.uint C.ulong C.longlong C.ulonglong C.complexfloat C.complexdouble
check issue13830.go
check issue16116.go
if ! go build issue14669.go; then
exit 1
View
@@ -597,13 +597,15 @@ func (p *Package) rewriteCalls(f *File) {
// rewriteCall rewrites one call to add pointer checks. We replace
// each pointer argument x with _cgoCheckPointer(x).(T).
func (p *Package) rewriteCall(f *File, call *Call, name *Name) {
// Avoid a crash if the number of arguments is
// less than the number of parameters.
// This will be caught when the generated file is compiled.
if len(call.Call.Args) < len(name.FuncType.Params) {
return
}
any := false
for i, param := range name.FuncType.Params {
if len(call.Call.Args) <= i {
// Avoid a crash; this will be caught when the
// generated file is compiled.
return
}
if p.needsPointerCheck(f, param.Go, call.Call.Args[i]) {
any = true
break
View
@@ -122,3 +122,38 @@ NextVar:
}
return out
}
// TestLineNumber checks to make sure the generated assembly has line numbers
// see issue #16214
func TestLineNumber(t *testing.T) {
testenv.MustHaveGoBuild(t)
dir, err := ioutil.TempDir("", "TestLineNumber")
if err != nil {
t.Fatalf("could not create directory: %v", err)
}
defer os.RemoveAll(dir)
src := filepath.Join(dir, "x.go")
err = ioutil.WriteFile(src, []byte(issue16214src), 0644)
if err != nil {
t.Fatalf("could not write file: %v", err)
}
cmd := exec.Command("go", "tool", "compile", "-S", "-o", filepath.Join(dir, "out.o"), src)
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("fail to run go tool compile: %v", err)
}
if strings.Contains(string(out), "unknown line number") {
t.Errorf("line number missing in assembly:\n%s", out)
}
}
var issue16214src = `
package main
func Mod32(x uint32) uint32 {
return x % 3 // frontend rewrites it as HMUL with 2863311531, the LITERAL node has Lineno 0
}
`
View
@@ -153,7 +153,13 @@ const debugFormat = false // default: false
// TODO(gri) disable and remove once there is only one export format again
const forceObjFileStability = true
const exportVersion = "v0"
// Supported export format versions.
// TODO(gri) Make this more systematic (issue #16244).
const (
exportVersion0 = "v0"
exportVersion1 = "v1"
exportVersion = exportVersion1
)
// exportInlined enables the export of inlined function bodies and related
// dependencies. The compiler should work w/o any loss of functionality with
@@ -727,6 +733,14 @@ func (p *exporter) typ(t *Type) {
p.paramList(sig.Params(), inlineable)
p.paramList(sig.Results(), inlineable)
// for issue #16243
// We make this conditional for 1.7 to avoid consistency problems
// with installed packages compiled with an older version.
// TODO(gri) Clean up after 1.7 is out (issue #16244)
if exportVersion == exportVersion1 {
p.bool(m.Nointerface)
}
var f *Func
if inlineable {
f = mfn.Func
View
@@ -21,8 +21,9 @@ import (
// changes to bimport.go and bexport.go.
type importer struct {
in *bufio.Reader
buf []byte // reused for reading strings
in *bufio.Reader
buf []byte // reused for reading strings
version string
// object lists, in order of deserialization
strList []string
@@ -67,8 +68,9 @@ func Import(in *bufio.Reader) {
// --- generic export data ---
if v := p.string(); v != exportVersion {
Fatalf("importer: unknown export data version: %s", v)
p.version = p.string()
if p.version != exportVersion0 && p.version != exportVersion1 {
Fatalf("importer: unknown export data version: %s", p.version)
}
// populate typList with predeclared "known" types
@@ -239,14 +241,20 @@ func (p *importer) pkg() *Pkg {
Fatalf("importer: package path %q for pkg index %d", path, len(p.pkgList))
}
// see importimport (export.go)
pkg := importpkg
if path != "" {
pkg = mkpkg(path)
}
if pkg.Name == "" {
pkg.Name = name
numImport[name]++
} else if pkg.Name != name {
Fatalf("importer: conflicting package names %s and %s for path %q", pkg.Name, name, path)
Yyerror("importer: conflicting package names %s and %s for path %q", pkg.Name, name, path)
}
if incannedimport == 0 && myimportpath != "" && path == myimportpath {
Yyerror("import %q: package depends on %q (import cycle)", importpkg.Path, path)
errorexit()
}
p.pkgList = append(p.pkgList, pkg)
@@ -426,10 +434,15 @@ func (p *importer) typ() *Type {
params := p.paramList()
result := p.paramList()
nointerface := false
if p.version == exportVersion1 {
nointerface = p.bool()
}
n := methodname1(newname(sym), recv[0].Right)
n.Type = functype(recv[0], params, result)
checkwidth(n.Type)
addmethod(sym, n.Type, tsym.Pkg, false, false)
addmethod(sym, n.Type, tsym.Pkg, false, nointerface)
p.funcList = append(p.funcList, n)
importlist = append(importlist, n)
View
@@ -3,7 +3,7 @@
package gc
const runtimeimport = "" +
"cn\x00\x03v0\x01\rruntime\x00\t\x11newobject\x00\x02\x17\"\vtyp·2\x00\x00" +
"cn\x00\x03v1\x01\rruntime\x00\t\x11newobject\x00\x02\x17\"\vtyp·2\x00\x00" +
"\x01\x17:\x00\t\x13panicindex\x00\x00\x00\t\x13panicslice\x00\x00\x00\t\x15pani" +
"cdivide\x00\x00\x00\t\x15throwreturn\x00\x00\x00\t\x11throwinit\x00\x00\x00" +
"\t\x11panicwrap\x00\x05 \x00 \x00 \x00\x00\t\rgopanic\x00\x01\x1b\x00\x00\x00\x00\t\x11go" +
@@ -105,6 +105,6 @@ const runtimeimport = "" +
"\x01\x02\v\x00\x01\x00\n$$\n"
const unsafeimport = "" +
"cn\x00\x03v0\x01\vunsafe\x00\x05\r\rPointer\x00\x16\x00\t\x0fOffsetof\x00\x01" +
"cn\x00\x03v1\x01\vunsafe\x00\x05\r\rPointer\x00\x16\x00\t\x0fOffsetof\x00\x01" +
":\x00\x01\x16\x00\t\vSizeof\x00\x01:\x00\x01\x16\x00\t\rAlignof\x00\x01:\x00\x01\x16\x00\v\b\x00\v" +
"\x00\x01\x00\n$$\n"
View
@@ -166,7 +166,7 @@ func closurename(n *Node) *Sym {
prefix := ""
if n.Func.Outerfunc == nil {
// Global closure.
outer = "glob"
outer = "glob."
prefix = "func"
closurename_closgen++
View
@@ -1551,10 +1551,12 @@ func esccall(e *EscState, n *Node, up *Node) {
}
var src *Node
note := ""
i := 0
lls := ll.Slice()
for t, it := IterFields(fntype.Params()); i < len(lls); i++ {
src = lls[i]
note = t.Note
if t.Isddd && !n.Isddd {
// Introduce ODDDARG node to represent ... allocation.
src = Nod(ODDDARG, nil, nil)
@@ -1566,7 +1568,7 @@ func esccall(e *EscState, n *Node, up *Node) {
}
if haspointers(t.Type) {
if escassignfromtag(e, t.Note, nE.Escretval, src) == EscNone && up.Op != ODEFER && up.Op != OPROC {
if escassignfromtag(e, note, nE.Escretval, src) == EscNone && up.Op != ODEFER && up.Op != OPROC {
a := src
for a.Op == OCONVNOP {
a = a.Left
@@ -1596,14 +1598,24 @@ func esccall(e *EscState, n *Node, up *Node) {
// This occurs when function parameter type Isddd and n not Isddd
break
}
if note == uintptrEscapesTag {
escassignSinkNilWhy(e, src, src, "escaping uintptr")
}
t = it.Next()
}
// Store arguments into slice for ... arg.
for ; i < len(lls); i++ {
if Debug['m'] > 3 {
fmt.Printf("%v::esccall:: ... <- %v\n", linestr(lineno), Nconv(lls[i], FmtShort))
}
escassignNilWhy(e, src, lls[i], "arg to ...") // args to slice
if note == uintptrEscapesTag {
escassignSinkNilWhy(e, src, lls[i], "arg to uintptrescapes ...")
} else {
escassignNilWhy(e, src, lls[i], "arg to ...")
}
}
}
@@ -1963,9 +1975,20 @@ recurse:
// lets us take the address below to get a *string.
var unsafeUintptrTag = "unsafe-uintptr"
// This special tag is applied to uintptr parameters of functions
// marked go:uintptrescapes.
const uintptrEscapesTag = "uintptr-escapes"
func esctag(e *EscState, func_ *Node) {
func_.Esc = EscFuncTagged
name := func(s *Sym, narg int) string {
if s != nil {
return s.Name
}
return fmt.Sprintf("arg#%d", narg)
}
// External functions are assumed unsafe,
// unless //go:noescape is given before the declaration.
if func_.Nbody.Len() == 0 {
@@ -1988,13 +2011,7 @@ func esctag(e *EscState, func_ *Node) {
narg++
if t.Type.Etype == TUINTPTR {
if Debug['m'] != 0 {
var name string
if t.Sym != nil {
name = t.Sym.Name
} else {
name = fmt.Sprintf("arg#%d", narg)
}
Warnl(func_.Lineno, "%v assuming %v is unsafe uintptr", funcSym(func_), name)
Warnl(func_.Lineno, "%v assuming %v is unsafe uintptr", funcSym(func_), name(t.Sym, narg))
}
t.Note = unsafeUintptrTag
}
@@ -2003,6 +2020,27 @@ func esctag(e *EscState, func_ *Node) {
return
}
if func_.Func.Pragma&UintptrEscapes != 0 {
narg := 0
for _, t := range func_.Type.Params().Fields().Slice() {
narg++
if t.Type.Etype == TUINTPTR {
if Debug['m'] != 0 {
Warnl(func_.Lineno, "%v marking %v as escaping uintptr", funcSym(func_), name(t.Sym, narg))
}
t.Note = uintptrEscapesTag
}
if t.Isddd && t.Type.Elem().Etype == TUINTPTR {
// final argument is ...uintptr.
if Debug['m'] != 0 {
Warnl(func_.Lineno, "%v marking %v as escaping ...uintptr", funcSym(func_), name(t.Sym, narg))
}
t.Note = uintptrEscapesTag
}
}
}
savefn := Curfn
Curfn = func_
@@ -2015,7 +2053,9 @@ func esctag(e *EscState, func_ *Node) {
case EscNone, // not touched by escflood
EscReturn:
if haspointers(ln.Type) { // don't bother tagging for scalars
ln.Name.Param.Field.Note = mktag(int(ln.Esc))
if ln.Name.Param.Field.Note != uintptrEscapesTag {
ln.Name.Param.Field.Note = mktag(int(ln.Esc))
}
}
case EscHeap, // touched by escflood, moved to heap
View
@@ -479,6 +479,10 @@ func pkgtype(s *Sym) *Type {
return s.Def.Type
}
// numImport tracks how often a package with a given name is imported.
// It is used to provide a better error message (by using the package
// path to disambiguate) if a package that appears multiple times with
// the same name appears in an error message.
var numImport = make(map[string]int)
func importimport(s *Sym, path string) {
View
@@ -160,6 +160,14 @@ func moveToHeap(n *Node) {
if n.Class == PPARAM {
stackcopy.SetNotLiveAtEnd(true)
}
if n.Class == PPARAMOUT {
// Make sure the pointer to the heap copy is kept live throughout the function.
// The function could panic at any point, and then a defer could recover.
// Thus, we need the pointer to the heap copy always available so the
// post-deferreturn code can copy the return value back to the stack.
// See issue 16095.
heapaddr.setIsOutputParamHeapAddr(true)
}
n.Name.Param.Stackcopy = stackcopy
// Substitute the stackcopy into the function variable list so that
View
@@ -72,6 +72,7 @@ const (
Nowritebarrier // emit compiler error instead of write barrier
Nowritebarrierrec // error on write barrier in this or recursive callees
CgoUnsafeArgs // treat a pointer to one arg as a pointer to them all
UintptrEscapes // pointers converted to uintptr escape
)
type lexer struct {
@@ -930,6 +931,19 @@ func (l *lexer) getlinepragma() rune {
l.pragma |= Nowritebarrierrec | Nowritebarrier // implies Nowritebarrier
case "go:cgo_unsafe_args":
l.pragma |= CgoUnsafeArgs
case "go:uintptrescapes":
// For the next function declared in the file
// any uintptr arguments may be pointer values
// converted to uintptr. This directive
// ensures that the referenced allocated
// object, if any, is retained and not moved
// until the call completes, even though from
// the types alone it would appear that the
// object is no longer needed during the
// call. The conversion to uintptr must appear
// in the argument list.
// Used in syscall/dll_windows.go.
l.pragma |= UintptrEscapes
}
return c
}
View
@@ -373,7 +373,7 @@ func ordercall(n *Node, order *Order) {
if t == nil {
break
}
if t.Note == unsafeUintptrTag {
if t.Note == unsafeUintptrTag || t.Note == uintptrEscapesTag {
xp := n.List.Addr(i)
for (*xp).Op == OCONVNOP && !(*xp).Type.IsPtr() {
xp = &(*xp).Left
@@ -385,7 +385,11 @@ func ordercall(n *Node, order *Order) {
*xp = x
}
}
t = it.Next()
next := it.Next()
if next == nil && t.Isddd && t.Note == uintptrEscapesTag {
next = t
}
t = next
}
}
}
View
@@ -2006,7 +2006,8 @@ func (p *parser) hidden_fndcl() *Node {
ss.Type = functype(s2[0], s6, s8)
checkwidth(ss.Type)
addmethod(s4, ss.Type, p.structpkg, false, false)
addmethod(s4, ss.Type, p.structpkg, false, p.pragma&Nointerface != 0)
p.pragma = 0
funchdr(ss)
// inl.C's inlnode in on a dotmeth node expects to find the inlineable body as
View
@@ -1175,6 +1175,19 @@ func livenessepilogue(lv *Liveness) {
all := bvalloc(nvars)
ambig := bvalloc(localswords())
// Set ambig bit for the pointers to heap-allocated pparamout variables.
// These are implicitly read by post-deferreturn code and thus must be
// kept live throughout the function (if there is any defer that recovers).
if hasdefer {
for _, n := range lv.vars {
if n.IsOutputParamHeapAddr() {
n.Name.Needzero = true
xoffset := n.Xoffset + stkptrsize
onebitwalktype1(n.Type, &xoffset, ambig)
}
}
}
for _, bb := range lv.cfg {
// Compute avarinitany and avarinitall for entry to block.
// This duplicates information known during livenesssolve
View
@@ -799,6 +799,7 @@ func typeptrdata(t *Type) int64 {
const (
tflagUncommon = 1 << 0
tflagExtraStar = 1 << 1
tflagNamed = 1 << 2
)
var dcommontype_algarray *Sym
@@ -820,14 +821,10 @@ func dcommontype(s *Sym, ot int, t *Type) int {
algsym = dalgsym(t)
}
var sptr *Sym
tptr := Ptrto(t)
if !t.IsPtr() && (t.Sym != nil || methods(tptr) != nil) {
sptr := dtypesym(tptr)
r := obj.Addrel(Linksym(s))
r.Off = 0
r.Siz = 0
r.Sym = sptr.Lsym
r.Type = obj.R_USETYPE
sptr = dtypesym(tptr)
}
gcsym, useGCProg, ptrdata := dgcsym(t)
@@ -845,7 +842,7 @@ func dcommontype(s *Sym, ot int, t *Type) int {
// alg *typeAlg
// gcdata *byte
// str nameOff
// _ int32
// ptrToThis typeOff
// }
ot = duintptr(s, ot, uint64(t.Width))
ot = duintptr(s, ot, uint64(ptrdata))
@@ -856,6 +853,9 @@ func dcommontype(s *Sym, ot int, t *Type) int {
if uncommonSize(t) != 0 {
tflag |= tflagUncommon
}
if t.Sym != nil && t.Sym.Name != "" {
tflag |= tflagNamed
}
exported := false
p := Tconv(t, FmtLeft|FmtUnsigned)
@@ -909,8 +909,12 @@ func dcommontype(s *Sym, ot int, t *Type) int {
ot = dsymptr(s, ot, gcsym, 0) // gcdata
nsym := dname(p, "", nil, exported)
ot = dsymptrOffLSym(Linksym(s), ot, nsym, 0)
ot = duint32(s, ot, 0)
ot = dsymptrOffLSym(Linksym(s), ot, nsym, 0) // str
if sptr == nil {
ot = duint32(s, ot, 0)
} else {
ot = dsymptrOffLSym(Linksym(s), ot, Linksym(sptr), 0) // ptrToThis
}
return ot
}
View
@@ -384,6 +384,14 @@ func (s *state) endBlock() *ssa.Block {
// pushLine pushes a line number on the line number stack.
func (s *state) pushLine(line int32) {
if line == 0 {
// the frontend may emit node with line number missing,
// use the parent line number in this case.
line = s.peekLine()
if Debug['K'] != 0 {
Warn("buildssa: line 0")
}
}
s.line = append(s.line, line)
}
@@ -1731,7 +1739,7 @@ func (s *state) expr(n *Node) *ssa.Value {
addop := ssa.OpAdd64F
subop := ssa.OpSub64F
pt := floatForComplex(n.Type) // Could be Float32 or Float64
wt := Types[TFLOAT64] // Compute in Float64 to minimize cancellation error
wt := Types[TFLOAT64] // Compute in Float64 to minimize cancelation error
areal := s.newValue1(ssa.OpComplexReal, pt, a)
breal := s.newValue1(ssa.OpComplexReal, pt, b)
@@ -1769,7 +1777,7 @@ func (s *state) expr(n *Node) *ssa.Value {
subop := ssa.OpSub64F
divop := ssa.OpDiv64F
pt := floatForComplex(n.Type) // Could be Float32 or Float64
wt := Types[TFLOAT64] // Compute in Float64 to minimize cancellation error
wt := Types[TFLOAT64] // Compute in Float64 to minimize cancelation error
areal := s.newValue1(ssa.OpComplexReal, pt, a)
breal := s.newValue1(ssa.OpComplexReal, pt, b)
View
@@ -79,6 +79,7 @@ const (
hasBreak = 1 << iota
notLiveAtEnd
isClosureVar
isOutputParamHeapAddr
)
func (n *Node) HasBreak() bool {
@@ -112,6 +113,17 @@ func (n *Node) setIsClosureVar(b bool) {
}
}
func (n *Node) IsOutputParamHeapAddr() bool {
return n.flags&isOutputParamHeapAddr != 0
}
func (n *Node) setIsOutputParamHeapAddr(b bool) {
if b {
n.flags |= isOutputParamHeapAddr
} else {
n.flags &^= isOutputParamHeapAddr
}
}
// Val returns the Val for the node.
func (n *Node) Val() Val {
if n.hasVal != +1 {
View
@@ -441,9 +441,8 @@ func gmove(f *gc.Node, t *gc.Node) {
gc.Regfree(&r3)
return
//warn("gmove: convert int to float not implemented: %N -> %N\n", f, t);
/*
* signed integer to float
* integer to float
*/
case gc.TINT32<<16 | gc.TFLOAT32,
gc.TINT32<<16 | gc.TFLOAT64,
@@ -452,40 +451,42 @@ func gmove(f *gc.Node, t *gc.Node) {
gc.TINT16<<16 | gc.TFLOAT32,
gc.TINT16<<16 | gc.TFLOAT64,
gc.TINT8<<16 | gc.TFLOAT32,
gc.TINT8<<16 | gc.TFLOAT64:
var r1 gc.Node
gc.Regalloc(&r1, gc.Types[gc.TINT64], nil)
gmove(f, &r1)
gc.Regalloc(&r2, gc.Types[gc.TFLOAT64], t)
p1 := gins(ppc64.AMOVD, &r1, nil)
p1.To.Type = obj.TYPE_MEM
p1.To.Reg = ppc64.REGSP
p1.To.Offset = -8
p1 = gins(ppc64.AFMOVD, nil, &r2)
p1.From.Type = obj.TYPE_MEM
p1.From.Reg = ppc64.REGSP
p1.From.Offset = -8
gins(ppc64.AFCFID, &r2, &r2)
gc.Regfree(&r1)
gmove(&r2, t)
gc.Regfree(&r2)
return
/*
* unsigned integer to float
*/
case gc.TUINT16<<16 | gc.TFLOAT32,
gc.TINT8<<16 | gc.TFLOAT64,
gc.TUINT16<<16 | gc.TFLOAT32,
gc.TUINT16<<16 | gc.TFLOAT64,
gc.TUINT8<<16 | gc.TFLOAT32,
gc.TUINT8<<16 | gc.TFLOAT64,
gc.TUINT32<<16 | gc.TFLOAT32,
gc.TUINT32<<16 | gc.TFLOAT64,
gc.TUINT64<<16 | gc.TFLOAT32,
gc.TUINT64<<16 | gc.TFLOAT64:
bignodes()
// The algorithm is:
// if small enough, use native int64 -> float64 conversion,
// otherwise halve (x -> (x>>1)|(x&1)), convert, and double.
// Note: could use FCFIDU instead if target supports it.
var r1 gc.Node
gc.Regalloc(&r1, gc.Types[gc.TUINT64], nil)
gc.Regalloc(&r1, gc.Types[gc.TINT64], nil)
gmove(f, &r1)
if ft == gc.TUINT64 {
gc.Nodreg(&r2, gc.Types[gc.TUINT64], ppc64.REGTMP)
gmove(&bigi, &r2)
gins(ppc64.ACMPU, &r1, &r2)
p1 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT64]), nil, +1)
var r3 gc.Node
gc.Regalloc(&r3, gc.Types[gc.TUINT64], nil)
p2 := gins(ppc64.AANDCC, nil, &r3) // andi.
p2.Reg = r1.Reg
p2.From.Type = obj.TYPE_CONST
p2.From.Offset = 1
p3 := gins(ppc64.ASRD, nil, &r1)
p3.From.Type = obj.TYPE_CONST
p3.From.Offset = 1
gins(ppc64.AOR, &r3, &r1)
gc.Regfree(&r3)
gc.Patch(p1, gc.Pc)
}
gc.Regalloc(&r2, gc.Types[gc.TFLOAT64], t)
p1 := gins(ppc64.AMOVD, &r1, nil)
p1.To.Type = obj.TYPE_MEM
@@ -495,8 +496,14 @@ func gmove(f *gc.Node, t *gc.Node) {
p1.From.Type = obj.TYPE_MEM
p1.From.Reg = ppc64.REGSP
p1.From.Offset = -8
gins(ppc64.AFCFIDU, &r2, &r2)
gins(ppc64.AFCFID, &r2, &r2)
gc.Regfree(&r1)
if ft == gc.TUINT64 {
p1 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT64]), nil, +1) // use CR0 here again
gc.Nodreg(&r1, gc.Types[gc.TFLOAT64], ppc64.FREGTWO)
gins(ppc64.AFMUL, &r1, &r2)
gc.Patch(p1, gc.Pc)
}
gmove(&r2, t)
gc.Regfree(&r2)
return
View
@@ -508,8 +508,7 @@
// searches for a branch or tag named "go1". If no such version exists it
// retrieves the most recent version of the package.
//
// Unless vendoring support is disabled (see 'go help gopath'),
// when go get checks out or updates a Git repository,
// When go get checks out or updates a Git repository,
// it also updates any git submodules referenced by the repository.
//
// Get never checks out or updates code stored in vendor directories.
@@ -1271,10 +1270,9 @@
// let package authors make sure the custom import path is used and not a
// direct path to the underlying code hosting site.
//
// If vendoring is enabled (see 'go help gopath'), then import path checking is
// disabled for code found within vendor trees. This makes it possible to copy
// code into alternate locations in vendor trees without needing to update import
// comments.
// Import path checking is disabled for code found within vendor trees.
// This makes it possible to copy code into alternate locations in vendor trees
// without needing to update import comments.
//
// See https://golang.org/s/go14customimport for details.
//
View
@@ -14,7 +14,6 @@ import (
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
)
@@ -276,8 +275,8 @@ func isGoGenerate(buf []byte) bool {
// single go:generate command.
func (g *Generator) setEnv() {
g.env = []string{
"GOARCH=" + runtime.GOARCH,
"GOOS=" + runtime.GOOS,
"GOARCH=" + buildContext.GOARCH,
"GOOS=" + buildContext.GOOS,
"GOFILE=" + g.file,
"GOLINE=" + strconv.Itoa(g.lineNum),
"GOPACKAGE=" + g.pkg,
View
@@ -55,8 +55,7 @@ rule is that if the local installation is running version "go1", get
searches for a branch or tag named "go1". If no such version exists it
retrieves the most recent version of the package.
Unless vendoring support is disabled (see 'go help gopath'),
when go get checks out or updates a Git repository,
When go get checks out or updates a Git repository,
it also updates any git submodules referenced by the repository.
Get never checks out or updates code stored in vendor directories.
View
@@ -489,6 +489,16 @@ func (tg *testgoData) path(name string) string {
return filepath.Join(tg.tempdir, name)
}
// mustExist fails if path does not exist.
func (tg *testgoData) mustExist(path string) {
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
tg.t.Fatalf("%s does not exist but should", path)
}
tg.t.Fatalf("%s stat failed: %v", path, err)
}
}
// mustNotExist fails if path exists.
func (tg *testgoData) mustNotExist(path string) {
if _, err := os.Stat(path); err == nil || !os.IsNotExist(err) {
@@ -2920,3 +2930,27 @@ func TestAlwaysLinkSysoFiles(t *testing.T) {
tg.run("list", "-f", "{{.SysoFiles}}", "syso")
tg.grepStdout("a.syso", "missing syso file with CGO_ENABLED=0")
}
// Issue 16120.
func TestGenerateUsesBuildContext(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("this test won't run under Windows")
}
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.tempDir("src/gen")
tg.tempFile("src/gen/gen.go", "package gen\n//go:generate echo $GOOS $GOARCH\n")
tg.setenv("GOPATH", tg.path("."))
tg.setenv("GOOS", "linux")
tg.setenv("GOARCH", "amd64")
tg.run("generate", "gen")
tg.grepStdout("linux amd64", "unexpected GOOS/GOARCH combination")
tg.setenv("GOOS", "darwin")
tg.setenv("GOARCH", "386")
tg.run("generate", "gen")
tg.grepStdout("darwin 386", "unexpected GOOS/GOARCH combination")
}
View
@@ -261,10 +261,9 @@ unless it is being referred to by that import path. In this way, import comments
let package authors make sure the custom import path is used and not a
direct path to the underlying code hosting site.
If vendoring is enabled (see 'go help gopath'), then import path checking is
disabled for code found within vendor trees. This makes it possible to copy
code into alternate locations in vendor trees without needing to update import
comments.
Import path checking is disabled for code found within vendor trees.
This makes it possible to copy code into alternate locations in vendor trees
without needing to update import comments.
See https://golang.org/s/go14customimport for details.
`,
View
@@ -383,9 +383,6 @@ func (v *vcsCmd) ping(scheme, repo string) error {
// The parent of dir must exist; dir must not.
func (v *vcsCmd) create(dir, repo string) error {
for _, cmd := range v.createCmd {
if strings.Contains(cmd, "submodule") {
continue
}
if err := v.run(".", cmd, "dir", dir, "repo", repo); err != nil {
return err
}
@@ -396,9 +393,6 @@ func (v *vcsCmd) create(dir, repo string) error {
// download downloads any new changes for the repo in dir.
func (v *vcsCmd) download(dir string) error {
for _, cmd := range v.downloadCmd {
if strings.Contains(cmd, "submodule") {
continue
}
if err := v.run(dir, cmd); err != nil {
return err
}
@@ -445,9 +439,6 @@ func (v *vcsCmd) tagSync(dir, tag string) error {
if tag == "" && v.tagSyncDefault != nil {
for _, cmd := range v.tagSyncDefault {
if strings.Contains(cmd, "submodule") {
continue
}
if err := v.run(dir, cmd); err != nil {
return err
}
@@ -456,9 +447,6 @@ func (v *vcsCmd) tagSync(dir, tag string) error {
}
for _, cmd := range v.tagSyncCmd {
if strings.Contains(cmd, "submodule") {
continue
}
if err := v.run(dir, cmd, "tag", tag); err != nil {
return err
}
@@ -870,7 +858,6 @@ var vcsPaths = []*vcsPath{
re: `^(?P<root>git\.openstack\.org/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(\.git)?(/[A-Za-z0-9_.\-]+)*$`,
vcs: "git",
repo: "https://{root}",
check: noVCSSuffix,
},
// General syntax for any server.
View
@@ -197,6 +197,7 @@ func TestGetSubmodules(t *testing.T) {
tg.setenv("GOPATH", tg.path("."))
tg.run("get", "-d", "github.com/rsc/go-get-issue-12612")
tg.run("get", "-u", "-d", "github.com/rsc/go-get-issue-12612")
tg.mustExist(tg.path("src/github.com/rsc/go-get-issue-12612/vendor/golang.org/x/crypto/.git"))
}
func TestVendorCache(t *testing.T) {
View
@@ -9,6 +9,7 @@ import (
"debug/gosym"
"flag"
"fmt"
"net/url"
"os"
"regexp"
"strings"
@@ -50,7 +51,16 @@ func symbolize(mode, source string, p *profile.Profile, obj plugin.ObjTool, ui p
ui.PrintErr("expecting -symbolize=[local|remote|none][:force]")
fallthrough
case "", "force":
// Ignore these options, -force is recognized by symbolizer.Symbolize
// -force is recognized by symbolizer.Symbolize.
// If the source is remote, and the mapping file
// does not exist, don't use local symbolization.
if isRemote(source) {
if len(p.Mapping) == 0 {
local = false
} else if _, err := os.Stat(p.Mapping[0].File); err != nil {
local = false
}
}
}
}
@@ -67,6 +77,21 @@ func symbolize(mode, source string, p *profile.Profile, obj plugin.ObjTool, ui p
return err
}
// isRemote returns whether source is a URL for a remote source.
func isRemote(source string) bool {
url, err := url.Parse(source)
if err != nil {
url, err = url.Parse("http://" + source)
if err != nil {
return false
}
}
if scheme := strings.ToLower(url.Scheme); scheme == "" || scheme == "file" {
return false
}
return true
}
// flags implements the driver.FlagPackage interface using the builtin flag package.
type flags struct {
}
@@ -117,8 +142,10 @@ func (*objTool) Open(name string, start uint64) (plugin.ObjFile, error) {
name: name,
file: of,
}
if load, err := of.LoadAddress(); err == nil {
f.offset = start - load
if start != 0 {
if load, err := of.LoadAddress(); err == nil {
f.offset = start - load
}
}
return f, nil
}
@@ -306,6 +333,11 @@ func (f *file) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) {
}
var out []*plugin.Sym
for _, s := range f.sym {
// Ignore a symbol with address 0 and size 0.
// An ELF STT_FILE symbol will look like that.
if s.Addr == 0 && s.Size == 0 {
continue
}
if (r == nil || r.MatchString(s.Name)) && (addr == 0 || s.Addr <= addr && addr < s.Addr+uint64(s.Size)) {
out = append(out, &plugin.Sym{
Name: []string{s.Name},
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -125,14 +125,10 @@ func checkCopyLocksFunc(f *File, name string, recv *ast.FieldList, typ *ast.Func
}
}
if typ.Results != nil {
for _, field := range typ.Results.List {
expr := field.Type
if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil {
f.Badf(expr.Pos(), "%s returns lock by value: %v", name, path)
}
}
}
// Don't check typ.Results. If T has a Lock field it's OK to write
// return T{}
// because that is returning the zero value. Leave result checking
// to the return statement.
}
// checkCopyLocksRange checks whether a range statement
@@ -194,6 +190,16 @@ func lockPathRhs(f *File, x ast.Expr) typePath {
if _, ok := x.(*ast.CompositeLit); ok {
return nil
}
if _, ok := x.(*ast.CallExpr); ok {
// A call may return a zero value.
return nil
}
if star, ok := x.(*ast.StarExpr); ok {
if _, ok := star.X.(*ast.CallExpr); ok {
// A call may return a pointer to a zero value.
return nil
}
}
return lockPath(f.pkg.typesPkg, f.pkg.types[x].Type)
}
View
@@ -29,6 +29,8 @@ type deadState struct {
}
// checkUnreachable checks a function body for dead code.
//
// TODO(adonovan): use the new cfg package, which is more precise.
func checkUnreachable(f *File, node ast.Node) {
var body *ast.BlockStmt
switch n := node.(type) {
View
@@ -91,6 +91,15 @@ Flag: -tests
Mistakes involving tests including functions with incorrect names or signatures
and example tests that document identifiers not in the package.
Failure to call the cancelation function returned by context.WithCancel.
Flag: -lostcancel
The cancelation function returned by context.WithCancel, WithTimeout,
and WithDeadline must be called or the new context will remain live
until its parent context is cancelled.
(The background context is never cancelled.)
Methods
Flag: -methods
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -0,0 +1,142 @@
// Copyright 2016 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.
// This package constructs a simple control-flow graph (CFG) of the
// statements and expressions within a single function.
//
// Use cfg.New to construct the CFG for a function body.
//
// The blocks of the CFG contain all the function's non-control
// statements. The CFG does not contain control statements such as If,
// Switch, Select, and Branch, but does contain their subexpressions.
// For example, this source code:
//
// if x := f(); x != nil {
// T()
// } else {
// F()
// }
//
// produces this CFG:
//
// 1: x := f()
// x != nil
// succs: 2, 3
// 2: T()
// succs: 4
// 3: F()
// succs: 4
// 4:
//
// The CFG does contain Return statements; even implicit returns are
// materialized (at the position of the function's closing brace).
//
// The CFG does not record conditions associated with conditional branch
// edges, nor the short-circuit semantics of the && and || operators,
// nor abnormal control flow caused by panic. If you need this
// information, use golang.org/x/tools/go/ssa instead.
//
package cfg
// Although the vet tool has type information, it is often extremely
// fragmentary, so for simplicity this package does not depend on
// go/types. Consequently control-flow conditions are ignored even
// when constant, and "mayReturn" information must be provided by the
// client.
import (
"bytes"
"fmt"
"go/ast"
"go/format"
"go/token"
)
// A CFG represents the control-flow graph of a single function.
//
// The entry point is Blocks[0]; there may be multiple return blocks.
type CFG struct {
Blocks []*Block // block[0] is entry; order otherwise undefined
}
// A Block represents a basic block: a list of statements and
// expressions that are always evaluated sequentially.
//
// A block may have 0-2 successors: zero for a return block or a block
// that calls a function such as panic that never returns; one for a
// normal (jump) block; and two for a conditional (if) block.
type Block struct {
Nodes []ast.Node // statements, expressions, and ValueSpecs
Succs []*Block // successor nodes in the graph
comment string // for debugging
index int32 // index within CFG.Blocks
unreachable bool // is block of stmts following return/panic/for{}
succs2 [2]*Block // underlying array for Succs
}
// New returns a new control-flow graph for the specified function body,
// which must be non-nil.
//
// The CFG builder calls mayReturn to determine whether a given function
// call may return. For example, calls to panic, os.Exit, and log.Fatal
// do not return, so the builder can remove infeasible graph edges
// following such calls. The builder calls mayReturn only for a
// CallExpr beneath an ExprStmt.
func New(body *ast.BlockStmt, mayReturn func(*ast.CallExpr) bool) *CFG {
b := builder{
mayReturn: mayReturn,
cfg: new(CFG),
}
b.current = b.newBlock("entry")
b.stmt(body)
// Does control fall off the end of the function's body?
// Make implicit return explicit.
if b.current != nil && !b.current.unreachable {
b.add(&ast.ReturnStmt{
Return: body.End() - 1,
})
}
return b.cfg
}
func (b *Block) String() string {
return fmt.Sprintf("block %d (%s)", b.index, b.comment)
}
// Return returns the return statement at the end of this block if present, nil otherwise.
func (b *Block) Return() (ret *ast.ReturnStmt) {
if len(b.Nodes) > 0 {
ret, _ = b.Nodes[len(b.Nodes)-1].(*ast.ReturnStmt)
}
return
}
// Format formats the control-flow graph for ease of debugging.
func (g *CFG) Format(fset *token.FileSet) string {
var buf bytes.Buffer
for _, b := range g.Blocks {
fmt.Fprintf(&buf, ".%d: # %s\n", b.index, b.comment)
for _, n := range b.Nodes {
fmt.Fprintf(&buf, "\t%s\n", formatNode(fset, n))
}
if len(b.Succs) > 0 {
fmt.Fprintf(&buf, "\tsuccs:")
for _, succ := range b.Succs {
fmt.Fprintf(&buf, " %d", succ.index)
}
buf.WriteByte('\n')
}
buf.WriteByte('\n')
}
return buf.String()
}
func formatNode(fset *token.FileSet, n ast.Node) string {
var buf bytes.Buffer
format.Node(&buf, fset, n)
// Indent secondary lines by a tab.
return string(bytes.Replace(buf.Bytes(), []byte("\n"), []byte("\n\t"), -1))
}
View
@@ -0,0 +1,190 @@
// Copyright 2016 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 cfg
import (
"bytes"
"fmt"
"go/ast"
"go/parser"
"go/token"
"testing"
)
const src = `package main
import "log"
func f1() {
live()
return
dead()
}
func f2() {
for {
live()
}
dead()
}
func f3() {
if true { // even known values are ignored
return
}
for true { // even known values are ignored
live()
}
for {
live()
}
dead()
}
func f4(x int) {
switch x {
case 1:
live()
fallthrough
case 2:
live()
log.Fatal()
default:
panic("oops")
}
dead()
}
func f4(ch chan int) {
select {
case <-ch:
live()
return
default:
live()
panic("oops")
}
dead()
}
func f5(unknown bool) {
for {
if unknown {
break
}
continue
dead()
}
live()
}
func f6(unknown bool) {
outer:
for {
for {
break outer
dead()
}
dead()
}
live()
}
func f7() {
for {
break nosuchlabel
dead()
}
dead()
}
func f8() {
select{}
dead()
}
func f9(ch chan int) {
select {
case <-ch:
return
}
dead()
}
func f10(ch chan int) {
select {
case <-ch:
return
dead()
default:
}
live()
}
func f11() {
goto; // mustn't crash
dead()
}
`
func TestDeadCode(t *testing.T) {
// We'll use dead code detection to verify the CFG.
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "dummy.go", src, parser.Mode(0))
if err != nil {
t.Fatal(err)
}
for _, decl := range f.Decls {
if decl, ok := decl.(*ast.FuncDecl); ok {
g := New(decl.Body, mayReturn)
// Mark blocks reachable from entry.
live := make(map[*Block]bool)
var visit func(*Block)
visit = func(b *Block) {
if !live[b] {
live[b] = true
for _, succ := range b.Succs {
visit(succ)
}
}
}
visit(g.Blocks[0])
// Print statements in unreachable blocks
// (in order determined by builder).
var buf bytes.Buffer
for _, b := range g.Blocks {
if !live[b] {
for _, n := range b.Nodes {
fmt.Fprintf(&buf, "\t%s\n", formatNode(fset, n))
}
}
}
// Check that the result contains "dead" at least once but not "live".
if !bytes.Contains(buf.Bytes(), []byte("dead")) ||
bytes.Contains(buf.Bytes(), []byte("live")) {
t.Errorf("unexpected dead statements in function %s:\n%s",
decl.Name.Name,
&buf)
t.Logf("control flow graph:\n%s", g.Format(fset))
}
}
}
}
// A trivial mayReturn predicate that looks only at syntax, not types.
func mayReturn(call *ast.CallExpr) bool {
switch fun := call.Fun.(type) {
case *ast.Ident:
return fun.Name != "panic"
case *ast.SelectorExpr:
return fun.Sel.Name != "Fatal"
}
return true
}
View
@@ -0,0 +1,318 @@
// Copyright 2016 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 (
"cmd/vet/internal/cfg"
"fmt"
"go/ast"
"go/types"
"strconv"
)
func init() {
register("lostcancel",
"check for failure to call cancelation function returned by context.WithCancel",
checkLostCancel,
funcDecl, funcLit)
}
const debugLostCancel = false
var contextPackage = "context"
// checkLostCancel reports a failure to the call the cancel function
// returned by context.WithCancel, either because the variable was
// assigned to the blank identifier, or because there exists a
// control-flow path from the call to a return statement and that path
// does not "use" the cancel function. Any reference to the variable
// counts as a use, even within a nested function literal.
//
// checkLostCancel analyzes a single named or literal function.
func checkLostCancel(f *File, node ast.Node) {
// Fast path: bypass check if file doesn't use context.WithCancel.
if !hasImport(f.file, contextPackage) {
return
}
// Maps each cancel variable to its defining ValueSpec/AssignStmt.
cancelvars := make(map[*types.Var]ast.Node)
// Find the set of cancel vars to analyze.
stack := make([]ast.Node, 0, 32)
ast.Inspect(node, func(n ast.Node) bool {
switch n.(type) {
case *ast.FuncLit:
if len(stack) > 0 {
return false // don't stray into nested functions
}
case nil:
stack = stack[:len(stack)-1] // pop
return true
}
stack = append(stack, n) // push
// Look for [{AssignStmt,ValueSpec} CallExpr SelectorExpr]:
//
// ctx, cancel := context.WithCancel(...)
// ctx, cancel = context.WithCancel(...)
// var ctx, cancel = context.WithCancel(...)
//
if isContextWithCancel(f, n) && isCall(stack[len(stack)-2]) {
var id *ast.Ident // id of cancel var
stmt := stack[len(stack)-3]
switch stmt := stmt.(type) {
case *ast.ValueSpec:
if len(stmt.Names) > 1 {
id = stmt.Names[1]
}
case *ast.AssignStmt:
if len(stmt.Lhs) > 1 {
id, _ = stmt.Lhs[1].(*ast.Ident)
}
}
if id != nil {
if id.Name == "_" {
f.Badf(id.Pos(), "the cancel function returned by context.%s should be called, not discarded, to avoid a context leak",
n.(*ast.SelectorExpr).Sel.Name)
} else if v, ok := f.pkg.uses[id].(*types.Var); ok {
cancelvars[v] = stmt
} else if v, ok := f.pkg.defs[id].(*types.Var); ok {
cancelvars[v] = stmt
}
}
}
return true
})
if len(cancelvars) == 0 {
return // no need to build CFG
}
// Tell the CFG builder which functions never return.
info := &types.Info{Uses: f.pkg.uses, Selections: f.pkg.selectors}
mayReturn := func(call *ast.CallExpr) bool {
name := callName(info, call)
return !noReturnFuncs[name]
}
// Build the CFG.
var g *cfg.CFG
var sig *types.Signature
switch node := node.(type) {
case *ast.FuncDecl:
sig, _ = f.pkg.defs[node.Name].Type().(*types.Signature)
g = cfg.New(node.Body, mayReturn)
case *ast.FuncLit:
sig, _ = f.pkg.types[node.Type].Type.(*types.Signature)
g = cfg.New(node.Body, mayReturn)
}
// Print CFG.
if debugLostCancel {
fmt.Println(g.Format(f.fset))
}
// Examine the CFG for each variable in turn.
// (It would be more efficient to analyze all cancelvars in a
// single pass over the AST, but seldom is there more than one.)
for v, stmt := range cancelvars {
if ret := lostCancelPath(f, g, v, stmt, sig); ret != nil {
lineno := f.fset.Position(stmt.Pos()).Line
f.Badf(stmt.Pos(), "the %s function is not used on all paths (possible context leak)", v.Name())
f.Badf(ret.Pos(), "this return statement may be reached without using the %s var defined on line %d", v.Name(), lineno)
}
}
}
func isCall(n ast.Node) bool { _, ok := n.(*ast.CallExpr); return ok }
func hasImport(f *ast.File, path string) bool {
for _, imp := range f.Imports {
v, _ := strconv.Unquote(imp.Path.Value)
if v == path {
return true
}
}
return false
}
// isContextWithCancel reports whether n is one of the qualified identifiers
// context.With{Cancel,Timeout,Deadline}.
func isContextWithCancel(f *File, n ast.Node) bool {
if sel, ok := n.(*ast.SelectorExpr); ok {
switch sel.Sel.Name {
case "WithCancel", "WithTimeout", "WithDeadline":
if x, ok := sel.X.(*ast.Ident); ok {
if pkgname, ok := f.pkg.uses[x].(*types.PkgName); ok {
return pkgname.Imported().Path() == contextPackage
}
// Import failed, so we can't check package path.
// Just check the local package name (heuristic).
return x.Name == "context"
}
}
}
return false
}
// lostCancelPath finds a path through the CFG, from stmt (which defines
// the 'cancel' variable v) to a return statement, that doesn't "use" v.
// If it finds one, it returns the return statement (which may be synthetic).
// sig is the function's type, if known.
func lostCancelPath(f *File, g *cfg.CFG, v *types.Var, stmt ast.Node, sig *types.Signature) *ast.ReturnStmt {
vIsNamedResult := sig != nil && tupleContains(sig.Results(), v)
// uses reports whether stmts contain a "use" of variable v.
uses := func(f *File, v *types.Var, stmts []ast.Node) bool {
found := false
for _, stmt := range stmts {
ast.Inspect(stmt, func(n ast.Node) bool {
switch n := n.(type) {
case *ast.Ident:
if f.pkg.uses[n] == v {
found = true
}
case *ast.ReturnStmt:
// A naked return statement counts as a use
// of the named result variables.
if n.Results == nil && vIsNamedResult {
found = true
}
}
return !found
})
}
return found
}
// blockUses computes "uses" for each block, caching the result.
memo := make(map[*cfg.Block]bool)
blockUses := func(f *File, v *types.Var, b *cfg.Block) bool {
res, ok := memo[b]
if !ok {
res = uses(f, v, b.Nodes)
memo[b] = res
}
return res
}
// Find the var's defining block in the CFG,
// plus the rest of the statements of that block.
var defblock *cfg.Block
var rest []ast.Node
outer:
for _, b := range g.Blocks {
for i, n := range b.Nodes {
if n == stmt {
defblock = b
rest = b.Nodes[i+1:]
break outer
}
}
}
if defblock == nil {
panic("internal error: can't find defining block for cancel var")
}
// Is v "used" in the remainder of its defining block?
if uses(f, v, rest) {
return nil
}
// Does the defining block return without using v?
if ret := defblock.Return(); ret != nil {
return ret
}
// Search the CFG depth-first for a path, from defblock to a
// return block, in which v is never "used".
seen := make(map[*cfg.Block]bool)
var search func(blocks []*cfg.Block) *ast.ReturnStmt
search = func(blocks []*cfg.Block) *ast.ReturnStmt {
for _, b := range blocks {
if !seen[b] {
seen[b] = true
// Prune the search if the block uses v.
if blockUses(f, v, b) {
continue
}
// Found path to return statement?
if ret := b.Return(); ret != nil {
if debugLostCancel {
fmt.Printf("found path to return in block %s\n", b)
}
return ret // found
}
// Recur
if ret := search(b.Succs); ret != nil {
if debugLostCancel {
fmt.Printf(" from block %s\n", b)
}
return ret
}
}
}
return nil
}
return search(defblock.Succs)
}
func tupleContains(tuple *types.Tuple, v *types.Var) bool {
for i := 0; i < tuple.Len(); i++ {
if tuple.At(i) == v {
return true
}
}
return false
}
var noReturnFuncs = map[string]bool{
"(*testing.common).FailNow": true,
"(*testing.common).Fatal": true,
"(*testing.common).Fatalf": true,
"(*testing.common).Skip": true,
"(*testing.common).SkipNow": true,
"(*testing.common).Skipf": true,
"log.Fatal": true,
"log.Fatalf": true,
"log.Fatalln": true,
"os.Exit": true,
"panic": true,
"runtime.Goexit": true,
}
// callName returns the canonical name of the builtin, method, or
// function called by call, if known.
func callName(info *types.Info, call *ast.CallExpr) string {
switch fun := call.Fun.(type) {
case *ast.Ident:
// builtin, e.g. "panic"
if obj, ok := info.Uses[fun].(*types.Builtin); ok {
return obj.Name()
}
case *ast.SelectorExpr:
if sel, ok := info.Selections[fun]; ok && sel.Kind() == types.MethodVal {
// method call, e.g. "(*testing.common).Fatal"
meth := sel.Obj()
return fmt.Sprintf("(%s).%s",
meth.Type().(*types.Signature).Recv().Type(),
meth.Name())
}
if obj, ok := info.Uses[fun.Sel]; ok {
// qualified identifier, e.g. "os.Exit"
return fmt.Sprintf("%s.%s",
obj.Pkg().Path(),
obj.Name())
}
}
// function with no name, or defined in missing imported package
return ""
}
View
@@ -182,6 +182,9 @@ type File struct {
file *ast.File
b bytes.Buffer // for use by methods
// Parsed package "foo" when checking package "foo_test"
basePkg *Package
// The objects that are receivers of a "String() string" method.
// This is used by the recursiveStringer method in print.go.
stringers map[*ast.Object]bool
@@ -238,7 +241,7 @@ func main() {
}
os.Exit(exitCode)
}
if !doPackage(".", flag.Args()) {
if doPackage(".", flag.Args(), nil) == nil {
warnf("no files checked")
}
os.Exit(exitCode)
@@ -278,12 +281,12 @@ func doPackageDir(directory string) {
names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package.
names = append(names, pkg.SFiles...)
prefixDirectory(directory, names)
doPackage(directory, names)
basePkg := doPackage(directory, names, nil)
// Is there also a "foo_test" package? If so, do that one as well.
if len(pkg.XTestGoFiles) > 0 {
names = pkg.XTestGoFiles
prefixDirectory(directory, names)
doPackage(directory, names)
doPackage(directory, names, basePkg)
}
}
@@ -299,8 +302,8 @@ type Package struct {
}
// doPackage analyzes the single package constructed from the named files.
// It returns whether any files were checked.
func doPackage(directory string, names []string) bool {
// It returns the parsed Package or nil if none of the files have been checked.
func doPackage(directory string, names []string, basePkg *Package) *Package {
var files []*File
var astFiles []*ast.File
fs := token.NewFileSet()
@@ -309,22 +312,22 @@ func doPackage(directory string, names []string) bool {
if err != nil {
// Warn but continue to next package.
warnf("%s: %s", name, err)
return false
return nil
}
checkBuildTag(name, data)
var parsedFile *ast.File
if strings.HasSuffix(name, ".go") {
parsedFile, err = parser.ParseFile(fs, name, data, 0)
if err != nil {
warnf("%s: %s", name, err)
return false
return nil
}
astFiles = append(astFiles, parsedFile)
}
files = append(files, &File{fset: fs, content: data, name: name, file: parsedFile})
}
if len(astFiles) == 0 {
return false
return nil
}
pkg := new(Package)
pkg.path = astFiles[0].Name.Name
@@ -346,13 +349,14 @@ func doPackage(directory string, names []string) bool {
}
for _, file := range files {
file.pkg = pkg
file.basePkg = basePkg
file.checkers = chk
if file.file != nil {
file.walkFile(file.name, file.file)
}
}
asmCheck(pkg)
return true
return pkg
}
func visit(path string, f os.FileInfo, err error) error {
View
@@ -12,7 +12,7 @@ import "sync"
func OkFunc(*sync.Mutex) {}
func BadFunc(sync.Mutex) {} // ERROR "BadFunc passes lock by value: sync.Mutex"
func OkRet() *sync.Mutex {}
func BadRet() sync.Mutex {} // ERROR "BadRet returns lock by value: sync.Mutex"
func BadRet() sync.Mutex {} // Don't warn about results
var (
OkClosure = func(*sync.Mutex) {}
@@ -28,7 +28,7 @@ func (EmbeddedRWMutex) BadMeth() {} // ERROR "BadMeth passes lock by value: test
func OkFunc(e *EmbeddedRWMutex) {}
func BadFunc(EmbeddedRWMutex) {} // ERROR "BadFunc passes lock by value: testdata.EmbeddedRWMutex"
func OkRet() *EmbeddedRWMutex {}
func BadRet() EmbeddedRWMutex {} // ERROR "BadRet returns lock by value: testdata.EmbeddedRWMutex"
func BadRet() EmbeddedRWMutex {} // Don't warn about results
type FieldMutex struct {
s sync.Mutex
@@ -107,6 +107,14 @@ func ReturnViaInterface(x int) (int, interface{}) {
}
}
// Some cases that we don't warn about.
func AcceptedCases() {
x := EmbeddedRwMutex{} // composite literal on RHS is OK (#16227)
x = BadRet() // function call on RHS is OK (#16227)
x = *OKRet() // indirection of function call on RHS is OK (#16227)
}
// TODO: Unfortunate cases
// Non-ideal error message:
View
@@ -4,11 +4,11 @@ package buf_test
func Example() {} // OK because is package-level.
func Example_suffix() // OK because refers to suffix annotation.
func Example_suffix() {} // OK because refers to suffix annotation.
func Example_BadSuffix() // ERROR "Example_BadSuffix has malformed example suffix: BadSuffix"
func Example_BadSuffix() {} // ERROR "Example_BadSuffix has malformed example suffix: BadSuffix"
func ExampleBuf() // OK because refers to known top-level type.
func ExampleBuf() {} // OK because refers to known top-level type.
func ExampleBuf_Append() {} // OK because refers to known method.
@@ -28,8 +28,8 @@ func ExampleBuf_Len(i int) {} // ERROR "ExampleBuf_Len should be niladic"
// "Puffer" is German for "Buffer".
func ExamplePuffer() // ERROR "ExamplePuffer refers to unknown identifier: Puffer"
func ExamplePuffer() {} // ERROR "ExamplePuffer refers to unknown identifier: Puffer"
func ExamplePuffer_Append() // ERROR "ExamplePuffer_Append refers to unknown identifier: Puffer"
func ExamplePuffer_Append() {} // ERROR "ExamplePuffer_Append refers to unknown identifier: Puffer"
func ExamplePuffer_suffix() // ERROR "ExamplePuffer_suffix refers to unknown identifier: Puffer"
func ExamplePuffer_suffix() {} // ERROR "ExamplePuffer_suffix refers to unknown identifier: Puffer"
View
@@ -0,0 +1,155 @@
// Copyright 2016 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 testdata
import (
"context"
"log"
"os"
"testing"
)
// Check the three functions and assignment forms (var, :=, =) we look for.
// (Do these early: line numbers are fragile.)
func _() {
var ctx, cancel = context.WithCancel() // ERROR "the cancel function is not used on all paths \(possible context leak\)"
} // ERROR "this return statement may be reached without using the cancel var defined on line 17"
func _() {
ctx, cancel2 := context.WithDeadline() // ERROR "the cancel2 function is not used..."
} // ERROR "may be reached without using the cancel2 var defined on line 21"
func _() {
var ctx context.Context
var cancel3 func()
ctx, cancel3 = context.WithTimeout() // ERROR "function is not used..."
} // ERROR "this return statement may be reached without using the cancel3 var defined on line 27"
func _() {
ctx, _ := context.WithCancel() // ERROR "the cancel function returned by context.WithCancel should be called, not discarded, to avoid a context leak"
ctx, _ = context.WithTimeout() // ERROR "the cancel function returned by context.WithTimeout should be called, not discarded, to avoid a context leak"
ctx, _ = context.WithDeadline() // ERROR "the cancel function returned by context.WithDeadline should be called, not discarded, to avoid a context leak"
}
func _() {
ctx, cancel := context.WithCancel()
defer cancel() // ok
}
func _() {
ctx, cancel := context.WithCancel() // ERROR "not used on all paths"
if condition {
cancel()
}
return // ERROR "this return statement may be reached without using the cancel var"
}
func _() {
ctx, cancel := context.WithCancel()
if condition {
cancel()
} else {
// ok: infinite loop
for {
print(0)
}
}
}
func _() {
ctx, cancel := context.WithCancel() // ERROR "not used on all paths"
if condition {
cancel()
} else {
for i := 0; i < 10; i++ {
print(0)
}
}
} // ERROR "this return statement may be reached without using the cancel var"
func _() {
ctx, cancel := context.WithCancel()
// ok: used on all paths
switch someInt {
case 0:
new(testing.T).FailNow()
case 1:
log.Fatal()
case 2:
cancel()
case 3:
print("hi")
fallthrough
default:
os.Exit(1)
}
}
func _() {
ctx, cancel := context.WithCancel() // ERROR "not used on all paths"
switch someInt {
case 0:
new(testing.T).FailNow()
case 1:
log.Fatal()
case 2:
cancel()
case 3:
print("hi") // falls through to implicit return
default:
os.Exit(1)
}
} // ERROR "this return statement may be reached without using the cancel var"
func _(ch chan int) int {
ctx, cancel := context.WithCancel() // ERROR "not used on all paths"
select {
case <-ch:
new(testing.T).FailNow()
case y <- ch:
print("hi") // falls through to implicit return
case ch <- 1:
cancel()
default:
os.Exit(1)
}
} // ERROR "this return statement may be reached without using the cancel var"
func _(ch chan int) int {
ctx, cancel := context.WithCancel()
// A blocking select must execute one of its cases.
select {
case <-ch:
panic()
}
}
func _() {
go func() {
ctx, cancel := context.WithCancel() // ERROR "not used on all paths"
print(ctx)
}() // ERROR "may be reached without using the cancel var"
}
var condition bool
var someInt int
// Regression test for Go issue 16143.
func _() {
var x struct{ f func() }
x.f()
}
// Regression test for Go issue 16230.
func _() (ctx context.Context, cancel func()) {
ctx, cancel = context.WithCancel()
return // a naked return counts as a load of the named result values
}
// Same as above, but for literal function.
var _ = func() (ctx context.Context, cancel func()) {
ctx, cancel = context.WithCancel()
return
}
View
@@ -59,23 +59,28 @@ func lookup(name string, scopes []*types.Scope) types.Object {
return nil
}
func extendedScope(pkg *Package) []*types.Scope {
scopes := []*types.Scope{pkg.typesPkg.Scope()}
pkgName := pkg.typesPkg.Name()
if strings.HasPrefix(pkgName, "_test") {
basePkg := strings.TrimSuffix(pkgName, "_test")
for _, p := range pkg.typesPkg.Imports() {
if p.Name() == basePkg {
scopes = append(scopes, p.Scope())
break
func extendedScope(f *File) []*types.Scope {
scopes := []*types.Scope{f.pkg.typesPkg.Scope()}
if f.basePkg != nil {
scopes = append(scopes, f.basePkg.typesPkg.Scope())
} else {
// If basePkg is not specified (e.g. when checking a single file) try to
// find it among imports.
pkgName := f.pkg.typesPkg.Name()
if strings.HasSuffix(pkgName, "_test") {
basePkgName := strings.TrimSuffix(pkgName, "_test")
for _, p := range f.pkg.typesPkg.Imports() {
if p.Name() == basePkgName {
scopes = append(scopes, p.Scope())
break
}
}
}
}
return scopes
}
func checkExample(fn *ast.FuncDecl, pkg *Package, report reporter) {
func checkExample(fn *ast.FuncDecl, f *File, report reporter) {
fnName := fn.Name.Name
if params := fn.Type.Params; len(params.List) != 0 {
report("%s should be niladic", fnName)
@@ -100,7 +105,7 @@ func checkExample(fn *ast.FuncDecl, pkg *Package, report reporter) {
exName = strings.TrimPrefix(fnName, "Example")
elems = strings.SplitN(exName, "_", 3)
ident = elems[0]
obj = lookup(ident, extendedScope(pkg))
obj = lookup(ident, extendedScope(f))
)
if ident != "" && obj == nil {
// Check ExampleFoo and ExampleBadFoo.
@@ -173,7 +178,7 @@ func checkTestFunctions(f *File, node ast.Node) {
switch {
case strings.HasPrefix(fn.Name.Name, "Example"):
checkExample(fn, f.pkg, report)
checkExample(fn, f, report)
case strings.HasPrefix(fn.Name.Name, "Test"):
checkTest(fn, "Test", report)
case strings.HasPrefix(fn.Name.Name, "Benchmark"):
View
@@ -102,7 +102,7 @@ func TestVet(t *testing.T) {
func TestDivergentPackagesExamples(t *testing.T) {
Build(t)
// errchk ./testvet
Vet(t, []string{"testdata/divergent/buf.go", "testdata/divergent/buf_test.go"})
Vet(t, []string{"testdata/divergent"})
}
func TestIncompleteExamples(t *testing.T) {
View
@@ -683,6 +683,10 @@ outer:
levprd[nprod] = 0
}
if TEMPSIZE < ntokens+nnonter+1 {
errorf("too many tokens (%d) or non-terminals (%d)", ntokens, nnonter)
}
//
// end of all rules
// dump out the prefix code
View
@@ -766,7 +766,7 @@ func (f *decompressor) Reset(r io.Reader, dict []byte) error {
dict: f.dict,
step: (*decompressor).nextBlock,
}
f.dict.init(maxMatchOffset, nil)
f.dict.init(maxMatchOffset, dict)
return nil
}
View
@@ -37,3 +37,33 @@ func TestReset(t *testing.T) {
}
}
}
func TestResetDict(t *testing.T) {
dict := []byte("the lorem fox")
ss := []string{
"lorem ipsum izzle fo rizzle",
"the quick brown fox jumped over",
}
deflated := make([]bytes.Buffer, len(ss))
for i, s := range ss {
w, _ := NewWriterDict(&deflated[i], DefaultCompression, dict)
w.Write([]byte(s))
w.Close()
}
inflated := make([]bytes.Buffer, len(ss))
f := NewReader(nil)
for i := range inflated {
f.(Resetter).Reset(&deflated[i], dict)
io.Copy(&inflated[i], f)
}
f.Close()
for i, s := range ss {
if s != inflated[i].String() {
t.Errorf("inflated[%d]:\ngot %q\nwant %q", i, inflated[i], s)
}
}
}
View
@@ -6,32 +6,35 @@
// cancelation signals, and other request-scoped values across API boundaries
// and between processes.
//
// Incoming requests to a server should create a Context, and outgoing calls to
// servers should accept a Context. The chain of function calls between them
// must propagate the Context, optionally replacing it with a derived Context
// created using WithCancel, WithDeadline, WithTimeout, or WithValue. These
// Context values form a tree: when a Context is canceled, all Contexts derived
// from it are also canceled.
// Incoming requests to a server should create a Context, and outgoing
// calls to servers should accept a Context. The chain of function
// calls between them must propagate the Context, optionally replacing
// it with a derived Context created using WithCancel, WithDeadline,
// WithTimeout, or WithValue. When a Context is canceled, all
// Contexts derived from it are also canceled.
//
// The WithCancel, WithDeadline, and WithTimeout functions return a derived
// Context and a CancelFunc. Calling the CancelFunc cancels the new Context and
// any Contexts derived from it, removes the Context from the parent's tree, and
// stops any associated timers. Failing to call the CancelFunc leaks the
// associated resources until the parent Context is canceled or the timer fires.
// The WithCancel, WithDeadline, and WithTimeout functions take a
// Context (the parent) and return a derived Context (the child) and a
// CancelFunc. Calling the CancelFunc cancels the child and its
// children, removes the parent's reference to the child, and stops
// any associated timers. Failing to call the CancelFunc leaks the
// child and its children until the parent is canceled or the timer
// fires. The go vet tool checks that CancelFuncs are used on all
// control-flow paths.
//
// Programs that use Contexts should follow these rules to keep interfaces
// consistent across packages and enable static analysis tools to check context
// propagation:
//
// Do not store Contexts inside a struct type; instead, pass a Context
// explicitly to each function that needs it. The Context should be the first
// explicitly to each function that needs it. The Context should be the first
// parameter, typically named ctx:
//
// func DoSomething(ctx context.Context, arg Arg) error {
// // ... use ctx ...
// }
//
// Do not pass a nil Context, even if a function permits it. Pass context.TODO
// Do not pass a nil Context, even if a function permits it. Pass context.TODO
// if you are unsure about which Context to use.
//
// Use context Values only for request-scoped data that transits processes and
@@ -58,13 +61,13 @@ import (
// Context's methods may be called by multiple goroutines simultaneously.
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
//
// WithCancel arranges for Done to be closed when cancel is called;
// WithDeadline arranges for Done to be closed when the deadline
@@ -93,24 +96,24 @@ type Context interface {
// a Done channel for cancelation.
Done() <-chan struct{}
// Err returns a non-nil error value after Done is closed. Err returns
// Err returns a non-nil error value after Done is closed. Err returns
// Canceled if the context was canceled or DeadlineExceeded if the
// context's deadline passed. No other values for Err are defined.
// context's deadline passed. No other values for Err are defined.
// After Done is closed, successive calls to Err return the same value.
Err() error
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
//
// A key identifies a specific value in a Context. Functions that wish
// A key identifies a specific value in a Context. Functions that wish
// to store values in Context typically allocate a key in a global
// variable then use that key as the argument to context.WithValue and
// Context.Value. A key can be any type that supports equality;
// Context.Value. A key can be any type that supports equality;
// packages should define keys as an unexported type to avoid
// collisions.
//
@@ -129,7 +132,7 @@ type Context interface {
// // This prevents collisions with keys defined in other packages.
// type key int
//
// // userKey is the key for user.User values in Contexts. It is
// // userKey is the key for user.User values in Contexts. It is
// // unexported; clients use user.NewContext and user.FromContext
// // instead of using this key directly.
// var userKey key = 0
@@ -160,7 +163,7 @@ func (deadlineExceededError) Error() string { return "context deadline exceeded"
func (deadlineExceededError) Timeout() bool { return true }
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
// struct{}, since vars of this type must have distinct addresses.
type emptyCtx int
@@ -196,17 +199,17 @@ var (
)
// Background returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline. It is typically used by the main function,
// values, and has no deadline. It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming
// requests.
func Background() Context {
return background
}
// TODO returns a non-nil, empty Context. Code should use context.TODO when
// TODO returns a non-nil, empty Context. Code should use context.TODO when
// it's unclear which Context to use or it is not yet available (because the
// surrounding function has not yet been extended to accept a Context
// parameter). TODO is recognized by static analysis tools that determine
// parameter). TODO is recognized by static analysis tools that determine
// whether Contexts are propagated correctly in a program.
func TODO() Context {
return todo
@@ -266,7 +269,7 @@ func propagateCancel(parent Context, child canceler) {
}
// parentCancelCtx follows a chain of parent references until it finds a
// *cancelCtx. This function understands how each of the concrete types in this
// *cancelCtx. This function understands how each of the concrete types in this
// package represents its parent.
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
for {
@@ -296,14 +299,14 @@ func removeChild(parent Context, child canceler) {
p.mu.Unlock()
}
// A canceler is a context type that can be canceled directly. The
// A canceler is a context type that can be canceled directly. The
// implementations are *cancelCtx and *timerCtx.
type canceler interface {
cancel(removeFromParent bool, err error)
Done() <-chan struct{}
}
// A cancelCtx can be canceled. When canceled, it also cancels any children
// A cancelCtx can be canceled. When canceled, it also cancels any children
// that implement canceler.
type cancelCtx struct {
Context
@@ -355,8 +358,8 @@ func (c *cancelCtx) cancel(removeFromParent bool, err error) {
}
// WithDeadline returns a copy of the parent context with the deadline adjusted
// to be no later than d. If the parent's deadline is already earlier than d,
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
// to be no later than d. If the parent's deadline is already earlier than d,
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
// context's Done channel is closed when the deadline expires, when the returned
// cancel function is called, or when the parent context's Done channel is
// closed, whichever happens first.
@@ -388,8 +391,8 @@ func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
return c, func() { c.cancel(true, Canceled) }
}
// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
// implement Done and Err. It implements cancel by stopping its timer then
// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
// implement Done and Err. It implements cancel by stopping its timer then
// delegating to cancelCtx.cancel.
type timerCtx struct {
cancelCtx
@@ -451,7 +454,7 @@ func WithValue(parent Context, key, val interface{}) Context {
return &valueCtx{parent, key, val}
}
// A valueCtx carries a key-value pair. It implements Value for that key and
// A valueCtx carries a key-value pair. It implements Value for that key and
// delegates all other calls to the embedded Context.
type valueCtx struct {
Context
View
@@ -13,13 +13,21 @@ import (
func ExampleWithTimeout() {
// Pass a context with a timeout to tell a blocking function that it
// should abandon its work after the timeout elapses.
ctx, _ := context.WithTimeout(context.Background(), 50*time.Millisecond)
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
select {
case <-time.After(1 * time.Second):
fmt.Println("overslept")
case <-ctx.Done():
fmt.Println(ctx.Err()) // prints "context deadline exceeded"
}
// Even though ctx should have expired already, it is good
// practice to call its cancelation function in any case.
// Failure to do so may keep the context and its parent alive
// longer than necessary.
cancel()
// Output:
// context deadline exceeded
}
View
@@ -143,10 +143,11 @@ func fermatInverse(k, N *big.Int) *big.Int {
var errZeroParam = errors.New("zero parameter")
// Sign signs an arbitrary length hash (which should be the result of hashing a
// larger message) using the private key, priv. It returns the signature as a
// pair of integers. The security of the private key depends on the entropy of
// rand.
// Sign signs a hash (which should be the result of hashing a larger message)
// using the private key, priv. If the hash is longer than the bit-length of the
// private key's curve order, the hash will be truncated to that length. It
// returns the signature as a pair of integers. The security of the private key
// depends on the entropy of rand.
func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) {
// Get max(log2(q) / 2, 256) bits of entropy from rand.
entropylen := (priv.Curve.Params().BitSize + 7) / 16
View
@@ -422,6 +422,33 @@ func ticketKeyFromBytes(b [32]byte) (key ticketKey) {
return key
}
// clone returns a copy of c. Only the exported fields are copied.
func (c *Config) clone() *Config {
return &Config{
Rand: c.Rand,
Time: c.Time,
Certificates: c.Certificates,
NameToCertificate: c.NameToCertificate,
GetCertificate: c.GetCertificate,
RootCAs: c.RootCAs,
NextProtos: c.NextProtos,
ServerName: c.ServerName,
ClientAuth: c.ClientAuth,
ClientCAs: c.ClientCAs,
InsecureSkipVerify: c.InsecureSkipVerify,
CipherSuites: c.CipherSuites,
PreferServerCipherSuites: c.PreferServerCipherSuites,
SessionTicketsDisabled: c.SessionTicketsDisabled,
SessionTicketKey: c.SessionTicketKey,
ClientSessionCache: c.ClientSessionCache,
MinVersion: c.MinVersion,
MaxVersion: c.MaxVersion,
CurvePreferences: c.CurvePreferences,
DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
Renegotiation: c.Renegotiation,
}
}
func (c *Config) serverInit() {
if c.SessionTicketsDisabled {
return
View
@@ -124,9 +124,9 @@ func TestCertificateSelection(t *testing.T) {
func runDynamicRecordSizingTest(t *testing.T, config *Config) {
clientConn, serverConn := net.Pipe()
serverConfig := *config
serverConfig := config.clone()
serverConfig.DynamicRecordSizingDisabled = false
tlsConn := Server(serverConn, &serverConfig)
tlsConn := Server(serverConn, serverConfig)
recordSizesChan := make(chan []int, 1)
go func() {
@@ -225,19 +225,19 @@ func runDynamicRecordSizingTest(t *testing.T, config *Config) {
}
func TestDynamicRecordSizingWithStreamCipher(t *testing.T) {
config := *testConfig
config := testConfig.clone()
config.CipherSuites = []uint16{TLS_RSA_WITH_RC4_128_SHA}
runDynamicRecordSizingTest(t, &config)
runDynamicRecordSizingTest(t, config)
}
func TestDynamicRecordSizingWithCBC(t *testing.T) {
config := *testConfig
config := testConfig.clone()
config.CipherSuites = []uint16{TLS_RSA_WITH_AES_256_CBC_SHA}
runDynamicRecordSizingTest(t, &config)
runDynamicRecordSizingTest(t, config)
}
func TestDynamicRecordSizingWithAEAD(t *testing.T) {
config := *testConfig
config := testConfig.clone()
config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}
runDynamicRecordSizingTest(t, &config)
runDynamicRecordSizingTest(t, config)
}
View
@@ -509,14 +509,14 @@ func TestHandshakeClientAES256GCMSHA384(t *testing.T) {
}
func TestHandshakeClientCertRSA(t *testing.T) {
config := *testConfig
config := testConfig.clone()
cert, _ := X509KeyPair([]byte(clientCertificatePEM), []byte(clientKeyPEM))
config.Certificates = []Certificate{cert}
test := &clientTest{
name: "ClientCert-RSA-RSA",
command: []string{"openssl", "s_server", "-cipher", "RC4-SHA", "-verify", "1"},
config: &config,
config: config,
}
runClientTestTLS10(t, test)
@@ -525,7 +525,7 @@ func TestHandshakeClientCertRSA(t *testing.T) {
test = &clientTest{
name: "ClientCert-RSA-ECDSA",
command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES128-SHA", "-verify", "1"},
config: &config,
config: config,
cert: testECDSACertificate,
key: testECDSAPrivateKey,
}
@@ -536,7 +536,7 @@ func TestHandshakeClientCertRSA(t *testing.T) {
test = &clientTest{
name: "ClientCert-RSA-AES256-GCM-SHA384",
command: []string{"openssl", "s_server", "-cipher", "ECDHE-RSA-AES256-GCM-SHA384", "-verify", "1"},
config: &config,
config: config,
cert: testRSACertificate,
key: testRSAPrivateKey,
}
@@ -545,14 +545,14 @@ func TestHandshakeClientCertRSA(t *testing.T) {
}
func TestHandshakeClientCertECDSA(t *testing.T) {
config := *testConfig
config := testConfig.clone()
cert, _ := X509KeyPair([]byte(clientECDSACertificatePEM), []byte(clientECDSAKeyPEM))
config.Certificates = []Certificate{cert}
test := &clientTest{
name: "ClientCert-ECDSA-RSA",
command: []string{"openssl", "s_server", "-cipher", "RC4-SHA", "-verify", "1"},
config: &config,
config: config,
}
runClientTestTLS10(t, test)
@@ -561,7 +561,7 @@ func TestHandshakeClientCertECDSA(t *testing.T) {
test = &clientTest{
name: "ClientCert-ECDSA-ECDSA",
command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES128-SHA", "-verify", "1"},
config: &config,
config: config,
cert: testECDSACertificate,
key: testECDSAPrivateKey,
}
@@ -691,15 +691,15 @@ func TestLRUClientSessionCache(t *testing.T) {
}
func TestHandshakeClientALPNMatch(t *testing.T) {
config := *testConfig
config := testConfig.clone()
config.NextProtos = []string{"proto2", "proto1"}
test := &clientTest{
name: "ALPN",
// Note that this needs OpenSSL 1.0.2 because that is the first
// version that supports the -alpn flag.
command: []string{"openssl", "s_server", "-alpn", "proto1,proto2"},
config: &config,
config: config,
validate: func(state ConnectionState) error {
// The server's preferences should override the client.
if state.NegotiatedProtocol != "proto1" {
@@ -712,15 +712,15 @@ func TestHandshakeClientALPNMatch(t *testing.T) {
}
func TestHandshakeClientALPNNoMatch(t *testing.T) {
config := *testConfig
config := testConfig.clone()
config.NextProtos = []string{"proto3"}
test := &clientTest{
name: "ALPN-NoMatch",
// Note that this needs OpenSSL 1.0.2 because that is the first
// version that supports the -alpn flag.
command: []string{"openssl", "s_server", "-alpn", "proto1,proto2"},
config: &config,
config: config,
validate: func(state ConnectionState) error {
// There's no overlap so OpenSSL will not select a protocol.
if state.NegotiatedProtocol != "" {
@@ -736,7 +736,7 @@ func TestHandshakeClientALPNNoMatch(t *testing.T) {
const sctsBase64 = "ABIBaQFnAHUApLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BAAAAFHl5nuFgAABAMARjBEAiAcS4JdlW5nW9sElUv2zvQyPoZ6ejKrGGB03gjaBZFMLwIgc1Qbbn+hsH0RvObzhS+XZhr3iuQQJY8S9G85D9KeGPAAdgBo9pj4H2SCvjqM7rkoHUz8cVFdZ5PURNEKZ6y7T0/7xAAAAUeX4bVwAAAEAwBHMEUCIDIhFDgG2HIuADBkGuLobU5a4dlCHoJLliWJ1SYT05z6AiEAjxIoZFFPRNWMGGIjskOTMwXzQ1Wh2e7NxXE1kd1J0QsAdgDuS723dc5guuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAUhcZIqHAAAEAwBHMEUCICmJ1rBT09LpkbzxtUC+Hi7nXLR0J+2PmwLp+sJMuqK+AiEAr0NkUnEVKVhAkccIFpYDqHOlZaBsuEhWWrYpg2RtKp0="
func TestHandshakClientSCTs(t *testing.T) {
config := *testConfig
config := testConfig.clone()
scts, err := base64.StdEncoding.DecodeString(sctsBase64)
if err != nil {
@@ -748,7 +748,7 @@ func TestHandshakClientSCTs(t *testing.T) {
// Note that this needs OpenSSL 1.0.2 because that is the first
// version that supports the -serverinfo flag.
command: []string{"openssl", "s_server"},
config: &config,
config: config,
extensions: [][]byte{scts},
validate: func(state ConnectionState) error {
expectedSCTs := [][]byte{
@@ -771,11 +771,11 @@ func TestHandshakClientSCTs(t *testing.T) {
}
func TestRenegotiationRejected(t *testing.T) {
config := *testConfig
config := testConfig.clone()
test := &clientTest{
name: "RenegotiationRejected",
command: []string{"openssl", "s_server", "-state"},
config: &config,
config: config,
numRenegotiations: 1,
renegotiationExpectedToFail: 1,
checkRenegotiationError: func(renegotiationNum int, err error) error {
@@ -793,41 +793,41 @@ func TestRenegotiationRejected(t *testing.T) {
}
func TestRenegotiateOnce(t *testing.T) {
config := *testConfig
config := testConfig.clone()
config.Renegotiation = RenegotiateOnceAsClient
test := &clientTest{
name: "RenegotiateOnce",
command: []string{"openssl", "s_server", "-state"},
config: &config,
config: config,
numRenegotiations: 1,
}
runClientTestTLS12(t, test)
}
func TestRenegotiateTwice(t *testing.T) {
config := *testConfig
config := testConfig.clone()
config.Renegotiation = RenegotiateFreelyAsClient
test := &clientTest{
name: "RenegotiateTwice",
command: []string{"openssl", "s_server", "-state"},
config: &config,
config: config,
numRenegotiations: 2,
}
runClientTestTLS12(t, test)
}
func TestRenegotiateTwiceRejected(t *testing.T) {
config := *testConfig
config := testConfig.clone()
config.Renegotiation = RenegotiateOnceAsClient
test := &clientTest{
name: "RenegotiateTwiceRejected",
command: []string{"openssl", "s_server", "-state"},
config: &config,
config: config,
numRenegotiations: 2,
renegotiationExpectedToFail: 2,
checkRenegotiationError: func(renegotiationNum int, err error) error {
View
@@ -130,11 +130,11 @@ func TestNoRC4ByDefault(t *testing.T) {
cipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
compressionMethods: []uint8{compressionNone},
}
serverConfig := *testConfig
serverConfig := testConfig.clone()
// Reset the enabled cipher suites to nil in order to test the
// defaults.
serverConfig.CipherSuites = nil
testClientHelloFailure(t, &serverConfig, clientHello, "no cipher suite supported by both client and server")
testClientHelloFailure(t, serverConfig, clientHello, "no cipher suite supported by both client and server")
}
func TestDontSelectECDSAWithRSAKey(t *testing.T) {
@@ -147,19 +147,19 @@ func TestDontSelectECDSAWithRSAKey(t *testing.T) {
supportedCurves: []CurveID{CurveP256},
supportedPoints: []uint8{pointFormatUncompressed},
}
serverConfig := *testConfig
serverConfig := testConfig.clone()
serverConfig.CipherSuites = clientHello.cipherSuites
serverConfig.Certificates = make([]Certificate, 1)
serverConfig.Certificates[0].Certificate = [][]byte{testECDSACertificate}
serverConfig.Certificates[0].PrivateKey = testECDSAPrivateKey
serverConfig.BuildNameToCertificate()
// First test that it *does* work when the server's key is ECDSA.
testClientHello(t, &serverConfig, clientHello)
testClientHello(t, serverConfig, clientHello)
// Now test that switching to an RSA key causes the expected error (and
// not an internal error about a signing failure).
serverConfig.Certificates = testConfig.Certificates
testClientHelloFailure(t, &serverConfig, clientHello, "no cipher suite supported by both client and server")
testClientHelloFailure(t, serverConfig, clientHello, "no cipher suite supported by both client and server")
}
func TestDontSelectRSAWithECDSAKey(t *testing.T) {
@@ -172,18 +172,18 @@ func TestDontSelectRSAWithECDSAKey(t *testing.T) {
supportedCurves: []CurveID{CurveP256},
supportedPoints: []uint8{pointFormatUncompressed},
}
serverConfig := *testConfig
serverConfig := testConfig.clone()
serverConfig.CipherSuites = clientHello.cipherSuites
// First test that it *does* work when the server's key is RSA.
testClientHello(t, &serverConfig, clientHello)
testClientHello(t, serverConfig, clientHello)
// Now test that switching to an ECDSA key causes the expected error
// (and not an internal error about a signing failure).
serverConfig.Certificates = make([]Certificate, 1)
serverConfig.Certificates[0].Certificate = [][]byte{testECDSACertificate}
serverConfig.Certificates[0].PrivateKey = testECDSAPrivateKey
serverConfig.BuildNameToCertificate()
testClientHelloFailure(t, &serverConfig, clientHello, "no cipher suite supported by both client and server")
testClientHelloFailure(t, serverConfig, clientHello, "no cipher suite supported by both client and server")
}
func TestRenegotiationExtension(t *testing.T) {
@@ -265,9 +265,9 @@ func TestTLS12OnlyCipherSuites(t *testing.T) {
reply, clientErr = cli.readHandshake()
c.Close()
}()
config := *testConfig
config := testConfig.clone()
config.CipherSuites = clientHello.cipherSuites
Server(s, &config).Handshake()
Server(s, config).Handshake()
s.Close()
if clientErr != nil {
t.Fatal(clientErr)
@@ -732,7 +732,7 @@ func TestHandshakeServerAES256GCMSHA384(t *testing.T) {
}
func TestHandshakeServerECDHEECDSAAES(t *testing.T) {
config := *testConfig
config := testConfig.clone()
config.Certificates = make([]Certificate, 1)
config.Certificates[0].Certificate = [][]byte{testECDSACertificate}
config.Certificates[0].PrivateKey = testECDSAPrivateKey
@@ -741,22 +741,22 @@ func TestHandshakeServerECDHEECDSAAES(t *testing.T) {
test := &serverTest{
name: "ECDHE-ECDSA-AES",
command: []string{"openssl", "s_client", "-no_ticket", "-cipher", "ECDHE-ECDSA-AES256-SHA"},
config: &config,
config: config,
}
runServerTestTLS10(t, test)
runServerTestTLS12(t, test)
}
func TestHandshakeServerALPN(t *testing.T) {
config := *testConfig
config := testConfig.clone()
config.NextProtos = []string{"proto1", "proto2"}
test := &serverTest{
name: "ALPN",
// Note that this needs OpenSSL 1.0.2 because that is the first
// version that supports the -alpn flag.
command: []string{"openssl", "s_client", "-alpn", "proto2,proto1"},
config: &config,
config: config,
validate: func(state ConnectionState) error {
// The server's preferences should override the client.
if state.NegotiatedProtocol != "proto1" {
@@ -769,15 +769,15 @@ func TestHandshakeServerALPN(t *testing.T) {
}
func TestHandshakeServerALPNNoMatch(t *testing.T) {
config := *testConfig
config := testConfig.clone()
config.NextProtos = []string{"proto3"}
test := &serverTest{
name: "ALPN-NoMatch",
// Note that this needs OpenSSL 1.0.2 because that is the first
// version that supports the -alpn flag.
command: []string{"openssl", "s_client", "-alpn", "proto2,proto1"},
config: &config,
config: config,
validate: func(state ConnectionState) error {
// Rather than reject the connection, Go doesn't select
// a protocol when there is no overlap.
@@ -804,7 +804,7 @@ func TestHandshakeServerSNI(t *testing.T) {
// TestHandshakeServerSNICertForName is similar to TestHandshakeServerSNI, but
// tests the dynamic GetCertificate method
func TestHandshakeServerSNIGetCertificate(t *testing.T) {
config := *testConfig
config := testConfig.clone()
// Replace the NameToCertificate map with a GetCertificate function
nameToCert := config.NameToCertificate
@@ -816,7 +816,7 @@ func TestHandshakeServerSNIGetCertificate(t *testing.T) {
test := &serverTest{
name: "SNI-GetCertificate",
command: []string{"openssl", "s_client", "-no_ticket", "-cipher", "AES128-SHA", "-servername", "snitest.com"},
config: &config,
config: config,
}
runServerTestTLS12(t, test)
}
@@ -826,15 +826,15 @@ func TestHandshakeServerSNIGetCertificate(t *testing.T) {
// GetCertificate method doesn't return a cert, we fall back to what's in
// the NameToCertificate map.
func TestHandshakeServerSNIGetCertificateNotFound(t *testing.T) {
config := *testConfig
config := testConfig.clone()
config.GetCertificate = func(clientHello *ClientHelloInfo) (*Certificate, error) {
return nil, nil
}
test := &serverTest{
name: "SNI-GetCertificateNotFound",
command: []string{"openssl", "s_client", "-no_ticket", "-cipher", "AES128-SHA", "-servername", "snitest.com"},
config: &config,
config: config,
}
runServerTestTLS12(t, test)
}
@@ -844,7 +844,7 @@ func TestHandshakeServerSNIGetCertificateNotFound(t *testing.T) {
func TestHandshakeServerSNIGetCertificateError(t *testing.T) {
const errMsg = "TestHandshakeServerSNIGetCertificateError error"
serverConfig := *testConfig
serverConfig := testConfig.clone()
serverConfig.GetCertificate = func(clientHello *ClientHelloInfo) (*Certificate, error) {
return nil, errors.New(errMsg)
}
@@ -855,15 +855,15 @@ func TestHandshakeServerSNIGetCertificateError(t *testing.T) {
compressionMethods: []uint8{compressionNone},
serverName: "test",
}
testClientHelloFailure(t, &serverConfig, clientHello, errMsg)
testClientHelloFailure(t, serverConfig, clientHello, errMsg)
}
// TestHandshakeServerEmptyCertificates tests that GetCertificates is called in
// the case that Certificates is empty, even without SNI.
func TestHandshakeServerEmptyCertificates(t *testing.T) {
const errMsg = "TestHandshakeServerEmptyCertificates error"
serverConfig := *testConfig
serverConfig := testConfig.clone()
serverConfig.GetCertificate = func(clientHello *ClientHelloInfo) (*Certificate, error) {
return nil, errors.New(errMsg)
}
@@ -874,7 +874,7 @@ func TestHandshakeServerEmptyCertificates(t *testing.T) {
cipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
compressionMethods: []uint8{compressionNone},
}
testClientHelloFailure(t, &serverConfig, clientHello, errMsg)
testClientHelloFailure(t, serverConfig, clientHello, errMsg)
// With an empty Certificates and a nil GetCertificate, the server
// should always return a “no certificates” error.
@@ -885,23 +885,23 @@ func TestHandshakeServerEmptyCertificates(t *testing.T) {
cipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
compressionMethods: []uint8{compressionNone},
}
testClientHelloFailure(t, &serverConfig, clientHello, "no certificates")
testClientHelloFailure(t, serverConfig, clientHello, "no certificates")
}
// TestCipherSuiteCertPreferance ensures that we select an RSA ciphersuite with
// an RSA certificate and an ECDSA ciphersuite with an ECDSA certificate.
func TestCipherSuiteCertPreferenceECDSA(t *testing.T) {
config := *testConfig
config := testConfig.clone()
config.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}
config.PreferServerCipherSuites = true
test := &serverTest{
name: "CipherSuiteCertPreferenceRSA",
config: &config,
config: config,
}
runServerTestTLS12(t, test)
config = *testConfig
config = testConfig.clone()
config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}
config.Certificates = []Certificate{
{
@@ -914,7 +914,7 @@ func TestCipherSuiteCertPreferenceECDSA(t *testing.T) {
test = &serverTest{
name: "CipherSuiteCertPreferenceECDSA",
config: &config,
config: config,
}
runServerTestTLS12(t, test)
}
@@ -940,12 +940,12 @@ func TestResumptionDisabled(t *testing.T) {
sessionFilePath := tempFile("")
defer os.Remove(sessionFilePath)
config := *testConfig
config := testConfig.clone()
test := &serverTest{
name: "IssueTicketPreDisable",
command: []string{"openssl", "s_client", "-cipher", "RC4-SHA", "-sess_out", sessionFilePath},
config: &config,
config: config,
}
runServerTestTLS12(t, test)
@@ -954,7 +954,7 @@ func TestResumptionDisabled(t *testing.T) {
test = &serverTest{
name: "ResumeDisabled",
command: []string{"openssl", "s_client", "-cipher", "RC4-SHA", "-sess_in", sessionFilePath},
config: &config,
config: config,
}
runServerTestTLS12(t, test)
@@ -963,12 +963,12 @@ func TestResumptionDisabled(t *testing.T) {
}
func TestFallbackSCSV(t *testing.T) {
serverConfig := &Config{
serverConfig := Config{
Certificates: testConfig.Certificates,
}
test := &serverTest{
name: "FallbackSCSV",
config: serverConfig,
config: &serverConfig,
// OpenSSL 1.0.1j is needed for the -fallback_scsv option.
command: []string{"openssl", "s_client", "-fallback_scsv"},
expectHandshakeErrorIncluding: "inappropriate protocol fallback",
@@ -1053,28 +1053,28 @@ func TestClientAuth(t *testing.T) {
defer os.Remove(ecdsaKeyPath)
}
config := *testConfig
config := testConfig.clone()
config.ClientAuth = RequestClientCert
test := &serverTest{
name: "ClientAuthRequestedNotGiven",
command: []string{"openssl", "s_client", "-no_ticket", "-cipher", "RC4-SHA"},
config: &config,
config: config,
}
runServerTestTLS12(t, test)
test = &serverTest{
name: "ClientAuthRequestedAndGiven",
command: []string{"openssl", "s_client", "-no_ticket", "-cipher", "RC4-SHA", "-cert", certPath, "-key", keyPath},
config: &config,
config: config,
expectedPeerCerts: []string{clientCertificatePEM},
}
runServerTestTLS12(t, test)
test = &serverTest{
name: "ClientAuthRequestedAndECDSAGiven",
command: []string{"openssl", "s_client", "-no_ticket", "-cipher", "RC4-SHA", "-cert", ecdsaCertPath, "-key", ecdsaKeyPath},
config: &config,
config: config,
expectedPeerCerts: []string{clientECDSACertificatePEM},
}
runServerTestTLS12(t, test)
View
@@ -135,9 +135,9 @@ func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*
// from the hostname we're connecting to.
if config.ServerName == "" {
// Make a copy to avoid polluting argument or default.
c := *config
c := config.clone()
c.ServerName = hostname
config = &c
config = c
}
conn := Client(rawConn, config)
Oops, something went wrong.