mirror of
https://github.com/golang/go.git
synced 2026-01-29 15:12:08 +03:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
753452fac6 | ||
|
|
6ceba5d5e8 | ||
|
|
5c2b5ee3c8 | ||
|
|
efdb1813a0 | ||
|
|
c4552c1c61 | ||
|
|
faafe0e30c | ||
|
|
93468efeb7 | ||
|
|
f5dd484480 | ||
|
|
70e980631c | ||
|
|
230a376b5a | ||
|
|
6b36535cf3 | ||
|
|
0ad8bf4122 | ||
|
|
26741a15f7 | ||
|
|
fab76f07f5 | ||
|
|
4f7363cd72 | ||
|
|
6759eff8eb | ||
|
|
75f4f703eb | ||
|
|
2f6557233c | ||
|
|
4de7b10483 | ||
|
|
3b6f4b04ba | ||
|
|
740dfbadbd | ||
|
|
edecc650ec | ||
|
|
2e150a0aa7 | ||
|
|
f0377a2851 | ||
|
|
2287d95e9b | ||
|
|
a2f37b7fe1 | ||
|
|
ca0b97e80a | ||
|
|
230c3918a8 | ||
|
|
b67902fee7 | ||
|
|
5a589904a3 | ||
|
|
f75aafdf56 | ||
|
|
b261730e56 | ||
|
|
40712a9625 | ||
|
|
24f46bd34f | ||
|
|
c24b5d43a6 | ||
|
|
dc3612e0d1 | ||
|
|
6f12826a86 | ||
|
|
3129c67db7 | ||
|
|
bb8706890b | ||
|
|
287be1e42d | ||
|
|
308bdd0256 | ||
|
|
9b4e323e88 | ||
|
|
a5add8c726 | ||
|
|
e464c08bef | ||
|
|
c0f5de1ad2 | ||
|
|
1861ac159e | ||
|
|
759c2c48ea | ||
|
|
09fa0b8e95 | ||
|
|
0d81858868 | ||
|
|
681a667ced | ||
|
|
806a84d3f1 | ||
|
|
87d4bd1997 | ||
|
|
1e933ed7c0 | ||
|
|
78d024a588 | ||
|
|
09fc3cc5df | ||
|
|
17a48e0a00 |
@@ -67,7 +67,6 @@ official forums operated by the Go project (“Go spaces”):
|
||||
<li>The <a href="https://groups.google.com/group/golang-nuts">golang-nuts</a> and
|
||||
<a href="https://groups.google.com/group/golang-dev">golang-dev</a> mailing lists.
|
||||
<li>The #go-nuts IRC channel on Freenode.
|
||||
<li>The <a href="https://reddit.com/r/golang">/r/golang subreddit</a>.
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -34,6 +34,7 @@ We encourage all Go users to subscribe to
|
||||
<p>A <a href="/doc/devel/release.html">summary</a> of the changes between Go releases. Notes for the major releases:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="/doc/go1.7">Go 1.7</a> <small>(August 2016)</small></li>
|
||||
<li><a href="/doc/go1.6">Go 1.6</a> <small>(February 2016)</small></li>
|
||||
<li><a href="/doc/go1.5">Go 1.5</a> <small>(August 2015)</small></li>
|
||||
<li><a href="/doc/go1.4">Go 1.4</a> <small>(December 2014)</small></li>
|
||||
|
||||
@@ -30,6 +30,52 @@ to fix critical security problems in both Go 1.4 and Go 1.5 as they arise.
|
||||
See the <a href="/security">security policy</a> for more details.
|
||||
</p>
|
||||
|
||||
<h2 id="go1.7">go1.7 (released 2016/08/15)</h2>
|
||||
|
||||
<p>
|
||||
Go 1.7 is a major release of Go.
|
||||
Read the <a href="/doc/go1.7">Go 1.7 Release Notes</a> for more information.
|
||||
</p>
|
||||
|
||||
<h3 id="go1.7.minor">Minor revisions</h3>
|
||||
|
||||
<p>
|
||||
go1.7.1 (released 2016/09/07) includes fixes to the compiler, runtime,
|
||||
documentation, and the <code>compress/flate</code>, <code>hash/crc32</code>,
|
||||
<code>io</code>, <code>net</code>, <code>net/http</code>,
|
||||
<code>path/filepath</code>, <code>reflect</code>, and <code>syscall</code>
|
||||
packages.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.7.1">Go
|
||||
1.7.1 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.7.2 should not be used. It was tagged but not fully released.
|
||||
The release was deferred due to a last minute bug report.
|
||||
Use go1.7.3 instead, and refer to the summary of changes below.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.7.3 (released 2016/10/19) includes fixes to the compiler, runtime,
|
||||
and the <code>crypto/cipher</code>, <code>crypto/tls</code>,
|
||||
<code>net/http</code>, and <code>strings</code> packages.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.7.3">Go
|
||||
1.7.3 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.7.4 (released 2016/12/01) includes two security fixes.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.7.4">Go
|
||||
1.7.4 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.7.5 (released 2017/01/26) includes fixes to the compiler, runtime,
|
||||
and the <code>crypto/x509</code> and <code>time</code> packages.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.7.5">Go
|
||||
1.7.5 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<h2 id="go1.6">go1.6 (released 2016/02/17)</h2>
|
||||
|
||||
<p>
|
||||
@@ -56,11 +102,18 @@ See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.6.2">Go
|
||||
<p>
|
||||
go1.6.3 (released 2016/07/17) includes security fixes to the
|
||||
<code>net/http/cgi</code> package and <code>net/http</code> package when used in
|
||||
a CGI environment. This release also adds support for macOS Sierra.
|
||||
a CGI environment.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.6.3">Go
|
||||
1.6.3 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.6.4 (released 2016/12/01) includes two security fixes.
|
||||
It contains the same fixes as Go 1.7.4 and was released at the same time.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.7.4">Go
|
||||
1.7.4 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<h2 id="go1.5">go1.5 (released 2015/08/19)</h2>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--{
|
||||
"Title": "Go 1.7 Release Notes DRAFT",
|
||||
"Title": "Go 1.7 Release Notes",
|
||||
"Path": "/doc/go1.7",
|
||||
"Template": true
|
||||
}-->
|
||||
@@ -25,15 +25,6 @@ Do not send CLs removing the interior tags from such phrases.
|
||||
ul li { margin: 0.5em 0; }
|
||||
</style>
|
||||
|
||||
<p>
|
||||
<!-- TODO: REMOVE THIS COMMENT -->
|
||||
<!-- TODO: Also remove "DRAFT" in the "Title" at the top of this file. -->
|
||||
<i>NOTE: This is a DRAFT of the Go 1.7 release notes, prepared for the Go 1.7 beta.
|
||||
Go 1.7 has NOT yet been released.
|
||||
By our regular schedule, it is expected some time in August 2016.
|
||||
</i>
|
||||
</p>
|
||||
|
||||
<h2 id="introduction">Introduction to Go 1.7</h2>
|
||||
|
||||
<p>
|
||||
@@ -76,8 +67,7 @@ This change has no effect on the correctness of existing programs.
|
||||
|
||||
<p>
|
||||
Go 1.7 adds support for macOS 10.12 Sierra.
|
||||
This support was backported to Go 1.6.3.
|
||||
Binaries built with versions of Go before 1.6.3 will not work
|
||||
Binaries built with versions of Go before 1.7 will not work
|
||||
correctly on Sierra.
|
||||
</p>
|
||||
|
||||
@@ -919,7 +909,7 @@ For example, the address on which a request received is
|
||||
<p>
|
||||
The server's <a href="/pkg/net/http/#Server.Serve"><code>Serve</code></a> method
|
||||
now only enables HTTP/2 support if the <code>Server.TLSConfig</code> field is <code>nil</code>
|
||||
or includes <code>"h2"</code> in its <code>TLSConfig.NextProto</code>.
|
||||
or includes <code>"h2"</code> in its <code>TLSConfig.NextProtos</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
238
doc/gopher/favicon.svg
Normal file
238
doc/gopher/favicon.svg
Normal file
@@ -0,0 +1,238 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32.000001"
|
||||
id="svg4416"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="favicon.svg"
|
||||
inkscape:export-filename="../../favicon.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90">
|
||||
<defs
|
||||
id="defs4418" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="15.839192"
|
||||
inkscape:cx="17.966652"
|
||||
inkscape:cy="9.2991824"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:snap-bbox-edge-midpoints="false"
|
||||
inkscape:bbox-nodes="true"
|
||||
showguides="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1018"
|
||||
inkscape:window-x="1912"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-global="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid5148" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata4421">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="icon"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1020.3622)">
|
||||
<ellipse
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#384e54;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="ellipse4216"
|
||||
cx="-907.35657"
|
||||
cy="479.90009"
|
||||
rx="3.5793996"
|
||||
ry="3.8207953"
|
||||
transform="matrix(-0.49169095,-0.87076978,-0.87076978,0.49169095,0,0)"
|
||||
inkscape:transform-center-x="0.67794294"
|
||||
inkscape:transform-center-y="-2.3634048" />
|
||||
<ellipse
|
||||
inkscape:transform-center-y="-2.3633882"
|
||||
inkscape:transform-center-x="-0.67793718"
|
||||
transform="matrix(0.49169095,-0.87076978,0.87076978,0.49169095,0,0)"
|
||||
ry="3.8207953"
|
||||
rx="3.5793996"
|
||||
cy="507.8461"
|
||||
cx="-891.57654"
|
||||
id="ellipse4463"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#384e54;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#384e54;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 16.091693,1021.3642 c -1.105749,0.01 -2.210341,0.049 -3.31609,0.09 C 6.8422558,1021.6738 2,1026.3942 2,1032.3622 c 0,2.9786 0,13 0,20 l 28,0 c 0,-8 0,-16 0,-20 0,-5.9683 -4.667345,-10.4912 -10.59023,-10.908 -1.10575,-0.078 -2.212328,-0.099 -3.318077,-0.09 z"
|
||||
id="path4465"
|
||||
sodipodi:nodetypes="ccsccscc" />
|
||||
<path
|
||||
inkscape:transform-center-y="-1.3604657"
|
||||
inkscape:transform-center-x="-0.98424303"
|
||||
sodipodi:nodetypes="sssssss"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4469"
|
||||
d="m 4.6078867,1025.0462 c 0.459564,0.2595 1.818262,1.2013 1.980983,1.648 0.183401,0.5035 0.159385,1.0657 -0.114614,1.551 -0.346627,0.6138 -1.005341,0.9487 -1.696421,0.9365 -0.339886,-0.01 -1.720283,-0.6372 -2.042561,-0.8192 -0.97754,-0.5519 -1.350795,-1.7418 -0.833686,-2.6576 0.517109,-0.9158 1.728749,-1.2107 2.706299,-0.6587 z"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#76e1fe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
<rect
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:0.32850246;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect4473"
|
||||
width="3.0866659"
|
||||
height="3.5313663"
|
||||
x="14.406213"
|
||||
y="1035.6842"
|
||||
ry="0.62426329" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#76e1fe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 16,1023.3622 c -9,0 -12,3.7153 -12,9 l 0,20 24,0 c -0.04889,-7.3562 0,-18 0,-20 0,-5.2848 -3,-9 -12,-9 z"
|
||||
id="path4471"
|
||||
sodipodi:nodetypes="zsccsz" />
|
||||
<path
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#76e1fe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 27.074073,1025.0462 c -0.45957,0.2595 -1.818257,1.2013 -1.980979,1.648 -0.183401,0.5035 -0.159384,1.0657 0.114614,1.551 0.346627,0.6138 1.005335,0.9487 1.696415,0.9365 0.33988,-0.01 1.72029,-0.6372 2.04256,-0.8192 0.97754,-0.5519 1.35079,-1.7418 0.83369,-2.6576 -0.51711,-0.9158 -1.72876,-1.2107 -2.7063,-0.6587 z"
|
||||
id="path4481"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="sssssss"
|
||||
inkscape:transform-center-x="0.98424094"
|
||||
inkscape:transform-center-y="-1.3604657" />
|
||||
<circle
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="circle4477"
|
||||
cx="21.175734"
|
||||
cy="1030.3542"
|
||||
r="4.6537542"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<circle
|
||||
r="4.8316345"
|
||||
cy="1030.3542"
|
||||
cx="10.339486"
|
||||
id="circle4483"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<rect
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:0.32941176;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect4246"
|
||||
width="3.6673687"
|
||||
height="4.1063409"
|
||||
x="14.115863"
|
||||
y="1035.9174"
|
||||
ry="0.72590536" />
|
||||
<rect
|
||||
ry="0.72590536"
|
||||
y="1035.2253"
|
||||
x="14.115863"
|
||||
height="4.1063409"
|
||||
width="3.6673687"
|
||||
id="rect4485"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#fffcfb;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<path
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:0.32941176;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 19.999735,1036.5289 c 0,0.838 -0.871228,1.2682 -2.144766,1.1659 -0.02366,0 -0.04795,-0.6004 -0.254147,-0.5832 -0.503669,0.042 -1.095902,-0.02 -1.685964,-0.02 -0.612939,0 -1.206342,0.1826 -1.68549,0.017 -0.110233,-0.038 -0.178298,0.5838 -0.261532,0.5816 -1.243685,-0.033 -2.078803,-0.3383 -2.078803,-1.1618 0,-1.2118 1.815635,-2.1941 4.055351,-2.1941 2.239704,0 4.055351,0.9823 4.055351,2.1941 z"
|
||||
id="path4487"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="sssssssss"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<path
|
||||
sodipodi:nodetypes="sssssssss"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4489"
|
||||
d="m 19.977414,1035.7004 c 0,0.5685 -0.433659,0.8554 -1.138091,1.0001 -0.291933,0.06 -0.630371,0.096 -1.003719,0.1166 -0.56405,0.032 -1.207782,0.031 -1.89122,0.031 -0.672834,0 -1.307182,0 -1.864904,-0.029 -0.306268,-0.017 -0.589429,-0.043 -0.843164,-0.084 -0.813833,-0.1318 -1.324962,-0.417 -1.324962,-1.0344 0,-1.1601 1.805642,-2.1006 4.03303,-2.1006 2.227377,0 4.03303,0.9405 4.03303,2.1006 z"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c38c74;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<ellipse
|
||||
cy="1033.8501"
|
||||
cx="15.944382"
|
||||
id="ellipse4491"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#23201f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
rx="2.0801733"
|
||||
ry="1.343747"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<circle
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#171311;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="circle4493"
|
||||
cx="12.414201"
|
||||
cy="1030.3542"
|
||||
r="1.9630634"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<circle
|
||||
r="1.9630634"
|
||||
cy="1030.3542"
|
||||
cx="23.110121"
|
||||
id="circle4495"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#171311;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4497"
|
||||
d="m 5.0055377,1027.2727 c -1.170435,-1.0835 -2.026973,-0.7721 -2.044172,-0.7463"
|
||||
style="display:inline;fill:none;fill-rule:evenodd;stroke:#384e54;stroke-width:0.39730874;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
style="display:inline;fill:none;fill-rule:evenodd;stroke:#384e54;stroke-width:0.39730874;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 4.3852457,1026.9152 c -1.158557,0.036 -1.346704,0.6303 -1.33881,0.6523"
|
||||
id="path4499"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="display:inline;fill:none;fill-rule:evenodd;stroke:#384e54;stroke-width:0.39730874;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 26.630533,1027.1724 c 1.17043,-1.0835 2.02697,-0.7721 2.04417,-0.7463"
|
||||
id="path4501"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4503"
|
||||
d="m 27.321773,1026.673 c 1.15856,0.036 1.3467,0.6302 1.3388,0.6522"
|
||||
style="display:inline;fill:none;fill-rule:evenodd;stroke:#384e54;stroke-width:0.39730874;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 17 KiB |
@@ -119,27 +119,39 @@ Go does not support CentOS 6 on these systems.
|
||||
<p>
|
||||
The Go tool chain is written in Go. To build it, you need a Go compiler installed.
|
||||
The scripts that do the initial build of the tools look for an existing Go tool
|
||||
chain in <code>$HOME/go1.4</code>.
|
||||
(This path may be overridden by setting the <code>GOROOT_BOOTSTRAP</code>
|
||||
environment variable.)
|
||||
chain in <code>$GOROOT_BOOTSTRAP</code>.
|
||||
If unset, the default value of <code>GOROOT_BOOTSTRAP</code>
|
||||
is <code>$HOME/go1.4</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Build the tools with Go version 1.4 or a point release (1.4.1, 1.4.2 etc.).
|
||||
Go 1.4 binaries can be found at <a href="/dl/">the downloads page</a>.
|
||||
There are many options for the bootstrap tool chain.
|
||||
After obtaining one, set <code>GOROOT_BOOTSTRAP</code> to the
|
||||
directory containing the unpacked tree.
|
||||
For example, <code>$GOROOT_BOOTSTRAP/bin/go</code> should be
|
||||
the <code>go</code> command binary for the bootstrap tool chain.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Download the zip or tarball of Go 1.4 for your platform and extract it to
|
||||
<code>$HOME/go1.4</code> (or your nominated <code>GOROOT_BOOTSTRAP</code>
|
||||
location).
|
||||
To use a binary release as a bootstrap tool chain, see
|
||||
<a href="/dl/">the downloads page</a> or use any other
|
||||
packaged Go distribution.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you want to install Go 1.5 on a system that is not supported by Go 1.4 (such
|
||||
as <code>linux/ppc64</code> and <code>linux/mips64le</code>) you can either use
|
||||
<a href="/src/bootstrap.bash">bootstrap.bash</a> on a system that can bootstrap Go
|
||||
1.5 normally, or bootstrap with gccgo 5.
|
||||
To build a bootstrap tool chain from source, use
|
||||
either the git branch <code>release-branch.go1.4</code> or
|
||||
<a href="https://storage.googleapis.com/golang/go1.4-bootstrap-20161024.tar.gz">go1.4-bootstrap-20161024.tar.gz</a>,
|
||||
which contains the Go 1.4 source code plus accumulated fixes
|
||||
to keep the tools running on newer operating systems.
|
||||
(Go 1.4 was the last distribution in which the tool chain was written in C.)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To cross-compile a bootstrap tool chain from source, which is
|
||||
necessary on systems Go 1.4 did not target (for
|
||||
example, <code>linux/ppc64le</code>), install Go on a different system
|
||||
and run <a href="/src/bootstrap.bash">bootstrap.bash</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -158,8 +170,9 @@ and used as <code>GOROOT_BOOTSTRAP</code> to bootstrap a local build.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To use gccgo, you need to arrange for <code>$GOROOT_BOOTSTRAP/bin/go</code> to be
|
||||
the go tool that comes as part of gccgo 5. For example on Ubuntu Vivid:
|
||||
To use gccgo as the bootstrap toolchain, you need to arrange
|
||||
for <code>$GOROOT_BOOTSTRAP/bin/go</code> to be the go tool that comes
|
||||
as part of gccgo 5. For example on Ubuntu Vivid:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
@@ -203,7 +216,7 @@ To build without <code>cgo</code>, set the environment variable
|
||||
Change to the directory that will be its parent
|
||||
and make sure the <code>go</code> directory does not exist.
|
||||
Then clone the repository and check out the latest release tag
|
||||
(<code class="versionTag">go1.6</code>, for example):</p>
|
||||
(<code class="versionTag">go1.7.2</code>, for example):</p>
|
||||
|
||||
<pre>
|
||||
$ git clone https://go.googlesource.com/go
|
||||
@@ -391,7 +404,7 @@ New releases are announced on the
|
||||
<a href="//groups.google.com/group/golang-announce">golang-announce</a>
|
||||
mailing list.
|
||||
Each announcement mentions the latest release tag, for instance,
|
||||
<code class="versionTag">go1.6</code>.
|
||||
<code class="versionTag">go1.7.2</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
BIN
favicon.ico
BIN
favicon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 5.6 KiB |
@@ -472,9 +472,29 @@ func escAnalyze(all []*Node, recursive bool) {
|
||||
|
||||
// visit the upstream of each dst, mark address nodes with
|
||||
// addrescapes, mark parameters unsafe
|
||||
escapes := make([]uint16, len(e.dsts))
|
||||
for i, n := range e.dsts {
|
||||
escapes[i] = n.Esc
|
||||
}
|
||||
for _, n := range e.dsts {
|
||||
escflood(e, n)
|
||||
}
|
||||
for {
|
||||
done := true
|
||||
for i, n := range e.dsts {
|
||||
if n.Esc != escapes[i] {
|
||||
done = false
|
||||
if Debug['m'] > 2 {
|
||||
Warnl(n.Lineno, "Reflooding %v %S", e.curfnSym(n), n)
|
||||
}
|
||||
escapes[i] = n.Esc
|
||||
escflood(e, n)
|
||||
}
|
||||
}
|
||||
if done {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// for all top level functions, tag the typenodes corresponding to the param nodes
|
||||
for _, n := range all {
|
||||
@@ -1796,6 +1816,7 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep,
|
||||
}
|
||||
|
||||
leaks = level.int() <= 0 && level.guaranteedDereference() <= 0 && dstE.Escloopdepth < modSrcLoopdepth
|
||||
leaks = leaks || level.int() <= 0 && dst.Esc&EscMask == EscHeap
|
||||
|
||||
osrcesc = src.Esc
|
||||
switch src.Op {
|
||||
|
||||
@@ -1042,6 +1042,8 @@ func anylit(ctxt int, n *Node, var_ *Node, init *Nodes) {
|
||||
|
||||
var r *Node
|
||||
if n.Right != nil {
|
||||
// n.Right is stack temporary used as backing store.
|
||||
init.Append(Nod(OAS, n.Right, nil)) // zero backing store, just in case (#18410)
|
||||
r = Nod(OADDR, n.Right, nil)
|
||||
r = typecheck(r, Erv)
|
||||
} else {
|
||||
|
||||
@@ -14,8 +14,7 @@ func dse(f *Func) {
|
||||
defer f.retSparseSet(loadUse)
|
||||
storeUse := f.newSparseSet(f.NumValues())
|
||||
defer f.retSparseSet(storeUse)
|
||||
shadowed := f.newSparseSet(f.NumValues())
|
||||
defer f.retSparseSet(shadowed)
|
||||
shadowed := newSparseMap(f.NumValues()) // TODO: cache
|
||||
for _, b := range f.Blocks {
|
||||
// Find all the stores in this block. Categorize their uses:
|
||||
// loadUse contains stores which are used by a subsequent load.
|
||||
@@ -81,17 +80,18 @@ func dse(f *Func) {
|
||||
shadowed.clear()
|
||||
}
|
||||
if v.Op == OpStore || v.Op == OpZero {
|
||||
if shadowed.contains(v.Args[0].ID) {
|
||||
sz := v.AuxInt
|
||||
if shadowedSize := int64(shadowed.get(v.Args[0].ID)); shadowedSize != -1 && shadowedSize >= sz {
|
||||
// Modify store into a copy
|
||||
if v.Op == OpStore {
|
||||
// store addr value mem
|
||||
v.SetArgs1(v.Args[2])
|
||||
} else {
|
||||
// zero addr mem
|
||||
sz := v.Args[0].Type.ElemType().Size()
|
||||
if v.AuxInt != sz {
|
||||
typesz := v.Args[0].Type.ElemType().Size()
|
||||
if sz != typesz {
|
||||
f.Fatalf("mismatched zero/store sizes: %d and %d [%s]",
|
||||
v.AuxInt, sz, v.LongString())
|
||||
sz, typesz, v.LongString())
|
||||
}
|
||||
v.SetArgs1(v.Args[1])
|
||||
}
|
||||
@@ -99,7 +99,10 @@ func dse(f *Func) {
|
||||
v.AuxInt = 0
|
||||
v.Op = OpCopy
|
||||
} else {
|
||||
shadowed.add(v.Args[0].ID)
|
||||
if sz > 0x7fffffff { // work around sparseMap's int32 value type
|
||||
sz = 0x7fffffff
|
||||
}
|
||||
shadowed.set(v.Args[0].ID, int32(sz))
|
||||
}
|
||||
}
|
||||
// walk to previous store
|
||||
|
||||
@@ -8,7 +8,7 @@ import "testing"
|
||||
|
||||
func TestDeadStore(t *testing.T) {
|
||||
c := testConfig(t)
|
||||
elemType := &TypeImpl{Size_: 8, Name: "testtype"}
|
||||
elemType := &TypeImpl{Size_: 1, Name: "testtype"}
|
||||
ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr", Elem_: elemType} // dummy for testing
|
||||
fun := Fun(c, "entry",
|
||||
Bloc("entry",
|
||||
@@ -18,7 +18,7 @@ func TestDeadStore(t *testing.T) {
|
||||
Valu("addr1", OpAddr, ptrType, 0, nil, "sb"),
|
||||
Valu("addr2", OpAddr, ptrType, 0, nil, "sb"),
|
||||
Valu("addr3", OpAddr, ptrType, 0, nil, "sb"),
|
||||
Valu("zero1", OpZero, TypeMem, 8, nil, "addr3", "start"),
|
||||
Valu("zero1", OpZero, TypeMem, 1, nil, "addr3", "start"),
|
||||
Valu("store1", OpStore, TypeMem, 1, nil, "addr1", "v", "zero1"),
|
||||
Valu("store2", OpStore, TypeMem, 1, nil, "addr2", "v", "store1"),
|
||||
Valu("store3", OpStore, TypeMem, 1, nil, "addr1", "v", "store2"),
|
||||
@@ -95,3 +95,32 @@ func TestDeadStoreTypes(t *testing.T) {
|
||||
t.Errorf("store %s incorrectly removed", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeadStoreUnsafe(t *testing.T) {
|
||||
// Make sure a narrow store can't shadow a wider one. The test above
|
||||
// covers the case of two different types, but unsafe pointer casting
|
||||
// can get to a point where the size is changed but type unchanged.
|
||||
c := testConfig(t)
|
||||
ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr"} // dummy for testing
|
||||
fun := Fun(c, "entry",
|
||||
Bloc("entry",
|
||||
Valu("start", OpInitMem, TypeMem, 0, nil),
|
||||
Valu("sb", OpSB, TypeInvalid, 0, nil),
|
||||
Valu("v", OpConstBool, TypeBool, 1, nil),
|
||||
Valu("addr1", OpAddr, ptrType, 0, nil, "sb"),
|
||||
Valu("store1", OpStore, TypeMem, 8, nil, "addr1", "v", "start"), // store 8 bytes
|
||||
Valu("store2", OpStore, TypeMem, 1, nil, "addr1", "v", "store1"), // store 1 byte
|
||||
Goto("exit")),
|
||||
Bloc("exit",
|
||||
Exit("store2")))
|
||||
|
||||
CheckFunc(fun.f)
|
||||
cse(fun.f)
|
||||
dse(fun.f)
|
||||
CheckFunc(fun.f)
|
||||
|
||||
v := fun.values["store1"]
|
||||
if v.Op == OpCopy {
|
||||
t.Errorf("store %s incorrectly removed", v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,7 +724,7 @@ func (w *Writer) Close() error {
|
||||
// the result of NewWriter or NewWriterDict called with dst
|
||||
// and w's level and dictionary.
|
||||
func (w *Writer) Reset(dst io.Writer) {
|
||||
if dw, ok := w.d.w.w.(*dictWriter); ok {
|
||||
if dw, ok := w.d.w.writer.(*dictWriter); ok {
|
||||
// w was created with NewWriterDict
|
||||
dw.w = dst
|
||||
w.d.reset(dw)
|
||||
|
||||
@@ -6,6 +6,7 @@ package flate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"io"
|
||||
@@ -631,3 +632,52 @@ func TestBestSpeed(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var errIO = errors.New("IO error")
|
||||
|
||||
// failWriter fails with errIO exactly at the nth call to Write.
|
||||
type failWriter struct{ n int }
|
||||
|
||||
func (w *failWriter) Write(b []byte) (int, error) {
|
||||
w.n--
|
||||
if w.n == -1 {
|
||||
return 0, errIO
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func TestWriterPersistentError(t *testing.T) {
|
||||
d, err := ioutil.ReadFile("../testdata/Mark.Twain-Tom.Sawyer.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile: %v", err)
|
||||
}
|
||||
d = d[:10000] // Keep this test short
|
||||
|
||||
zw, err := NewWriter(nil, DefaultCompression)
|
||||
if err != nil {
|
||||
t.Fatalf("NewWriter: %v", err)
|
||||
}
|
||||
|
||||
// Sweep over the threshold at which an error is returned.
|
||||
// The variable i makes it such that the ith call to failWriter.Write will
|
||||
// return errIO. Since failWriter errors are not persistent, we must ensure
|
||||
// that flate.Writer errors are persistent.
|
||||
for i := 0; i < 1000; i++ {
|
||||
fw := &failWriter{i}
|
||||
zw.Reset(fw)
|
||||
|
||||
_, werr := zw.Write(d)
|
||||
cerr := zw.Close()
|
||||
if werr != errIO && werr != nil {
|
||||
t.Errorf("test %d, mismatching Write error: got %v, want %v", i, werr, errIO)
|
||||
}
|
||||
if cerr != errIO && fw.n < 0 {
|
||||
t.Errorf("test %d, mismatching Close error: got %v, want %v", i, cerr, errIO)
|
||||
}
|
||||
if fw.n >= 0 {
|
||||
// At this point, the failure threshold was sufficiently high enough
|
||||
// that we wrote the whole stream without any errors.
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,11 @@ var offsetBase = []uint32{
|
||||
var codegenOrder = []uint32{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}
|
||||
|
||||
type huffmanBitWriter struct {
|
||||
w io.Writer
|
||||
// writer is the underlying writer.
|
||||
// Do not use it directly; use the write method, which ensures
|
||||
// that Write errors are sticky.
|
||||
writer io.Writer
|
||||
|
||||
// Data waiting to be written is bytes[0:nbytes]
|
||||
// and then the low nbits of bits.
|
||||
bits uint64
|
||||
@@ -96,7 +100,7 @@ type huffmanBitWriter struct {
|
||||
|
||||
func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter {
|
||||
return &huffmanBitWriter{
|
||||
w: w,
|
||||
writer: w,
|
||||
literalFreq: make([]int32, maxNumLit),
|
||||
offsetFreq: make([]int32, offsetCodeCount),
|
||||
codegen: make([]uint8, maxNumLit+offsetCodeCount+1),
|
||||
@@ -107,7 +111,7 @@ func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter {
|
||||
}
|
||||
|
||||
func (w *huffmanBitWriter) reset(writer io.Writer) {
|
||||
w.w = writer
|
||||
w.writer = writer
|
||||
w.bits, w.nbits, w.nbytes, w.err = 0, 0, 0, nil
|
||||
w.bytes = [bufferSize]byte{}
|
||||
}
|
||||
@@ -129,11 +133,21 @@ func (w *huffmanBitWriter) flush() {
|
||||
n++
|
||||
}
|
||||
w.bits = 0
|
||||
_, w.err = w.w.Write(w.bytes[:n])
|
||||
w.write(w.bytes[:n])
|
||||
w.nbytes = 0
|
||||
}
|
||||
|
||||
func (w *huffmanBitWriter) write(b []byte) {
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
_, w.err = w.writer.Write(b)
|
||||
}
|
||||
|
||||
func (w *huffmanBitWriter) writeBits(b int32, nb uint) {
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
w.bits |= uint64(b) << w.nbits
|
||||
w.nbits += nb
|
||||
if w.nbits >= 48 {
|
||||
@@ -150,7 +164,7 @@ func (w *huffmanBitWriter) writeBits(b int32, nb uint) {
|
||||
bytes[5] = byte(bits >> 40)
|
||||
n += 6
|
||||
if n >= bufferFlushSize {
|
||||
_, w.err = w.w.Write(w.bytes[:n])
|
||||
w.write(w.bytes[:n])
|
||||
n = 0
|
||||
}
|
||||
w.nbytes = n
|
||||
@@ -173,13 +187,10 @@ func (w *huffmanBitWriter) writeBytes(bytes []byte) {
|
||||
n++
|
||||
}
|
||||
if n != 0 {
|
||||
_, w.err = w.w.Write(w.bytes[:n])
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
w.write(w.bytes[:n])
|
||||
}
|
||||
w.nbytes = 0
|
||||
_, w.err = w.w.Write(bytes)
|
||||
w.write(bytes)
|
||||
}
|
||||
|
||||
// RFC 1951 3.2.7 specifies a special run-length encoding for specifying
|
||||
@@ -341,7 +352,7 @@ func (w *huffmanBitWriter) writeCode(c hcode) {
|
||||
bytes[5] = byte(bits >> 40)
|
||||
n += 6
|
||||
if n >= bufferFlushSize {
|
||||
_, w.err = w.w.Write(w.bytes[:n])
|
||||
w.write(w.bytes[:n])
|
||||
n = 0
|
||||
}
|
||||
w.nbytes = n
|
||||
@@ -572,6 +583,9 @@ func (w *huffmanBitWriter) indexTokens(tokens []token) (numLiterals, numOffsets
|
||||
// writeTokens writes a slice of tokens to the output.
|
||||
// codes for literal and offset encoding must be supplied.
|
||||
func (w *huffmanBitWriter) writeTokens(tokens []token, leCodes, oeCodes []hcode) {
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
for _, t := range tokens {
|
||||
if t < matchType {
|
||||
w.writeCode(leCodes[t.literal()])
|
||||
@@ -676,9 +690,9 @@ func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte) {
|
||||
if n < bufferFlushSize {
|
||||
continue
|
||||
}
|
||||
_, w.err = w.w.Write(w.bytes[:n])
|
||||
w.write(w.bytes[:n])
|
||||
if w.err != nil {
|
||||
return
|
||||
return // Return early in the event of write failures
|
||||
}
|
||||
n = 0
|
||||
}
|
||||
|
||||
@@ -48,7 +48,9 @@ func (x *cbc) CryptBlocks(dst, src []byte) {
|
||||
if len(dst) < len(src) {
|
||||
panic("crypto/cipher: output smaller than input")
|
||||
}
|
||||
cryptBlocksChain(x.c, &x.iv[0], &x.b.key[0], &dst[0], &src[0], len(src))
|
||||
if len(src) > 0 {
|
||||
cryptBlocksChain(x.c, &x.iv[0], &x.b.key[0], &dst[0], &src[0], len(src))
|
||||
}
|
||||
}
|
||||
|
||||
func (x *cbc) SetIV(iv []byte) {
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
package cipher_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -34,3 +36,55 @@ func mustPanic(t *testing.T, msg string, f func()) {
|
||||
}()
|
||||
f()
|
||||
}
|
||||
|
||||
func TestEmptyPlaintext(t *testing.T) {
|
||||
var key [16]byte
|
||||
a, err := aes.NewCipher(key[:16])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
d, err := des.NewCipher(key[:8])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
s := 16
|
||||
pt := make([]byte, s)
|
||||
ct := make([]byte, s)
|
||||
for i := 0; i < 16; i++ {
|
||||
pt[i], ct[i] = byte(i), byte(i)
|
||||
}
|
||||
|
||||
assertEqual := func(name string, got, want []byte) {
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Fatalf("%s: got %v, want %v", name, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
for _, b := range []cipher.Block{a, d} {
|
||||
iv := make([]byte, b.BlockSize())
|
||||
cbce := cipher.NewCBCEncrypter(b, iv)
|
||||
cbce.CryptBlocks(ct, pt[:0])
|
||||
assertEqual("CBC encrypt", ct, pt)
|
||||
|
||||
cbcd := cipher.NewCBCDecrypter(b, iv)
|
||||
cbcd.CryptBlocks(ct, pt[:0])
|
||||
assertEqual("CBC decrypt", ct, pt)
|
||||
|
||||
cfbe := cipher.NewCFBEncrypter(b, iv)
|
||||
cfbe.XORKeyStream(ct, pt[:0])
|
||||
assertEqual("CFB encrypt", ct, pt)
|
||||
|
||||
cfbd := cipher.NewCFBDecrypter(b, iv)
|
||||
cfbd.XORKeyStream(ct, pt[:0])
|
||||
assertEqual("CFB decrypt", ct, pt)
|
||||
|
||||
ctr := cipher.NewCTR(b, iv)
|
||||
ctr.XORKeyStream(ct, pt[:0])
|
||||
assertEqual("CTR", ct, pt)
|
||||
|
||||
ofb := cipher.NewOFB(b, iv)
|
||||
ofb.XORKeyStream(ct, pt[:0])
|
||||
assertEqual("OFB", ct, pt)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,10 +29,14 @@ type Conn struct {
|
||||
|
||||
// constant after handshake; protected by handshakeMutex
|
||||
handshakeMutex sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex
|
||||
handshakeErr error // error resulting from handshake
|
||||
vers uint16 // TLS version
|
||||
haveVers bool // version has been negotiated
|
||||
config *Config // configuration passed to constructor
|
||||
// handshakeCond, if not nil, indicates that a goroutine is committed
|
||||
// to running the handshake for this Conn. Other goroutines that need
|
||||
// to wait for the handshake can wait on this, under handshakeMutex.
|
||||
handshakeCond *sync.Cond
|
||||
handshakeErr error // error resulting from handshake
|
||||
vers uint16 // TLS version
|
||||
haveVers bool // version has been negotiated
|
||||
config *Config // configuration passed to constructor
|
||||
// handshakeComplete is true if the connection is currently transfering
|
||||
// application data (i.e. is not currently processing a handshake).
|
||||
handshakeComplete bool
|
||||
@@ -1206,26 +1210,50 @@ func (c *Conn) Handshake() error {
|
||||
// need to check whether a handshake is pending (such as Write) to
|
||||
// block.
|
||||
//
|
||||
// Thus we take c.handshakeMutex first and, if we find that a handshake
|
||||
// is needed, then we unlock, acquire c.in and c.handshakeMutex in the
|
||||
// correct order, and check again.
|
||||
// Thus we first take c.handshakeMutex to check whether a handshake is
|
||||
// needed.
|
||||
//
|
||||
// If so then, previously, this code would unlock handshakeMutex and
|
||||
// then lock c.in and handshakeMutex in the correct order to run the
|
||||
// handshake. The problem was that it was possible for a Read to
|
||||
// complete the handshake once handshakeMutex was unlocked and then
|
||||
// keep c.in while waiting for network data. Thus a concurrent
|
||||
// operation could be blocked on c.in.
|
||||
//
|
||||
// Thus handshakeCond is used to signal that a goroutine is committed
|
||||
// to running the handshake and other goroutines can wait on it if they
|
||||
// need. handshakeCond is protected by handshakeMutex.
|
||||
c.handshakeMutex.Lock()
|
||||
defer c.handshakeMutex.Unlock()
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
if i == 1 {
|
||||
c.handshakeMutex.Unlock()
|
||||
c.in.Lock()
|
||||
defer c.in.Unlock()
|
||||
c.handshakeMutex.Lock()
|
||||
}
|
||||
|
||||
for {
|
||||
if err := c.handshakeErr; err != nil {
|
||||
return err
|
||||
}
|
||||
if c.handshakeComplete {
|
||||
return nil
|
||||
}
|
||||
if c.handshakeCond == nil {
|
||||
break
|
||||
}
|
||||
|
||||
c.handshakeCond.Wait()
|
||||
}
|
||||
|
||||
// Set handshakeCond to indicate that this goroutine is committing to
|
||||
// running the handshake.
|
||||
c.handshakeCond = sync.NewCond(&c.handshakeMutex)
|
||||
c.handshakeMutex.Unlock()
|
||||
|
||||
c.in.Lock()
|
||||
defer c.in.Unlock()
|
||||
|
||||
c.handshakeMutex.Lock()
|
||||
|
||||
// The handshake cannot have completed when handshakeMutex was unlocked
|
||||
// because this goroutine set handshakeCond.
|
||||
if c.handshakeErr != nil || c.handshakeComplete {
|
||||
panic("handshake should not have been able to complete after handshakeCond was set")
|
||||
}
|
||||
|
||||
if c.isClient {
|
||||
@@ -1236,6 +1264,16 @@ func (c *Conn) Handshake() error {
|
||||
if c.handshakeErr == nil {
|
||||
c.handshakes++
|
||||
}
|
||||
|
||||
if c.handshakeErr == nil && !c.handshakeComplete {
|
||||
panic("handshake should have had a result.")
|
||||
}
|
||||
|
||||
// Wake any other goroutines that are waiting for this handshake to
|
||||
// complete.
|
||||
c.handshakeCond.Broadcast()
|
||||
c.handshakeCond = nil
|
||||
|
||||
return c.handshakeErr
|
||||
}
|
||||
|
||||
|
||||
@@ -1045,3 +1045,57 @@ func TestBuffering(t *testing.T) {
|
||||
t.Errorf("expected server handshake to complete with only two writes, but saw %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandshakeRace(t *testing.T) {
|
||||
// This test races a Read and Write to try and complete a handshake in
|
||||
// order to provide some evidence that there are no races or deadlocks
|
||||
// in the handshake locking.
|
||||
for i := 0; i < 32; i++ {
|
||||
c, s := net.Pipe()
|
||||
|
||||
go func() {
|
||||
server := Server(s, testConfig)
|
||||
if err := server.Handshake(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var request [1]byte
|
||||
if n, err := server.Read(request[:]); err != nil || n != 1 {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
server.Write(request[:])
|
||||
server.Close()
|
||||
}()
|
||||
|
||||
startWrite := make(chan struct{})
|
||||
startRead := make(chan struct{})
|
||||
readDone := make(chan struct{})
|
||||
|
||||
client := Client(c, testConfig)
|
||||
go func() {
|
||||
<-startWrite
|
||||
var request [1]byte
|
||||
client.Write(request[:])
|
||||
}()
|
||||
|
||||
go func() {
|
||||
<-startRead
|
||||
var reply [1]byte
|
||||
if n, err := client.Read(reply[:]); err != nil || n != 1 {
|
||||
panic(err)
|
||||
}
|
||||
c.Close()
|
||||
readDone <- struct{}{}
|
||||
}()
|
||||
|
||||
if i&1 == 1 {
|
||||
startWrite <- struct{}{}
|
||||
startRead <- struct{}{}
|
||||
} else {
|
||||
startRead <- struct{}{}
|
||||
startWrite <- struct{}{}
|
||||
}
|
||||
<-readDone
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,21 @@ func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int, errCer
|
||||
return
|
||||
}
|
||||
|
||||
func (s *CertPool) contains(cert *Certificate) bool {
|
||||
if s == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
candidates := s.byName[string(cert.RawSubject)]
|
||||
for _, c := range candidates {
|
||||
if s.certs[c].Equal(cert) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// AddCert adds a certificate to a pool.
|
||||
func (s *CertPool) AddCert(cert *Certificate) {
|
||||
if cert == nil {
|
||||
|
||||
@@ -73,10 +73,11 @@ int useOldCode() {
|
||||
//
|
||||
// On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root
|
||||
// certificates of the system. On failure, the function returns -1.
|
||||
// Additionally, it fills untrustedPemRoots with certs that must be removed from pemRoots.
|
||||
//
|
||||
// Note: The CFDataRef returned in pemRoots must be released (using CFRelease) after
|
||||
// we've consumed its content.
|
||||
int FetchPEMRoots(CFDataRef *pemRoots) {
|
||||
// Note: The CFDataRef returned in pemRoots and untrustedPemRoots must
|
||||
// be released (using CFRelease) after we've consumed its content.
|
||||
int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) {
|
||||
if (useOldCode()) {
|
||||
return FetchPEMRoots_MountainLion(pemRoots);
|
||||
}
|
||||
@@ -93,23 +94,69 @@ int FetchPEMRoots(CFDataRef *pemRoots) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// kSecTrustSettingsResult is defined as CFSTR("kSecTrustSettingsResult"),
|
||||
// but the Go linker's internal linking mode can't handle CFSTR relocations.
|
||||
// Create our own dynamic string instead and release it below.
|
||||
CFStringRef policy = CFStringCreateWithCString(NULL, "kSecTrustSettingsResult", kCFStringEncodingUTF8);
|
||||
|
||||
CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
|
||||
CFMutableDataRef combinedUntrustedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
|
||||
for (int i = 0; i < numDomains; i++) {
|
||||
CFArrayRef certs = NULL;
|
||||
// Only get certificates from domain that are trusted
|
||||
OSStatus err = SecTrustSettingsCopyCertificates(domains[i], &certs);
|
||||
if (err != noErr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int numCerts = CFArrayGetCount(certs);
|
||||
CFIndex numCerts = CFArrayGetCount(certs);
|
||||
for (int j = 0; j < numCerts; j++) {
|
||||
CFDataRef data = NULL;
|
||||
CFErrorRef errRef = NULL;
|
||||
CFArrayRef trustSettings = NULL;
|
||||
SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, j);
|
||||
if (cert == NULL) {
|
||||
continue;
|
||||
}
|
||||
// We only want trusted certs.
|
||||
int untrusted = 0;
|
||||
if (i != 0) {
|
||||
// Certs found in the system domain are always trusted. If the user
|
||||
// configures "Never Trust" on such a cert, it will also be found in the
|
||||
// admin or user domain, causing it to be added to untrustedPemRoots. The
|
||||
// Go code will then clean this up.
|
||||
|
||||
// Trust may be stored in any of the domains. According to Apple's
|
||||
// SecTrustServer.c, "user trust settings overrule admin trust settings",
|
||||
// so take the last trust settings array we find.
|
||||
// Skip the system domain since it is always trusted.
|
||||
for (int k = 1; k < numDomains; k++) {
|
||||
CFArrayRef domainTrustSettings = NULL;
|
||||
err = SecTrustSettingsCopyTrustSettings(cert, domains[k], &domainTrustSettings);
|
||||
if (err == errSecSuccess && domainTrustSettings != NULL) {
|
||||
if (trustSettings) {
|
||||
CFRelease(trustSettings);
|
||||
}
|
||||
trustSettings = domainTrustSettings;
|
||||
}
|
||||
}
|
||||
if (trustSettings == NULL) {
|
||||
// "this certificate must be verified to a known trusted certificate"; aka not a root.
|
||||
continue;
|
||||
}
|
||||
for (CFIndex k = 0; k < CFArrayGetCount(trustSettings); k++) {
|
||||
CFNumberRef cfNum;
|
||||
CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, k);
|
||||
if (CFDictionaryGetValueIfPresent(tSetting, policy, (const void**)&cfNum)){
|
||||
SInt32 result = 0;
|
||||
CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result);
|
||||
// TODO: The rest of the dictionary specifies conditions for evaluation.
|
||||
if (result == kSecTrustSettingsResultDeny) {
|
||||
untrusted = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
CFRelease(trustSettings);
|
||||
}
|
||||
// We only want to add Root CAs, so make sure Subject and Issuer Name match
|
||||
CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, &errRef);
|
||||
if (errRef != NULL) {
|
||||
@@ -138,13 +185,16 @@ int FetchPEMRoots(CFDataRef *pemRoots) {
|
||||
}
|
||||
|
||||
if (data != NULL) {
|
||||
CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data));
|
||||
CFMutableDataRef appendTo = untrusted ? combinedUntrustedData : combinedData;
|
||||
CFDataAppendBytes(appendTo, CFDataGetBytePtr(data), CFDataGetLength(data));
|
||||
CFRelease(data);
|
||||
}
|
||||
}
|
||||
CFRelease(certs);
|
||||
}
|
||||
CFRelease(policy);
|
||||
*pemRoots = combinedData;
|
||||
*untrustedPemRoots = combinedUntrustedData;
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
@@ -158,7 +208,8 @@ func loadSystemRoots() (*CertPool, error) {
|
||||
roots := NewCertPool()
|
||||
|
||||
var data C.CFDataRef = nil
|
||||
err := C.FetchPEMRoots(&data)
|
||||
var untrustedData C.CFDataRef = nil
|
||||
err := C.FetchPEMRoots(&data, &untrustedData)
|
||||
if err == -1 {
|
||||
// TODO: better error message
|
||||
return nil, errors.New("crypto/x509: failed to load darwin system roots with cgo")
|
||||
@@ -167,5 +218,19 @@ func loadSystemRoots() (*CertPool, error) {
|
||||
defer C.CFRelease(C.CFTypeRef(data))
|
||||
buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
|
||||
roots.AppendCertsFromPEM(buf)
|
||||
return roots, nil
|
||||
if untrustedData == nil {
|
||||
return roots, nil
|
||||
}
|
||||
defer C.CFRelease(C.CFTypeRef(untrustedData))
|
||||
buf = C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(untrustedData)), C.int(C.CFDataGetLength(untrustedData)))
|
||||
untrustedRoots := NewCertPool()
|
||||
untrustedRoots.AppendCertsFromPEM(buf)
|
||||
|
||||
trustedRoots := NewCertPool()
|
||||
for _, c := range roots.certs {
|
||||
if !untrustedRoots.contains(c) {
|
||||
trustedRoots.AddCert(c)
|
||||
}
|
||||
}
|
||||
return trustedRoots, nil
|
||||
}
|
||||
|
||||
@@ -6,20 +6,239 @@
|
||||
|
||||
package x509
|
||||
|
||||
import "os/exec"
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var debugExecDarwinRoots = strings.Contains(os.Getenv("GODEBUG"), "x509roots=1")
|
||||
|
||||
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// This code is only used when compiling without cgo.
|
||||
// It is here, instead of root_nocgo_darwin.go, so that tests can check it
|
||||
// even if the tests are run with cgo enabled.
|
||||
// The linker will not include these unused functions in binaries built with cgo enabled.
|
||||
|
||||
// execSecurityRoots finds the macOS list of trusted root certificates
|
||||
// using only command-line tools. This is our fallback path when cgo isn't available.
|
||||
//
|
||||
// The strategy is as follows:
|
||||
//
|
||||
// 1. Run "security trust-settings-export" and "security
|
||||
// trust-settings-export -d" to discover the set of certs with some
|
||||
// user-tweaked trust policy. We're too lazy to parse the XML (at
|
||||
// least at this stage of Go 1.8) to understand what the trust
|
||||
// policy actually is. We just learn that there is _some_ policy.
|
||||
//
|
||||
// 2. Run "security find-certificate" to dump the list of system root
|
||||
// CAs in PEM format.
|
||||
//
|
||||
// 3. For each dumped cert, conditionally verify it with "security
|
||||
// verify-cert" if that cert was in the set discovered in Step 1.
|
||||
// Without the Step 1 optimization, running "security verify-cert"
|
||||
// 150-200 times takes 3.5 seconds. With the optimization, the
|
||||
// whole process takes about 180 milliseconds with 1 untrusted root
|
||||
// CA. (Compared to 110ms in the cgo path)
|
||||
func execSecurityRoots() (*CertPool, error) {
|
||||
hasPolicy, err := getCertsWithTrustPolicy()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if debugExecDarwinRoots {
|
||||
println(fmt.Sprintf("crypto/x509: %d certs have a trust policy", len(hasPolicy)))
|
||||
}
|
||||
|
||||
cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain")
|
||||
data, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
roots := NewCertPool()
|
||||
roots.AppendCertsFromPEM(data)
|
||||
var (
|
||||
mu sync.Mutex
|
||||
roots = NewCertPool()
|
||||
numVerified int // number of execs of 'security verify-cert', for debug stats
|
||||
)
|
||||
|
||||
blockCh := make(chan *pem.Block)
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// Using 4 goroutines to pipe into verify-cert seems to be
|
||||
// about the best we can do. The verify-cert binary seems to
|
||||
// just RPC to another server with coarse locking anyway, so
|
||||
// running 16 at a time for instance doesn't help at all. Due
|
||||
// to the "if hasPolicy" check below, though, we will rarely
|
||||
// (or never) call verify-cert on stock macOS systems, though.
|
||||
// The hope is that we only call verify-cert when the user has
|
||||
// tweaked their trust poliy. These 4 goroutines are only
|
||||
// defensive in the pathological case of many trust edits.
|
||||
for i := 0; i < 4; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for block := range blockCh {
|
||||
cert, err := ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
sha1CapHex := fmt.Sprintf("%X", sha1.Sum(block.Bytes))
|
||||
|
||||
valid := true
|
||||
verifyChecks := 0
|
||||
if hasPolicy[sha1CapHex] {
|
||||
verifyChecks++
|
||||
if !verifyCertWithSystem(block, cert) {
|
||||
valid = false
|
||||
}
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
numVerified += verifyChecks
|
||||
if valid {
|
||||
roots.AddCert(cert)
|
||||
}
|
||||
mu.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
for len(data) > 0 {
|
||||
var block *pem.Block
|
||||
block, data = pem.Decode(data)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
|
||||
continue
|
||||
}
|
||||
blockCh <- block
|
||||
}
|
||||
close(blockCh)
|
||||
wg.Wait()
|
||||
|
||||
if debugExecDarwinRoots {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
println(fmt.Sprintf("crypto/x509: ran security verify-cert %d times", numVerified))
|
||||
}
|
||||
|
||||
return roots, nil
|
||||
}
|
||||
|
||||
func verifyCertWithSystem(block *pem.Block, cert *Certificate) bool {
|
||||
data := pem.EncodeToMemory(block)
|
||||
|
||||
f, err := ioutil.TempFile("", "cert")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "can't create temporary file for cert: %v", err)
|
||||
return false
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
if _, err := f.Write(data); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err)
|
||||
return false
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err)
|
||||
return false
|
||||
}
|
||||
cmd := exec.Command("/usr/bin/security", "verify-cert", "-c", f.Name(), "-l", "-L")
|
||||
var stderr bytes.Buffer
|
||||
if debugExecDarwinRoots {
|
||||
cmd.Stderr = &stderr
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
if debugExecDarwinRoots {
|
||||
println(fmt.Sprintf("crypto/x509: verify-cert rejected %s: %q", cert.Subject.CommonName, bytes.TrimSpace(stderr.Bytes())))
|
||||
}
|
||||
return false
|
||||
}
|
||||
if debugExecDarwinRoots {
|
||||
println(fmt.Sprintf("crypto/x509: verify-cert approved %s", cert.Subject.CommonName))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// getCertsWithTrustPolicy returns the set of certs that have a
|
||||
// possibly-altered trust policy. The keys of the map are capitalized
|
||||
// sha1 hex of the raw cert.
|
||||
// They are the certs that should be checked against `security
|
||||
// verify-cert` to see whether the user altered the default trust
|
||||
// settings. This code is only used for cgo-disabled builds.
|
||||
func getCertsWithTrustPolicy() (map[string]bool, error) {
|
||||
set := map[string]bool{}
|
||||
td, err := ioutil.TempDir("", "x509trustpolicy")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
run := func(file string, args ...string) error {
|
||||
file = filepath.Join(td, file)
|
||||
args = append(args, file)
|
||||
cmd := exec.Command("/usr/bin/security", args...)
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
// If there are no trust settings, the
|
||||
// `security trust-settings-export` command
|
||||
// fails with:
|
||||
// exit status 1, SecTrustSettingsCreateExternalRepresentation: No Trust Settings were found.
|
||||
// Rather than match on English substrings that are probably localized
|
||||
// on macOS, just treat interpret any failure as meaning that there are
|
||||
// no trust settings.
|
||||
if debugExecDarwinRoots {
|
||||
println(fmt.Sprintf("crypto/x509: exec %q: %v, %s", cmd.Args, err, stderr.Bytes()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Gather all the runs of 40 capitalized hex characters.
|
||||
br := bufio.NewReader(f)
|
||||
var hexBuf bytes.Buffer
|
||||
for {
|
||||
b, err := br.ReadByte()
|
||||
isHex := ('A' <= b && b <= 'F') || ('0' <= b && b <= '9')
|
||||
if isHex {
|
||||
hexBuf.WriteByte(b)
|
||||
} else {
|
||||
if hexBuf.Len() == 40 {
|
||||
set[hexBuf.String()] = true
|
||||
}
|
||||
hexBuf.Reset()
|
||||
}
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
if err := run("user", "trust-settings-export"); err != nil {
|
||||
return nil, fmt.Errorf("dump-trust-settings (user): %v", err)
|
||||
}
|
||||
if err := run("admin", "trust-settings-export", "-d"); err != nil {
|
||||
return nil, fmt.Errorf("dump-trust-settings (admin): %v", err)
|
||||
}
|
||||
return set, nil
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ package x509
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSystemRoots(t *testing.T) {
|
||||
@@ -15,13 +16,21 @@ func TestSystemRoots(t *testing.T) {
|
||||
t.Skipf("skipping on %s/%s, no system root", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
sysRoots := systemRootsPool() // actual system roots
|
||||
t0 := time.Now()
|
||||
sysRoots := systemRootsPool() // actual system roots
|
||||
sysRootsDuration := time.Since(t0)
|
||||
|
||||
t1 := time.Now()
|
||||
execRoots, err := execSecurityRoots() // non-cgo roots
|
||||
execSysRootsDuration := time.Since(t1)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read system roots: %v", err)
|
||||
}
|
||||
|
||||
t.Logf(" cgo sys roots: %v", sysRootsDuration)
|
||||
t.Logf("non-cgo sys roots: %v", execSysRootsDuration)
|
||||
|
||||
for _, tt := range []*CertPool{sysRoots, execRoots} {
|
||||
if tt == nil {
|
||||
t.Fatal("no system roots")
|
||||
@@ -29,6 +38,7 @@ func TestSystemRoots(t *testing.T) {
|
||||
// On Mavericks, there are 212 bundled certs; require only
|
||||
// 150 here, since this is just a sanity check, and the
|
||||
// exact number will vary over time.
|
||||
t.Logf("got %d roots", len(tt.certs))
|
||||
if want, have := 150, len(tt.certs); have < want {
|
||||
t.Fatalf("want at least %d system roots, have %d", want, have)
|
||||
}
|
||||
|
||||
@@ -4,14 +4,9 @@
|
||||
|
||||
package crc32
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
vxMinLen = 64
|
||||
vxAlignment = 16
|
||||
vxAlignMask = vxAlignment - 1
|
||||
vxAlignMask = 15 // align to 16 bytes
|
||||
)
|
||||
|
||||
// hasVectorFacility reports whether the machine has the z/Architecture
|
||||
@@ -49,20 +44,13 @@ func genericIEEE(crc uint32, p []byte) uint32 {
|
||||
return update(crc, IEEETable, p)
|
||||
}
|
||||
|
||||
// updateCastagnoli calculates the checksum of p using genericCastagnoli to
|
||||
// align the data appropriately for vectorCastagnoli. It avoids using
|
||||
// vectorCastagnoli entirely if the length of p is less than or equal to
|
||||
// vxMinLen.
|
||||
// updateCastagnoli calculates the checksum of p using
|
||||
// vectorizedCastagnoli if possible and falling back onto
|
||||
// genericCastagnoli as needed.
|
||||
func updateCastagnoli(crc uint32, p []byte) uint32 {
|
||||
// Use vectorized function if vector facility is available and
|
||||
// data length is above threshold.
|
||||
if hasVX && len(p) > vxMinLen {
|
||||
pAddr := uintptr(unsafe.Pointer(&p[0]))
|
||||
if pAddr&vxAlignMask != 0 {
|
||||
prealign := vxAlignment - int(pAddr&vxAlignMask)
|
||||
crc = genericCastagnoli(crc, p[:prealign])
|
||||
p = p[prealign:]
|
||||
}
|
||||
if hasVX && len(p) >= vxMinLen {
|
||||
aligned := len(p) & ^vxAlignMask
|
||||
crc = vectorizedCastagnoli(crc, p[:aligned])
|
||||
p = p[aligned:]
|
||||
@@ -75,19 +63,12 @@ func updateCastagnoli(crc uint32, p []byte) uint32 {
|
||||
return genericCastagnoli(crc, p)
|
||||
}
|
||||
|
||||
// updateIEEE calculates the checksum of p using genericIEEE to align the data
|
||||
// appropriately for vectorIEEE. It avoids using vectorIEEE entirely if the length
|
||||
// of p is less than or equal to vxMinLen.
|
||||
// updateIEEE calculates the checksum of p using vectorizedIEEE if
|
||||
// possible and falling back onto genericIEEE as needed.
|
||||
func updateIEEE(crc uint32, p []byte) uint32 {
|
||||
// Use vectorized function if vector facility is available and
|
||||
// data length is above threshold.
|
||||
if hasVX && len(p) > vxMinLen {
|
||||
pAddr := uintptr(unsafe.Pointer(&p[0]))
|
||||
if pAddr&vxAlignMask != 0 {
|
||||
prealign := vxAlignment - int(pAddr&vxAlignMask)
|
||||
crc = genericIEEE(crc, p[:prealign])
|
||||
p = p[prealign:]
|
||||
}
|
||||
if hasVX && len(p) >= vxMinLen {
|
||||
aligned := len(p) & ^vxAlignMask
|
||||
crc = vectorizedIEEE(crc, p[:aligned])
|
||||
p = p[aligned:]
|
||||
|
||||
@@ -128,6 +128,10 @@ TEXT vectorizedBody<>(SB),NOSPLIT,$0
|
||||
VZERO V0
|
||||
VLVGF $3, R2, V0
|
||||
|
||||
// Crash if the input size is less than 64-bytes.
|
||||
CMP R4, $64
|
||||
BLT crash
|
||||
|
||||
// Load a 64-byte data chunk and XOR with CRC
|
||||
VLM 0(R3), V1, V4 // 64-bytes into V1..V4
|
||||
|
||||
@@ -243,3 +247,6 @@ done:
|
||||
XOR $0xffffffff, R2 // NOTW R2
|
||||
MOVWZ R2, ret + 32(FP)
|
||||
RET
|
||||
|
||||
crash:
|
||||
MOVD $0, (R0) // input size is less than 64-bytes
|
||||
|
||||
@@ -18,15 +18,16 @@ func (mr *multiReader) Read(p []byte) (n int, err error) {
|
||||
}
|
||||
}
|
||||
n, err = mr.readers[0].Read(p)
|
||||
if err == EOF {
|
||||
mr.readers = mr.readers[1:]
|
||||
}
|
||||
if n > 0 || err != EOF {
|
||||
if err == EOF {
|
||||
// Don't return EOF yet. There may be more bytes
|
||||
// in the remaining readers.
|
||||
if err == EOF && len(mr.readers) > 0 {
|
||||
// Don't return EOF yet. More readers remain.
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
mr.readers = mr.readers[1:]
|
||||
}
|
||||
return 0, EOF
|
||||
}
|
||||
|
||||
@@ -196,3 +196,41 @@ func TestMultiReaderFlatten(t *testing.T) {
|
||||
myDepth+2, readDepth)
|
||||
}
|
||||
}
|
||||
|
||||
// byteAndEOFReader is a Reader which reads one byte (the underlying
|
||||
// byte) and io.EOF at once in its Read call.
|
||||
type byteAndEOFReader byte
|
||||
|
||||
func (b byteAndEOFReader) Read(p []byte) (n int, err error) {
|
||||
if len(p) == 0 {
|
||||
// Read(0 bytes) is useless. We expect no such useless
|
||||
// calls in this test.
|
||||
panic("unexpected call")
|
||||
}
|
||||
p[0] = byte(b)
|
||||
return 1, EOF
|
||||
}
|
||||
|
||||
// In Go 1.7, this yielded bytes forever.
|
||||
func TestMultiReaderSingleByteWithEOF(t *testing.T) {
|
||||
got, err := ioutil.ReadAll(LimitReader(MultiReader(byteAndEOFReader('a'), byteAndEOFReader('b')), 10))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
const want = "ab"
|
||||
if string(got) != want {
|
||||
t.Errorf("got %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a reader returning (n, EOF) at the end of an MultiReader
|
||||
// chain continues to return EOF on its final read, rather than
|
||||
// yielding a (0, EOF).
|
||||
func TestMultiReaderFinalEOF(t *testing.T) {
|
||||
r := MultiReader(bytes.NewReader(nil), byteAndEOFReader('a'))
|
||||
buf := make([]byte, 2)
|
||||
n, err := r.Read(buf)
|
||||
if n != 1 || err != EOF {
|
||||
t.Errorf("got %v, %v; want 1, EOF", n, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,8 +79,10 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
_, err = io.Copy(file, io.MultiReader(&b, p))
|
||||
if cerr := file.Close(); err == nil {
|
||||
err = cerr
|
||||
}
|
||||
if err != nil {
|
||||
os.Remove(file.Name())
|
||||
return nil, err
|
||||
|
||||
@@ -141,7 +141,7 @@ func (d *Dialer) dialDNS(ctx context.Context, network, server string) (dnsConn,
|
||||
}
|
||||
|
||||
// exchange sends a query on the connection and hopes for a response.
|
||||
func exchange(ctx context.Context, server, name string, qtype uint16) (*dnsMsg, error) {
|
||||
func exchange(ctx context.Context, server, name string, qtype uint16, timeout time.Duration) (*dnsMsg, error) {
|
||||
d := testHookDNSDialer()
|
||||
out := dnsMsg{
|
||||
dnsMsgHdr: dnsMsgHdr{
|
||||
@@ -152,6 +152,12 @@ func exchange(ctx context.Context, server, name string, qtype uint16) (*dnsMsg,
|
||||
},
|
||||
}
|
||||
for _, network := range []string{"udp", "tcp"} {
|
||||
// TODO(mdempsky): Refactor so defers from UDP-based
|
||||
// exchanges happen before TCP-based exchange.
|
||||
|
||||
ctx, cancel := context.WithDeadline(ctx, time.Now().Add(timeout))
|
||||
defer cancel()
|
||||
|
||||
c, err := d.dialDNS(ctx, network, server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -180,17 +186,10 @@ func tryOneName(ctx context.Context, cfg *dnsConfig, name string, qtype uint16)
|
||||
return "", nil, &DNSError{Err: "no DNS servers", Name: name}
|
||||
}
|
||||
|
||||
deadline := time.Now().Add(cfg.timeout)
|
||||
if old, ok := ctx.Deadline(); !ok || deadline.Before(old) {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithDeadline(ctx, deadline)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
var lastErr error
|
||||
for i := 0; i < cfg.attempts; i++ {
|
||||
for _, server := range cfg.servers {
|
||||
msg, err := exchange(ctx, server, name, qtype)
|
||||
msg, err := exchange(ctx, server, name, qtype, cfg.timeout)
|
||||
if err != nil {
|
||||
lastErr = &DNSError{
|
||||
Err: err.Error(),
|
||||
@@ -338,8 +337,9 @@ func lookup(ctx context.Context, name string, qtype uint16) (cname string, rrs [
|
||||
}
|
||||
|
||||
// avoidDNS reports whether this is a hostname for which we should not
|
||||
// use DNS. Currently this includes only .onion and .local names,
|
||||
// per RFC 7686 and RFC 6762, respectively. See golang.org/issue/13705.
|
||||
// use DNS. Currently this includes only .onion, per RFC 7686. See
|
||||
// golang.org/issue/13705. Does not cover .local names (RFC 6762),
|
||||
// see golang.org/issue/16739.
|
||||
func avoidDNS(name string) bool {
|
||||
if name == "" {
|
||||
return true
|
||||
@@ -347,7 +347,7 @@ func avoidDNS(name string) bool {
|
||||
if name[len(name)-1] == '.' {
|
||||
name = name[:len(name)-1]
|
||||
}
|
||||
return stringsHasSuffixFold(name, ".onion") || stringsHasSuffixFold(name, ".local")
|
||||
return stringsHasSuffixFold(name, ".onion")
|
||||
}
|
||||
|
||||
// nameList returns a list of names for sequential DNS queries.
|
||||
|
||||
@@ -40,9 +40,9 @@ func TestDNSTransportFallback(t *testing.T) {
|
||||
testenv.MustHaveExternalNetwork(t)
|
||||
|
||||
for _, tt := range dnsTransportFallbackTests {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(tt.timeout)*time.Second)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
msg, err := exchange(ctx, tt.server, tt.name, tt.qtype)
|
||||
msg, err := exchange(ctx, tt.server, tt.name, tt.qtype, time.Second)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
@@ -82,9 +82,9 @@ func TestSpecialDomainName(t *testing.T) {
|
||||
|
||||
server := "8.8.8.8:53"
|
||||
for _, tt := range specialDomainNameTests {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
msg, err := exchange(ctx, server, tt.name, tt.qtype)
|
||||
msg, err := exchange(ctx, server, tt.name, tt.qtype, 3*time.Second)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
@@ -112,10 +112,11 @@ func TestAvoidDNSName(t *testing.T) {
|
||||
{"foo.ONION", true},
|
||||
{"foo.ONION.", true},
|
||||
|
||||
{"foo.local.", true},
|
||||
{"foo.local", true},
|
||||
{"foo.LOCAL", true},
|
||||
{"foo.LOCAL.", true},
|
||||
// But do resolve *.local address; Issue 16739
|
||||
{"foo.local.", false},
|
||||
{"foo.local", false},
|
||||
{"foo.LOCAL", false},
|
||||
{"foo.LOCAL.", false},
|
||||
|
||||
{"", true}, // will be rejected earlier too
|
||||
|
||||
@@ -500,7 +501,7 @@ func TestErrorForOriginalNameWhenSearching(t *testing.T) {
|
||||
d := &fakeDNSDialer{}
|
||||
testHookDNSDialer = func() dnsDialer { return d }
|
||||
|
||||
d.rh = func(s string, q *dnsMsg) (*dnsMsg, error) {
|
||||
d.rh = func(s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) {
|
||||
r := &dnsMsg{
|
||||
dnsMsgHdr: dnsMsgHdr{
|
||||
id: q.id,
|
||||
@@ -539,14 +540,15 @@ func TestIgnoreLameReferrals(t *testing.T) {
|
||||
}
|
||||
defer conf.teardown()
|
||||
|
||||
if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", "nameserver 192.0.2.2"}); err != nil {
|
||||
if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", // the one that will give a lame referral
|
||||
"nameserver 192.0.2.2"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
d := &fakeDNSDialer{}
|
||||
testHookDNSDialer = func() dnsDialer { return d }
|
||||
|
||||
d.rh = func(s string, q *dnsMsg) (*dnsMsg, error) {
|
||||
d.rh = func(s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) {
|
||||
t.Log(s, q)
|
||||
r := &dnsMsg{
|
||||
dnsMsgHdr: dnsMsgHdr{
|
||||
@@ -633,28 +635,30 @@ func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) {
|
||||
|
||||
type fakeDNSDialer struct {
|
||||
// reply handler
|
||||
rh func(s string, q *dnsMsg) (*dnsMsg, error)
|
||||
rh func(s string, q *dnsMsg, t time.Time) (*dnsMsg, error)
|
||||
}
|
||||
|
||||
func (f *fakeDNSDialer) dialDNS(_ context.Context, n, s string) (dnsConn, error) {
|
||||
return &fakeDNSConn{f.rh, s}, nil
|
||||
return &fakeDNSConn{f.rh, s, time.Time{}}, nil
|
||||
}
|
||||
|
||||
type fakeDNSConn struct {
|
||||
rh func(s string, q *dnsMsg) (*dnsMsg, error)
|
||||
rh func(s string, q *dnsMsg, t time.Time) (*dnsMsg, error)
|
||||
s string
|
||||
t time.Time
|
||||
}
|
||||
|
||||
func (f *fakeDNSConn) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeDNSConn) SetDeadline(time.Time) error {
|
||||
func (f *fakeDNSConn) SetDeadline(t time.Time) error {
|
||||
f.t = t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeDNSConn) dnsRoundTrip(q *dnsMsg) (*dnsMsg, error) {
|
||||
return f.rh(f.s, q)
|
||||
return f.rh(f.s, q, f.t)
|
||||
}
|
||||
|
||||
// UDP round-tripper algorithm should ignore invalid DNS responses (issue 13281).
|
||||
@@ -724,3 +728,72 @@ func TestIgnoreDNSForgeries(t *testing.T) {
|
||||
t.Errorf("got address %v, want %v", got, TestAddr)
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 16865. If a name server times out, continue to the next.
|
||||
func TestRetryTimeout(t *testing.T) {
|
||||
origTestHookDNSDialer := testHookDNSDialer
|
||||
defer func() { testHookDNSDialer = origTestHookDNSDialer }()
|
||||
|
||||
conf, err := newResolvConfTest()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer conf.teardown()
|
||||
|
||||
if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", // the one that will timeout
|
||||
"nameserver 192.0.2.2"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
d := &fakeDNSDialer{}
|
||||
testHookDNSDialer = func() dnsDialer { return d }
|
||||
|
||||
var deadline0 time.Time
|
||||
|
||||
d.rh = func(s string, q *dnsMsg, deadline time.Time) (*dnsMsg, error) {
|
||||
t.Log(s, q, deadline)
|
||||
|
||||
if deadline.IsZero() {
|
||||
t.Error("zero deadline")
|
||||
}
|
||||
|
||||
if s == "192.0.2.1:53" {
|
||||
deadline0 = deadline
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
return nil, errTimeout
|
||||
}
|
||||
|
||||
if deadline == deadline0 {
|
||||
t.Error("deadline didn't change")
|
||||
}
|
||||
|
||||
r := &dnsMsg{
|
||||
dnsMsgHdr: dnsMsgHdr{
|
||||
id: q.id,
|
||||
response: true,
|
||||
recursion_available: true,
|
||||
},
|
||||
question: q.question,
|
||||
answer: []dnsRR{
|
||||
&dnsRR_CNAME{
|
||||
Hdr: dnsRR_Header{
|
||||
Name: q.question[0].Name,
|
||||
Rrtype: dnsTypeCNAME,
|
||||
Class: dnsClassINET,
|
||||
},
|
||||
Cname: "golang.org",
|
||||
},
|
||||
},
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
_, err = goLookupCNAME(context.Background(), "www.golang.org")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if deadline0.IsZero() {
|
||||
t.Error("deadline0 still zero", deadline0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,7 +468,7 @@ func TestH12_RequestContentLength_Known_NonZero(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestH12_RequestContentLength_Known_Zero(t *testing.T) {
|
||||
h12requestContentLength(t, func() io.Reader { return strings.NewReader("") }, 0)
|
||||
h12requestContentLength(t, func() io.Reader { return nil }, 0)
|
||||
}
|
||||
|
||||
func TestH12_RequestContentLength_Unknown(t *testing.T) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Code generated by golang.org/x/tools/cmd/bundle.
|
||||
//go:generate bundle -o h2_bundle.go -prefix http2 golang.org/x/net/http2
|
||||
//go:generate bundle -o h2_bundle.go -prefix http2 -underscore golang.org/x/net/http2
|
||||
|
||||
// Package http2 implements the HTTP/2 protocol.
|
||||
//
|
||||
@@ -5448,28 +5448,17 @@ func http2checkConnHeaders(req *Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func http2bodyAndLength(req *Request) (body io.Reader, contentLen int64) {
|
||||
body = req.Body
|
||||
if body == nil {
|
||||
return nil, 0
|
||||
// actualContentLength returns a sanitized version of
|
||||
// req.ContentLength, where 0 actually means zero (not unknown) and -1
|
||||
// means unknown.
|
||||
func http2actualContentLength(req *Request) int64 {
|
||||
if req.Body == nil {
|
||||
return 0
|
||||
}
|
||||
if req.ContentLength != 0 {
|
||||
return req.Body, req.ContentLength
|
||||
return req.ContentLength
|
||||
}
|
||||
|
||||
// We have a body but a zero content length. Test to see if
|
||||
// it's actually zero or just unset.
|
||||
var buf [1]byte
|
||||
n, rerr := io.ReadFull(body, buf[:])
|
||||
if rerr != nil && rerr != io.EOF {
|
||||
return http2errorReader{rerr}, -1
|
||||
}
|
||||
if n == 1 {
|
||||
|
||||
return io.MultiReader(bytes.NewReader(buf[:]), body), -1
|
||||
}
|
||||
|
||||
return nil, 0
|
||||
return -1
|
||||
}
|
||||
|
||||
func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) {
|
||||
@@ -5483,9 +5472,6 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) {
|
||||
}
|
||||
hasTrailers := trailers != ""
|
||||
|
||||
body, contentLen := http2bodyAndLength(req)
|
||||
hasBody := body != nil
|
||||
|
||||
cc.mu.Lock()
|
||||
cc.lastActive = time.Now()
|
||||
if cc.closed || !cc.canTakeNewRequestLocked() {
|
||||
@@ -5493,6 +5479,10 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) {
|
||||
return nil, http2errClientConnUnusable
|
||||
}
|
||||
|
||||
body := req.Body
|
||||
hasBody := body != nil
|
||||
contentLen := http2actualContentLength(req)
|
||||
|
||||
// TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere?
|
||||
var requestedGzip bool
|
||||
if !cc.t.disableCompression() &&
|
||||
@@ -5714,8 +5704,13 @@ func (cs *http2clientStream) writeRequestBody(body io.Reader, bodyCloser io.Clos
|
||||
}
|
||||
}
|
||||
|
||||
if sentEnd {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var trls []byte
|
||||
if !sentEnd && hasTrailers {
|
||||
if hasTrailers {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
trls = cc.encodeTrailers(req)
|
||||
|
||||
@@ -498,8 +498,9 @@ func (t *Transport) CloseIdleConnections() {
|
||||
// CancelRequest cancels an in-flight request by closing its connection.
|
||||
// CancelRequest should only be called after RoundTrip has returned.
|
||||
//
|
||||
// Deprecated: Use Request.Cancel instead. CancelRequest cannot cancel
|
||||
// HTTP/2 requests.
|
||||
// Deprecated: Use Request.WithContext to create a request with a
|
||||
// cancelable context instead. CancelRequest cannot cancel HTTP/2
|
||||
// requests.
|
||||
func (t *Transport) CancelRequest(req *Request) {
|
||||
t.reqMu.Lock()
|
||||
cancel := t.reqCanceler[req]
|
||||
@@ -589,6 +590,7 @@ var (
|
||||
errReadLoopExiting = errors.New("http: persistConn.readLoop exiting")
|
||||
errServerClosedIdle = errors.New("http: server closed idle connection")
|
||||
errIdleConnTimeout = errors.New("http: idle connection timeout")
|
||||
errNotCachingH2Conn = errors.New("http: not caching alternate protocol's connections")
|
||||
)
|
||||
|
||||
// transportReadFromServerError is used by Transport.readLoop when the
|
||||
@@ -632,6 +634,9 @@ func (t *Transport) tryPutIdleConn(pconn *persistConn) error {
|
||||
if pconn.isBroken() {
|
||||
return errConnBroken
|
||||
}
|
||||
if pconn.alt != nil {
|
||||
return errNotCachingH2Conn
|
||||
}
|
||||
pconn.markReused()
|
||||
key := pconn.cacheKey
|
||||
|
||||
|
||||
@@ -3511,6 +3511,61 @@ func TestTransportIdleConnTimeout(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 16208: Go 1.7 crashed after Transport.IdleConnTimeout if an
|
||||
// HTTP/2 connection was established but but its caller no longer
|
||||
// wanted it. (Assuming the connection cache was enabled, which it is
|
||||
// by default)
|
||||
//
|
||||
// This test reproduced the crash by setting the IdleConnTimeout low
|
||||
// (to make the test reasonable) and then making a request which is
|
||||
// canceled by the DialTLS hook, which then also waits to return the
|
||||
// real connection until after the RoundTrip saw the error. Then we
|
||||
// know the successful tls.Dial from DialTLS will need to go into the
|
||||
// idle pool. Then we give it a of time to explode.
|
||||
func TestIdleConnH2Crash(t *testing.T) {
|
||||
cst := newClientServerTest(t, h2Mode, HandlerFunc(func(w ResponseWriter, r *Request) {
|
||||
// nothing
|
||||
}))
|
||||
defer cst.close()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
gotErr := make(chan bool, 1)
|
||||
|
||||
cst.tr.IdleConnTimeout = 5 * time.Millisecond
|
||||
cst.tr.DialTLS = func(network, addr string) (net.Conn, error) {
|
||||
cancel()
|
||||
<-gotErr
|
||||
c, err := tls.Dial(network, addr, &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
NextProtos: []string{"h2"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
if cs := c.ConnectionState(); cs.NegotiatedProtocol != "h2" {
|
||||
t.Errorf("protocol = %q; want %q", cs.NegotiatedProtocol, "h2")
|
||||
c.Close()
|
||||
return nil, errors.New("bogus")
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
req, _ := NewRequest("GET", cst.ts.URL, nil)
|
||||
req = req.WithContext(ctx)
|
||||
res, err := cst.c.Do(req)
|
||||
if err == nil {
|
||||
res.Body.Close()
|
||||
t.Fatal("unexpected success")
|
||||
}
|
||||
gotErr <- true
|
||||
|
||||
// Wait for the explosion.
|
||||
time.Sleep(cst.tr.IdleConnTimeout * 10)
|
||||
}
|
||||
|
||||
type funcConn struct {
|
||||
net.Conn
|
||||
read func([]byte) (int, error)
|
||||
|
||||
@@ -4,4 +4,7 @@
|
||||
|
||||
package filepath
|
||||
|
||||
var ToNorm = toNorm
|
||||
var (
|
||||
ToNorm = toNorm
|
||||
NormBase = normBase
|
||||
)
|
||||
|
||||
@@ -840,7 +840,7 @@ func TestEvalSymlinks(t *testing.T) {
|
||||
if p, err := filepath.EvalSymlinks(path); err != nil {
|
||||
t.Errorf("EvalSymlinks(%q) error: %v", d.path, err)
|
||||
} else if filepath.Clean(p) != filepath.Clean(dest) {
|
||||
t.Errorf("Clean(%q)=%q, want %q", path, p, dest)
|
||||
t.Errorf("EvalSymlinks(%q)=%q, want %q", path, p, dest)
|
||||
}
|
||||
|
||||
// test EvalSymlinks(".")
|
||||
@@ -872,6 +872,34 @@ func TestEvalSymlinks(t *testing.T) {
|
||||
t.Errorf(`EvalSymlinks(".") in %q directory returns %q, want "." or %q`, d.path, p, want)
|
||||
}()
|
||||
|
||||
// test EvalSymlinks(".."+path)
|
||||
func() {
|
||||
defer func() {
|
||||
err := os.Chdir(wd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
err := os.Chdir(simpleJoin(tmpDir, "test"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
path := simpleJoin("..", d.path)
|
||||
dest := simpleJoin("..", d.dest)
|
||||
if filepath.IsAbs(d.dest) || os.IsPathSeparator(d.dest[0]) {
|
||||
dest = d.dest
|
||||
}
|
||||
|
||||
if p, err := filepath.EvalSymlinks(path); err != nil {
|
||||
t.Errorf("EvalSymlinks(%q) error: %v", d.path, err)
|
||||
} else if filepath.Clean(p) != filepath.Clean(dest) {
|
||||
t.Errorf("EvalSymlinks(%q)=%q, want %q", path, p, dest)
|
||||
}
|
||||
}()
|
||||
|
||||
// test EvalSymlinks where parameter is relative path
|
||||
func() {
|
||||
defer func() {
|
||||
@@ -889,7 +917,7 @@ func TestEvalSymlinks(t *testing.T) {
|
||||
if p, err := filepath.EvalSymlinks(d.path); err != nil {
|
||||
t.Errorf("EvalSymlinks(%q) error: %v", d.path, err)
|
||||
} else if filepath.Clean(p) != filepath.Clean(d.dest) {
|
||||
t.Errorf("Clean(%q)=%q, want %q", d.path, p, d.dest)
|
||||
t.Errorf("EvalSymlinks(%q)=%q, want %q", d.path, p, d.dest)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -329,9 +329,106 @@ func TestToNorm(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
got, err := filepath.ToNorm(test.arg, stubBase)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected toNorm error, arg: %s, err: %v\n", test.arg, err)
|
||||
t.Errorf("toNorm(%s) failed: %v\n", test.arg, err)
|
||||
} else if got != test.want {
|
||||
t.Errorf("toNorm error, arg: %s, want: %s, got: %s\n", test.arg, test.want, got)
|
||||
t.Errorf("toNorm(%s) returns %s, but %s expected\n", test.arg, got, test.want)
|
||||
}
|
||||
}
|
||||
|
||||
testPath := `{{tmp}}\test\foo\bar`
|
||||
|
||||
testsDir := []struct {
|
||||
wd string
|
||||
arg string
|
||||
want string
|
||||
}{
|
||||
// test absolute paths
|
||||
{".", `{{tmp}}\test\foo\bar`, `{{tmp}}\test\foo\bar`},
|
||||
{".", `{{tmp}}\.\test/foo\bar`, `{{tmp}}\test\foo\bar`},
|
||||
{".", `{{tmp}}\test\..\test\foo\bar`, `{{tmp}}\test\foo\bar`},
|
||||
{".", `{{tmp}}\TEST\FOO\BAR`, `{{tmp}}\test\foo\bar`},
|
||||
|
||||
// test relative paths begin with drive letter
|
||||
{`{{tmp}}\test`, `{{tmpvol}}.`, `{{tmpvol}}.`},
|
||||
{`{{tmp}}\test`, `{{tmpvol}}..`, `{{tmpvol}}..`},
|
||||
{`{{tmp}}\test`, `{{tmpvol}}foo\bar`, `{{tmpvol}}foo\bar`},
|
||||
{`{{tmp}}\test`, `{{tmpvol}}.\foo\bar`, `{{tmpvol}}foo\bar`},
|
||||
{`{{tmp}}\test`, `{{tmpvol}}foo\..\foo\bar`, `{{tmpvol}}foo\bar`},
|
||||
{`{{tmp}}\test`, `{{tmpvol}}FOO\BAR`, `{{tmpvol}}foo\bar`},
|
||||
|
||||
// test relative paths begin with '\'
|
||||
{".", `{{tmpnovol}}\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
|
||||
{".", `{{tmpnovol}}\.\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
|
||||
{".", `{{tmpnovol}}\test\..\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
|
||||
{".", `{{tmpnovol}}\TEST\FOO\BAR`, `{{tmpnovol}}\test\foo\bar`},
|
||||
|
||||
// test relative paths begin without '\'
|
||||
{`{{tmp}}\test`, ".", `.`},
|
||||
{`{{tmp}}\test`, "..", `..`},
|
||||
{`{{tmp}}\test`, `foo\bar`, `foo\bar`},
|
||||
{`{{tmp}}\test`, `.\foo\bar`, `foo\bar`},
|
||||
{`{{tmp}}\test`, `foo\..\foo\bar`, `foo\bar`},
|
||||
{`{{tmp}}\test`, `FOO\BAR`, `foo\bar`},
|
||||
}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err := os.Chdir(cwd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
tmp, err := ioutil.TempDir("", "testToNorm")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
// ioutil.TempDir might return "non-canonical" name.
|
||||
tmp, err = filepath.EvalSymlinks(tmp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = os.MkdirAll(strings.Replace(testPath, "{{tmp}}", tmp, -1), 0777)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tmpVol := filepath.VolumeName(tmp)
|
||||
tmpNoVol := tmp[len(tmpVol):]
|
||||
|
||||
for _, test := range testsDir {
|
||||
wd := strings.Replace(strings.Replace(strings.Replace(test.wd, "{{tmp}}", tmp, -1), "{{tmpvol}}", tmpVol, -1), "{{tmpnovol}}", tmpNoVol, -1)
|
||||
arg := strings.Replace(strings.Replace(strings.Replace(test.arg, "{{tmp}}", tmp, -1), "{{tmpvol}}", tmpVol, -1), "{{tmpnovol}}", tmpNoVol, -1)
|
||||
want := strings.Replace(strings.Replace(strings.Replace(test.want, "{{tmp}}", tmp, -1), "{{tmpvol}}", tmpVol, -1), "{{tmpnovol}}", tmpNoVol, -1)
|
||||
|
||||
if test.wd == "." {
|
||||
err := os.Chdir(cwd)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
err := os.Chdir(wd)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
got, err := filepath.ToNorm(arg, filepath.NormBase)
|
||||
if err != nil {
|
||||
t.Errorf("toNorm(%s) failed: %v (wd=%s)\n", arg, err, wd)
|
||||
} else if got != want {
|
||||
t.Errorf("toNorm(%s) returns %s, but %s expected (wd=%s)\n", arg, got, want, wd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ func normVolumeName(path string) string {
|
||||
return strings.ToUpper(volume)
|
||||
}
|
||||
|
||||
// normBase retruns the last element of path.
|
||||
// normBase returns the last element of path with correct case.
|
||||
func normBase(path string) (string, error) {
|
||||
p, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
@@ -40,7 +40,24 @@ func normBase(path string) (string, error) {
|
||||
return syscall.UTF16ToString(data.FileName[:]), nil
|
||||
}
|
||||
|
||||
func toNorm(path string, base func(string) (string, error)) (string, error) {
|
||||
// baseIsDotDot returns whether the last element of path is "..".
|
||||
// The given path should be 'Clean'-ed in advance.
|
||||
func baseIsDotDot(path string) bool {
|
||||
i := strings.LastIndexByte(path, Separator)
|
||||
return path[i+1:] == ".."
|
||||
}
|
||||
|
||||
// toNorm returns the normalized path that is guranteed to be unique.
|
||||
// It should accept the following formats:
|
||||
// * UNC paths (e.g \\server\share\foo\bar)
|
||||
// * absolute paths (e.g C:\foo\bar)
|
||||
// * relative paths begin with drive letter (e.g C:foo\bar, C:..\foo\bar, C:.., C:.)
|
||||
// * relative paths begin with '\' (e.g \foo\bar)
|
||||
// * relative paths begin without '\' (e.g foo\bar, ..\foo\bar, .., .)
|
||||
// The returned normalized path will be in the same form (of 5 listed above) as the input path.
|
||||
// If two paths A and B are indicating the same file with the same format, toNorm(A) should be equal to toNorm(B).
|
||||
// The normBase parameter should be equal to the normBase func, except for in tests. See docs on the normBase func.
|
||||
func toNorm(path string, normBase func(string) (string, error)) (string, error) {
|
||||
if path == "" {
|
||||
return path, nil
|
||||
}
|
||||
@@ -58,7 +75,13 @@ func toNorm(path string, base func(string) (string, error)) (string, error) {
|
||||
var normPath string
|
||||
|
||||
for {
|
||||
name, err := base(volume + path)
|
||||
if baseIsDotDot(path) {
|
||||
normPath = path + `\` + normPath
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
name, err := normBase(volume + path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -5720,6 +5720,8 @@ func TestTypeStrings(t *testing.T) {
|
||||
{TypeOf(new(XM)), "*reflect_test.XM"},
|
||||
{TypeOf(new(XM).String), "func() string"},
|
||||
{TypeOf(new(XM)).Method(0).Type, "func(*reflect_test.XM) string"},
|
||||
{ChanOf(3, TypeOf(XM{})), "chan reflect_test.XM"},
|
||||
{MapOf(TypeOf(int(0)), TypeOf(XM{})), "map[int]reflect_test.XM"},
|
||||
}
|
||||
|
||||
for i, test := range stringTests {
|
||||
|
||||
@@ -70,6 +70,8 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value {
|
||||
// word in the passed-in argument frame.
|
||||
func makeFuncStub()
|
||||
|
||||
// This type is partially duplicated as runtime.reflectMethodValue.
|
||||
// Any changes should be reflected in both.
|
||||
type methodValue struct {
|
||||
fn uintptr
|
||||
stack *bitVector // stack bitmap for args - offset known to runtime
|
||||
|
||||
@@ -1848,6 +1848,7 @@ func ChanOf(dir ChanDir, t Type) Type {
|
||||
prototype := *(**chanType)(unsafe.Pointer(&ichan))
|
||||
ch := new(chanType)
|
||||
*ch = *prototype
|
||||
ch.tflag = 0
|
||||
ch.dir = uintptr(dir)
|
||||
ch.str = resolveReflectName(newName(s, "", "", false))
|
||||
ch.hash = fnv1(typ.hash, 'c', byte(dir))
|
||||
@@ -1892,6 +1893,7 @@ func MapOf(key, elem Type) Type {
|
||||
mt := new(mapType)
|
||||
*mt = **(**mapType)(unsafe.Pointer(&imap))
|
||||
mt.str = resolveReflectName(newName(s, "", "", false))
|
||||
mt.tflag = 0
|
||||
mt.hash = fnv1(etyp.hash, 'm', byte(ktyp.hash>>24), byte(ktyp.hash>>16), byte(ktyp.hash>>8), byte(ktyp.hash))
|
||||
mt.key = ktyp
|
||||
mt.elem = etyp
|
||||
|
||||
@@ -20,6 +20,7 @@ TEXT runtime·checkvectorfacility(SB),NOSPLIT,$32-0
|
||||
MOVD $2, R0
|
||||
MOVD R1, tmp-32(SP)
|
||||
MOVD $x-24(SP), R1
|
||||
XC $24, 0(R1), 0(R1)
|
||||
// STFLE 0(R1)
|
||||
WORD $0xB2B01000
|
||||
MOVBZ z-8(SP), R1
|
||||
|
||||
@@ -23,6 +23,7 @@ package runtime
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ucontext.h>
|
||||
#include <sys/umtx.h>
|
||||
#include <sys/_umtx.h>
|
||||
#include <sys/rtprio.h>
|
||||
#include <sys/thr.h>
|
||||
#include <sys/_sigset.h>
|
||||
@@ -49,6 +50,8 @@ const (
|
||||
SA_RESTART = C.SA_RESTART
|
||||
SA_ONSTACK = C.SA_ONSTACK
|
||||
|
||||
CLOCK_MONOTONIC = C.CLOCK_MONOTONIC
|
||||
|
||||
UMTX_OP_WAIT_UINT = C.UMTX_OP_WAIT_UINT
|
||||
UMTX_OP_WAIT_UINT_PRIVATE = C.UMTX_OP_WAIT_UINT_PRIVATE
|
||||
UMTX_OP_WAKE = C.UMTX_OP_WAKE
|
||||
@@ -130,4 +133,6 @@ type Timespec C.struct_timespec
|
||||
type Timeval C.struct_timeval
|
||||
type Itimerval C.struct_itimerval
|
||||
|
||||
type Umtx_time C.struct__umtx_time
|
||||
|
||||
type Kevent C.struct_kevent
|
||||
|
||||
@@ -24,6 +24,8 @@ const (
|
||||
_SA_RESTART = 0x2
|
||||
_SA_ONSTACK = 0x1
|
||||
|
||||
_CLOCK_MONOTONIC = 0x4
|
||||
|
||||
_UMTX_OP_WAIT_UINT = 0xb
|
||||
_UMTX_OP_WAIT_UINT_PRIVATE = 0xf
|
||||
_UMTX_OP_WAKE = 0x3
|
||||
@@ -203,6 +205,12 @@ type itimerval struct {
|
||||
it_value timeval
|
||||
}
|
||||
|
||||
type umtx_time struct {
|
||||
_timeout timespec
|
||||
_flags uint32
|
||||
_clockid uint32
|
||||
}
|
||||
|
||||
type keventt struct {
|
||||
ident uint32
|
||||
filter int16
|
||||
|
||||
@@ -24,6 +24,8 @@ const (
|
||||
_SA_RESTART = 0x2
|
||||
_SA_ONSTACK = 0x1
|
||||
|
||||
_CLOCK_MONOTONIC = 0x4
|
||||
|
||||
_UMTX_OP_WAIT_UINT = 0xb
|
||||
_UMTX_OP_WAIT_UINT_PRIVATE = 0xf
|
||||
_UMTX_OP_WAKE = 0x3
|
||||
@@ -214,6 +216,12 @@ type itimerval struct {
|
||||
it_value timeval
|
||||
}
|
||||
|
||||
type umtx_time struct {
|
||||
_timeout timespec
|
||||
_flags uint32
|
||||
_clockid uint32
|
||||
}
|
||||
|
||||
type keventt struct {
|
||||
ident uint64
|
||||
filter int16
|
||||
|
||||
@@ -24,6 +24,8 @@ const (
|
||||
_SA_RESTART = 0x2
|
||||
_SA_ONSTACK = 0x1
|
||||
|
||||
_CLOCK_MONOTONIC = 0x4
|
||||
|
||||
_UMTX_OP_WAIT_UINT = 0xb
|
||||
_UMTX_OP_WAIT_UINT_PRIVATE = 0xf
|
||||
_UMTX_OP_WAKE = 0x3
|
||||
@@ -176,6 +178,12 @@ type itimerval struct {
|
||||
it_value timeval
|
||||
}
|
||||
|
||||
type umtx_time struct {
|
||||
_timeout timespec
|
||||
_flags uint32
|
||||
_clockid uint32
|
||||
}
|
||||
|
||||
type keventt struct {
|
||||
ident uint32
|
||||
filter int16
|
||||
|
||||
@@ -1076,8 +1076,6 @@ top:
|
||||
// objects reachable from global roots since they don't have write
|
||||
// barriers. Rescan some roots and flush work caches.
|
||||
|
||||
gcMarkRootCheck()
|
||||
|
||||
// Disallow caching workbufs and indicate that we're in mark 2.
|
||||
gcBlackenPromptly = true
|
||||
|
||||
@@ -1101,6 +1099,16 @@ top:
|
||||
})
|
||||
})
|
||||
|
||||
// Check that roots are marked. We should be able to
|
||||
// do this before the forEachP, but based on issue
|
||||
// #16083 there may be a (harmless) race where we can
|
||||
// enter mark 2 while some workers are still scanning
|
||||
// stacks. The forEachP ensures these scans are done.
|
||||
//
|
||||
// TODO(austin): Figure out the race and fix this
|
||||
// properly.
|
||||
gcMarkRootCheck()
|
||||
|
||||
// Now we can start up mark 2 workers.
|
||||
atomic.Xaddint64(&gcController.dedicatedMarkWorkersNeeded, 0xffffffff)
|
||||
atomic.Xaddint64(&gcController.fractionalMarkWorkersNeeded, 0xffffffff)
|
||||
|
||||
@@ -106,26 +106,32 @@ func gcMarkRootCheck() {
|
||||
|
||||
lock(&allglock)
|
||||
// Check that stacks have been scanned.
|
||||
var gp *g
|
||||
if gcphase == _GCmarktermination {
|
||||
for i := 0; i < len(allgs); i++ {
|
||||
gp := allgs[i]
|
||||
gp = allgs[i]
|
||||
if !(gp.gcscandone && gp.gcscanvalid) && readgstatus(gp) != _Gdead {
|
||||
println("gp", gp, "goid", gp.goid,
|
||||
"status", readgstatus(gp),
|
||||
"gcscandone", gp.gcscandone,
|
||||
"gcscanvalid", gp.gcscanvalid)
|
||||
throw("scan missed a g")
|
||||
goto fail
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < work.nStackRoots; i++ {
|
||||
gp := allgs[i]
|
||||
gp = allgs[i]
|
||||
if !gp.gcscandone {
|
||||
throw("scan missed a g")
|
||||
goto fail
|
||||
}
|
||||
}
|
||||
}
|
||||
unlock(&allglock)
|
||||
return
|
||||
|
||||
fail:
|
||||
println("gp", gp, "goid", gp.goid,
|
||||
"status", readgstatus(gp),
|
||||
"gcscandone", gp.gcscandone,
|
||||
"gcscanvalid", gp.gcscanvalid)
|
||||
unlock(&allglock) // Avoid self-deadlock with traceback.
|
||||
throw("scan missed a g")
|
||||
}
|
||||
|
||||
// ptrmask for an allocation containing a single pointer.
|
||||
|
||||
@@ -917,7 +917,9 @@ func scavengelist(list *mSpanList, now, limit uint64) uintptr {
|
||||
// more memory than we want.)
|
||||
start = (start + sys.PhysPageSize - 1) &^ (sys.PhysPageSize - 1)
|
||||
end &^= sys.PhysPageSize - 1
|
||||
if start == end {
|
||||
if end <= start {
|
||||
// start and end don't span a
|
||||
// whole physical page.
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ func raise(sig int32)
|
||||
func raiseproc(sig int32)
|
||||
|
||||
//go:noescape
|
||||
func sys_umtx_op(addr *uint32, mode int32, val uint32, ptr2, ts *timespec) int32
|
||||
func sys_umtx_op(addr *uint32, mode int32, val uint32, uaddr1 uintptr, ut *umtx_time) int32
|
||||
|
||||
func osyield()
|
||||
|
||||
@@ -70,14 +70,14 @@ func futexsleep(addr *uint32, val uint32, ns int64) {
|
||||
}
|
||||
|
||||
func futexsleep1(addr *uint32, val uint32, ns int64) {
|
||||
var tsp *timespec
|
||||
var utp *umtx_time
|
||||
if ns >= 0 {
|
||||
var ts timespec
|
||||
ts.tv_nsec = 0
|
||||
ts.set_sec(int64(timediv(ns, 1000000000, (*int32)(unsafe.Pointer(&ts.tv_nsec)))))
|
||||
tsp = &ts
|
||||
var ut umtx_time
|
||||
ut._clockid = _CLOCK_MONOTONIC
|
||||
ut._timeout.set_sec(int64(timediv(ns, 1000000000, (*int32)(unsafe.Pointer(&ut._timeout.tv_nsec)))))
|
||||
utp = &ut
|
||||
}
|
||||
ret := sys_umtx_op(addr, _UMTX_OP_WAIT_UINT_PRIVATE, val, nil, tsp)
|
||||
ret := sys_umtx_op(addr, _UMTX_OP_WAIT_UINT_PRIVATE, val, unsafe.Sizeof(*utp), utp)
|
||||
if ret >= 0 || ret == -_EINTR {
|
||||
return
|
||||
}
|
||||
@@ -87,7 +87,7 @@ func futexsleep1(addr *uint32, val uint32, ns int64) {
|
||||
|
||||
//go:nosplit
|
||||
func futexwakeup(addr *uint32, cnt uint32) {
|
||||
ret := sys_umtx_op(addr, _UMTX_OP_WAKE_PRIVATE, cnt, nil, nil)
|
||||
ret := sys_umtx_op(addr, _UMTX_OP_WAKE_PRIVATE, cnt, 0, nil)
|
||||
if ret >= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -422,8 +422,62 @@ loop:
|
||||
gp.param = nil
|
||||
gopark(selparkcommit, nil, "select", traceEvGoBlockSelect, 2)
|
||||
|
||||
// someone woke us up
|
||||
sellock(scases, lockorder)
|
||||
// While we were asleep, some goroutine came along and completed
|
||||
// one of the cases in the select and woke us up (called ready).
|
||||
// As part of that process, the goroutine did a cas on done above
|
||||
// (aka *sg.selectdone for all queued sg) to win the right to
|
||||
// complete the select. Now done = 1.
|
||||
//
|
||||
// If we copy (grow) our own stack, we will update the
|
||||
// selectdone pointers inside the gp.waiting sudog list to point
|
||||
// at the new stack. Another goroutine attempting to
|
||||
// complete one of our (still linked in) select cases might
|
||||
// see the new selectdone pointer (pointing at the new stack)
|
||||
// before the new stack has real data; if the new stack has done = 0
|
||||
// (before the old values are copied over), the goroutine might
|
||||
// do a cas via sg.selectdone and incorrectly believe that it has
|
||||
// won the right to complete the select, executing a second
|
||||
// communication and attempting to wake us (call ready) again.
|
||||
//
|
||||
// Then things break.
|
||||
//
|
||||
// The best break is that the goroutine doing ready sees the
|
||||
// _Gcopystack status and throws, as in #17007.
|
||||
// A worse break would be for us to continue on, start running real code,
|
||||
// block in a semaphore acquisition (sema.go), and have the other
|
||||
// goroutine wake us up without having really acquired the semaphore.
|
||||
// That would result in the goroutine spuriously running and then
|
||||
// queue up another spurious wakeup when the semaphore really is ready.
|
||||
// In general the situation can cascade until something notices the
|
||||
// problem and causes a crash.
|
||||
//
|
||||
// A stack shrink does not have this problem, because it locks
|
||||
// all the channels that are involved first, blocking out the
|
||||
// possibility of a cas on selectdone.
|
||||
//
|
||||
// A stack growth before gopark above does not have this
|
||||
// problem, because we hold those channel locks (released by
|
||||
// selparkcommit).
|
||||
//
|
||||
// A stack growth after sellock below does not have this
|
||||
// problem, because again we hold those channel locks.
|
||||
//
|
||||
// The only problem is a stack growth during sellock.
|
||||
// To keep that from happening, run sellock on the system stack.
|
||||
//
|
||||
// It might be that we could avoid this if copystack copied the
|
||||
// stack before calling adjustsudogs. In that case,
|
||||
// syncadjustsudogs would need to recopy the tiny part that
|
||||
// it copies today, resulting in a little bit of extra copying.
|
||||
//
|
||||
// An even better fix, not for the week before a release candidate,
|
||||
// would be to put space in every sudog and make selectdone
|
||||
// point at (say) the space in the first sudog.
|
||||
|
||||
systemstack(func() {
|
||||
sellock(scases, lockorder)
|
||||
})
|
||||
|
||||
sg = (*sudog)(gp.param)
|
||||
gp.param = nil
|
||||
|
||||
@@ -464,8 +518,15 @@ loop:
|
||||
}
|
||||
|
||||
if cas == nil {
|
||||
// This can happen if we were woken up by a close().
|
||||
// TODO: figure that out explicitly so we don't need this loop.
|
||||
// We can wake up with gp.param == nil (so cas == nil)
|
||||
// when a channel involved in the select has been closed.
|
||||
// It is easiest to loop and re-run the operation;
|
||||
// we'll see that it's now closed.
|
||||
// Maybe some day we can signal the close explicitly,
|
||||
// but we'd have to distinguish close-on-reader from close-on-writer.
|
||||
// It's easiest not to duplicate the code and just recheck above.
|
||||
// We know that something closed, and things never un-close,
|
||||
// so we won't block again.
|
||||
goto loop
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ TEXT runtime·sys_umtx_op(SB),NOSPLIT,$0
|
||||
MOVQ addr+0(FP), DI
|
||||
MOVL mode+8(FP), SI
|
||||
MOVL val+12(FP), DX
|
||||
MOVQ ptr2+16(FP), R10
|
||||
MOVQ ts+24(FP), R8
|
||||
MOVQ uaddr1+16(FP), R10
|
||||
MOVQ ut+24(FP), R8
|
||||
MOVL $454, AX
|
||||
SYSCALL
|
||||
MOVL AX, ret+32(FP)
|
||||
|
||||
@@ -45,7 +45,7 @@ TEXT runtime·sys_umtx_op(SB),NOSPLIT,$0
|
||||
MOVW addr+0(FP), R0
|
||||
MOVW mode+4(FP), R1
|
||||
MOVW val+8(FP), R2
|
||||
MOVW ptr2+12(FP), R3
|
||||
MOVW uaddr1+12(FP), R3
|
||||
ADD $20, R13 // arg 5 is passed on stack
|
||||
MOVW $SYS__umtx_op, R7
|
||||
SWI $0
|
||||
|
||||
@@ -107,7 +107,7 @@ func tracebackdefers(gp *g, callback func(*stkframe, unsafe.Pointer) bool, v uns
|
||||
}
|
||||
frame.fn = f
|
||||
frame.argp = uintptr(deferArgs(d))
|
||||
frame.arglen, frame.argmap = getArgInfo(&frame, f, true)
|
||||
frame.arglen, frame.argmap = getArgInfo(&frame, f, true, fn)
|
||||
}
|
||||
frame.continpc = frame.pc
|
||||
if !callback((*stkframe)(noescape(unsafe.Pointer(&frame))), v) {
|
||||
@@ -339,7 +339,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
|
||||
// metadata recorded by f's caller.
|
||||
if callback != nil || printing {
|
||||
frame.argp = frame.fp + sys.MinFrameSize
|
||||
frame.arglen, frame.argmap = getArgInfo(&frame, f, callback != nil)
|
||||
frame.arglen, frame.argmap = getArgInfo(&frame, f, callback != nil, nil)
|
||||
}
|
||||
|
||||
// Determine frame's 'continuation PC', where it can continue.
|
||||
@@ -546,19 +546,48 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
|
||||
return n
|
||||
}
|
||||
|
||||
func getArgInfo(frame *stkframe, f *_func, needArgMap bool) (arglen uintptr, argmap *bitvector) {
|
||||
// reflectMethodValue is a partial duplicate of reflect.methodValue.
|
||||
type reflectMethodValue struct {
|
||||
fn uintptr
|
||||
stack *bitvector // args bitmap
|
||||
}
|
||||
|
||||
// getArgInfo returns the argument frame information for a call to f
|
||||
// with call frame frame.
|
||||
//
|
||||
// This is used for both actual calls with active stack frames and for
|
||||
// deferred calls that are not yet executing. If this is an actual
|
||||
// call, ctxt must be nil (getArgInfo will retrieve what it needs from
|
||||
// the active stack frame). If this is a deferred call, ctxt must be
|
||||
// the function object that was deferred.
|
||||
func getArgInfo(frame *stkframe, f *_func, needArgMap bool, ctxt *funcval) (arglen uintptr, argmap *bitvector) {
|
||||
arglen = uintptr(f.args)
|
||||
if needArgMap && f.args == _ArgsSizeUnknown {
|
||||
// Extract argument bitmaps for reflect stubs from the calls they made to reflect.
|
||||
switch funcname(f) {
|
||||
case "reflect.makeFuncStub", "reflect.methodValueCall":
|
||||
arg0 := frame.sp + sys.MinFrameSize
|
||||
fn := *(**[2]uintptr)(unsafe.Pointer(arg0))
|
||||
if fn[0] != f.entry {
|
||||
// These take a *reflect.methodValue as their
|
||||
// context register.
|
||||
var mv *reflectMethodValue
|
||||
if ctxt != nil {
|
||||
// This is not an actual call, but a
|
||||
// deferred call. The function value
|
||||
// is itself the *reflect.methodValue.
|
||||
mv = (*reflectMethodValue)(unsafe.Pointer(ctxt))
|
||||
} else {
|
||||
// This is a real call that took the
|
||||
// *reflect.methodValue as its context
|
||||
// register and immediately saved it
|
||||
// to 0(SP). Get the methodValue from
|
||||
// 0(SP).
|
||||
arg0 := frame.sp + sys.MinFrameSize
|
||||
mv = *(**reflectMethodValue)(unsafe.Pointer(arg0))
|
||||
}
|
||||
if mv.fn != f.entry {
|
||||
print("runtime: confused by ", funcname(f), "\n")
|
||||
throw("reflect mismatch")
|
||||
}
|
||||
bv := (*bitvector)(unsafe.Pointer(fn[1]))
|
||||
bv := mv.stack
|
||||
arglen = uintptr(bv.n * sys.PtrSize)
|
||||
argmap = bv
|
||||
}
|
||||
|
||||
@@ -7,28 +7,45 @@
|
||||
package syscall_test
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const MNT_WAIT = 1
|
||||
const MNT_NOWAIT = 2
|
||||
|
||||
func TestGetfsstat(t *testing.T) {
|
||||
n, err := syscall.Getfsstat(nil, MNT_WAIT)
|
||||
const flags = MNT_NOWAIT // see Issue 16937
|
||||
n, err := syscall.Getfsstat(nil, flags)
|
||||
t.Logf("Getfsstat(nil, %d) = (%v, %v)", flags, n, err)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
data := make([]syscall.Statfs_t, n)
|
||||
n, err = syscall.Getfsstat(data, MNT_WAIT)
|
||||
n2, err := syscall.Getfsstat(data, flags)
|
||||
t.Logf("Getfsstat([]syscall.Statfs_t, %d) = (%v, %v)", flags, n2, err)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
empty := syscall.Statfs_t{}
|
||||
for _, stat := range data {
|
||||
if stat == empty {
|
||||
t.Fatal("an empty Statfs_t struct was returned")
|
||||
if n != n2 {
|
||||
t.Errorf("Getfsstat(nil) = %d, but subsequent Getfsstat(slice) = %d", n, n2)
|
||||
}
|
||||
for i, stat := range data {
|
||||
if stat == (syscall.Statfs_t{}) {
|
||||
t.Errorf("index %v is an empty Statfs_t struct", i)
|
||||
}
|
||||
}
|
||||
if t.Failed() {
|
||||
for i, stat := range data[:n2] {
|
||||
t.Logf("data[%v] = %+v", i, stat)
|
||||
}
|
||||
mount, err := exec.Command("mount").CombinedOutput()
|
||||
if err != nil {
|
||||
t.Logf("mount: %v\n%s", err, mount)
|
||||
} else {
|
||||
t.Logf("mount: %s", mount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -939,8 +939,11 @@ func TestLoadFixed(t *testing.T) {
|
||||
// but Go and most other systems use "east is positive".
|
||||
// So GMT+1 corresponds to -3600 in the Go zone, not +3600.
|
||||
name, offset := Now().In(loc).Zone()
|
||||
if name != "GMT+1" || offset != -1*60*60 {
|
||||
t.Errorf("Now().In(loc).Zone() = %q, %d, want %q, %d", name, offset, "GMT+1", -1*60*60)
|
||||
// The zone abbreviation is "-01" since tzdata-2016g, and "GMT+1"
|
||||
// on earlier versions; we accept both. (Issue #17276).
|
||||
if !(name == "GMT+1" || name == "-01") || offset != -1*60*60 {
|
||||
t.Errorf("Now().In(loc).Zone() = %q, %d, want %q or %q, %d",
|
||||
name, offset, "GMT+1", "-01", -1*60*60)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
48
test/fixedbugs/issue16331.go
Normal file
48
test/fixedbugs/issue16331.go
Normal file
@@ -0,0 +1,48 @@
|
||||
// run
|
||||
|
||||
// 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.
|
||||
|
||||
// Perform tracebackdefers with a deferred reflection method.
|
||||
|
||||
package main
|
||||
|
||||
import "reflect"
|
||||
|
||||
type T struct{}
|
||||
|
||||
func (T) M() {
|
||||
}
|
||||
|
||||
func F(args []reflect.Value) (results []reflect.Value) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
// Test reflect.makeFuncStub.
|
||||
t := reflect.TypeOf((func())(nil))
|
||||
f := reflect.MakeFunc(t, F).Interface().(func())
|
||||
defer f()
|
||||
growstack(10000)
|
||||
done <- true
|
||||
}()
|
||||
<-done
|
||||
go func() {
|
||||
// Test reflect.methodValueCall.
|
||||
f := reflect.ValueOf(T{}).Method(0).Interface().(func())
|
||||
defer f()
|
||||
growstack(10000)
|
||||
done <- true
|
||||
}()
|
||||
<-done
|
||||
}
|
||||
|
||||
func growstack(x int) {
|
||||
if x == 0 {
|
||||
return
|
||||
}
|
||||
growstack(x - 1)
|
||||
}
|
||||
47
test/fixedbugs/issue17318.go
Normal file
47
test/fixedbugs/issue17318.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// errorcheck -0 -N -m -l
|
||||
|
||||
// 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.
|
||||
|
||||
// The escape analyzer needs to run till its root set settles
|
||||
// (this is not that often, it turns out).
|
||||
// This test is likely to become stale because the leak depends
|
||||
// on a spurious-escape bug -- return an interface as a named
|
||||
// output parameter appears to cause the called closure to escape,
|
||||
// where returning it as a regular type does not.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type closure func(i, j int) ent
|
||||
|
||||
type ent int
|
||||
|
||||
func (e ent) String() string {
|
||||
return fmt.Sprintf("%d", int(e)) // ERROR "ent.String ... argument does not escape$" "int\(e\) escapes to heap$"
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func foo(ops closure, j int) (err fmt.Stringer) { // ERROR "leaking param: ops$" "leaking param: ops to result err level=0$"
|
||||
enqueue := func(i int) fmt.Stringer { // ERROR "func literal escapes to heap$"
|
||||
return ops(i, j) // ERROR "ops\(i, j\) escapes to heap$"
|
||||
}
|
||||
err = enqueue(4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return // return result of enqueue, a fmt.Stringer
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 3 identical functions, to get different escape behavior.
|
||||
f := func(i, j int) ent { // ERROR "func literal escapes to heap$"
|
||||
return ent(i + j)
|
||||
}
|
||||
i := foo(f, 3).(ent)
|
||||
fmt.Printf("foo(f,3)=%d\n", int(i)) // ERROR "int\(i\) escapes to heap$" "main ... argument does not escape$"
|
||||
}
|
||||
40
test/fixedbugs/issue18410.go
Normal file
40
test/fixedbugs/issue18410.go
Normal file
@@ -0,0 +1,40 @@
|
||||
// run
|
||||
|
||||
// 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 checks partially initialized structure literals
|
||||
// used to create value.method functions have their
|
||||
// non-initialized fields properly zeroed/nil'd
|
||||
|
||||
package main
|
||||
|
||||
type X struct {
|
||||
A, B, C *int
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func (t X) Print() {
|
||||
if t.B != nil {
|
||||
panic("t.B must be nil")
|
||||
}
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func caller(f func()) {
|
||||
f()
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func test() {
|
||||
var i, j int
|
||||
x := X{A: &i, C: &j}
|
||||
caller(func() { X{A: &i, C: &j}.Print() })
|
||||
caller(X{A: &i, C: &j}.Print)
|
||||
caller(x.Print)
|
||||
}
|
||||
|
||||
func main() {
|
||||
test()
|
||||
}
|
||||
Reference in New Issue
Block a user