diff options
author | Filippo Valsorda <filippo@golang.org> | 2020-08-17 18:12:19 -0400 |
---|---|---|
committer | Filippo Valsorda <filippo@golang.org> | 2020-08-17 18:12:19 -0400 |
commit | a15df605fc4aa6347494fd55a5c4c6544b0eb981 (patch) | |
tree | 978711ebd88092dbdc5c4b34d9f13b15f49580fa | |
parent | 6e6e0b73d6e19872946e08b65c7cac3b0289e2f0 (diff) | |
parent | 0fdc3801bfd43d6f55e4ea5bf095e1ea55430339 (diff) | |
download | go-a15df605fc4aa6347494fd55a5c4c6544b0eb981.tar.gz go-a15df605fc4aa6347494fd55a5c4c6544b0eb981.zip |
[dev.boringcrypto.go1.15] all: merge go1.15 into dev.boringcrypto.go1.15
Change-Id: I6eecfb4730f88ea8546ec88afdffe33f45dedac9
94 files changed, 4044 insertions, 1069 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 798002cfb5..cebc92f53f 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -31,6 +31,7 @@ Aaron Cannon <cannona@fireantproductions.com> Aaron France <aaron.l.france@gmail.com> Aaron Jacobs <jacobsa@google.com> Aaron Kemp <kemp.aaron@gmail.com> +Aaron Patterson <tenderlove@ruby-lang.org> Aaron Stein <aaronstein12@gmail.com> Aaron Torres <tcboox@gmail.com> Aaron Zinman <aaron@azinman.com> @@ -58,6 +59,7 @@ Adrian Hesketh <adrianhesketh@hushmail.com> Adrian Nos <nos.adrian@gmail.com> Adrian O'Grady <elpollouk@gmail.com> Adrien Bustany <adrien-xx-google@bustany.org> +Adrien Delorme <adrien.delorme@icloud.com> Adrien Petel <peteladrien@gmail.com> Aécio Júnior <aeciodantasjunior@gmail.com> Aeneas Rekkas (arekkas) <aeneas@ory.am> @@ -114,6 +116,7 @@ Alex Zhirov <azhirov@google.com> Alexander Demakin <alexander.demakin@gmail.com> Alexander Döring <email@alexd.ch> Alexander F Rødseth <alexander.rodseth@appeartv.com> +Alexander Greim <alexxx@iltempo.de> Alexander Guz <kalimatas@gmail.com> Alexander Kauer <alexander@affine.space> Alexander Kucherenko <alxkchr@gmail.com> @@ -122,6 +125,7 @@ Alexander Lourier <aml@rulezz.ru> Alexander Menzhinsky <amenzhinsky@gmail.com> Alexander Morozov <lk4d4math@gmail.com> Alexander Neumann <alexander@bumpern.de> +Alexander Nohe <alex.nohe427@gmail.com> Alexander Orlov <alexander.orlov@loxal.net> Alexander Pantyukhin <apantykhin@gmail.com> Alexander Polcyn <apolcyn@google.com> @@ -149,6 +153,7 @@ Alexey Semenyuk <alexsemenyuk88@gmail.com> Alexis Hildebrandt <surryhill@gmail.com> Alexis Hunt <lexer@google.com> Alexis Imperial-Legrand <ail@google.com> +Ali Farooq <ali.farooq0@pm.me> Ali Rizvi-Santiago <arizvisa@gmail.com> Aliaksandr Valialkin <valyala@gmail.com> Alif Rachmawadi <subosito@gmail.com> @@ -156,14 +161,17 @@ Allan Simon <allan.simon@supinfo.com> Allen Li <ayatane@google.com> Alok Menghrajani <alok.menghrajani@gmail.com> Aman Gupta <aman@tmm1.net> +Amarjeet Anand <amarjeetanandsingh@gmail.com> Amir Mohammad Saied <amir@gluegadget.com> Amr Mohammed <merodiro@gmail.com> Amrut Joshi <amrut.joshi@gmail.com> +An Long <aisk1988@gmail.com> An Xiao <hac@zju.edu.cn> Anand K. Mistry <anand@mistry.ninja> Anders Pearson <anders@columbia.edu> Anderson Queiroz <contato@andersonq.eti.br> André Carvalho <asantostc@gmail.com> +André Martins <aanm90@gmail.com> Andre Nathan <andrenth@gmail.com> Andrea Nodari <andrea.nodari91@gmail.com> Andrea Spadaccini <spadaccio@google.com> @@ -187,9 +195,11 @@ Andrew Braunstein <awbraunstein@gmail.com> Andrew Bursavich <abursavich@gmail.com> Andrew Ekstedt <andrew.ekstedt@gmail.com> Andrew Etter <andrew.etter@gmail.com> +Andrew G. Morgan <agm@google.com> Andrew Gerrand <adg@golang.org> Andrew Harding <andrew@spacemonkey.com> Andrew Jackura <ajackura@google.com> +Andrew Louis <alouis@digitalocean.com> Andrew Lutomirski <andy@luto.us> Andrew Medvedev <andrew.y.medvedev@gmail.com> Andrew Pilloud <andrewpilloud@igneoussystems.com> @@ -219,6 +229,7 @@ Andy Lindeman <andy@lindeman.io> Andy Maloney <asmaloney@gmail.com> Andy Pan <panjf2000@gmail.com> Andy Walker <walkeraj@gmail.com> +Andy Wang <cbeuw.andy@gmail.com> Andzej Maciusovic <andzej.maciusovic@gmail.com> Anfernee Yongkun Gui <anfernee.gui@gmail.com> Angelo Bulfone <mbulfone@gmail.com> @@ -226,6 +237,7 @@ Anh Hai Trinh <anh.hai.trinh@gmail.com> Anit Gandhi <anitgandhi@gmail.com> Ankit Goyal <ankit3goyal@gmail.com> Anmol Sethi <anmol@aubble.com> +Annirudh Prasad <annirudh@wandb.com> Anschel Schaffer-Cohen <anschelsc@gmail.com> Anthony Alves <cvballa3g0@gmail.com> Anthony Canino <anthony.canino1@gmail.com> @@ -239,15 +251,18 @@ Anthony Woods <awoods@raintank.io> Antoine GIRARD <sapk@sapk.fr> Antoine Martin <antoine97.martin@gmail.com> Anton Gyllenberg <anton@iki.fi> +Anton Kuklin <anton.a.kuklin@gmail.com> Antonin Amand <antonin.amand@gmail.com> Antonio Antelo <aantelov87@gmail.com> Antonio Bibiano <antbbn@gmail.com> Antonio Huete Jimenez <tuxillo@quantumachine.net> Antonio Murdaca <runcom@redhat.com> Antonio Troina <thoeni@gmail.com> +Anze Kolar <me@akolar.com> Aofei Sheng <aofei@aofeisheng.com> Apisak Darakananda <pongad@gmail.com> Aram Hăvărneanu <aram@mgk.ro> +Araragi Hokuto <kanseihonbucho@protonmail.com> Arash Bina <arash@arash.io> Arda Güçlü <ardaguclu@gmail.com> Areski Belaid <areski@gmail.com> @@ -273,6 +288,7 @@ Audrius Butkevicius <audrius.butkevicius@gmail.com> Augusto Roman <aroman@gmail.com> Aulus Egnatius Varialus <varialus@gmail.com> Aurélien Rainone <aurelien.rainone@gmail.com> +Aurélio A. Heckert <aurium@gmail.com> Austin Clements <austin@google.com> <aclements@csail.mit.edu> Avi Flax <avi@timehop.com> awaw fumin <awawfumin@gmail.com> @@ -315,6 +331,7 @@ Benoit Sigoure <tsunanet@gmail.com> Berengar Lehr <Berengar.Lehr@gmx.de> Berkant Ipek <41230766+0xbkt@users.noreply.github.com> Bharath Thiruveedula <tbharath91@gmail.com> +Bhavin Gandhi <bhavin7392@gmail.com> Bill Neubauer <wcn@golang.org> <wcn@google.com> <bill.neubauer@gmail.com> Bill O'Farrell <billo@ca.ibm.com> Bill Prin <waprin@google.com> @@ -322,6 +339,7 @@ Bill Thiede <couchmoney@gmail.com> Bill Zissimopoulos <billziss@navimatics.com> Billie Harold Cleek <bhcleek@gmail.com> Billy Lynch <wlynch@google.com> +Billy Zaelani Malik <m.billyzaelani@gmail.com> Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> Bjorn Tillenius <bjorn@tillenius.me> Bjorn Tipling <bjorn.tipling@gmail.com> @@ -331,12 +349,15 @@ Blake Mesdag <blakemesdag@gmail.com> Blake Mizerany <blake.mizerany@gmail.com> Blixt <me@blixt.nyc> Bob Briski <rbriski@gmail.com> +Bob McNaughton <bobmcn@gmail.com> Bob Potter <bobby.potter@gmail.com> Bobby DeSimone <bobbydesimone@gmail.com> Bobby Powers <bobbypowers@gmail.com> +Boqin Qin <bobbqqin@gmail.com> Boris Nagaev <nagaev@google.com> Borja Clemente <borja.clemente@gmail.com> Brad Burch <brad.burch@gmail.com> +Brad Erickson <bderickson@gmail.com> Brad Fitzpatrick <bradfitz@golang.org> <bradfitz@gmail.com> Brad Garcia <bgarcia@golang.org> Brad Jones <rbjones@google.com> @@ -351,6 +372,7 @@ Brandon Bennett <bbennett@fb.com> Brandon Gilmore <varz@google.com> Brandon Philips <brandon@ifup.org> Brandon Ryan <bjryan19@gmail.com> +Brayden Cloud <bcloud@google.com> Brendan Daniel Tracey <tracey.brendan@gmail.com> Brendan O'Dea <bod@golang.org> Brett Cannon <bcannon@gmail.com> @@ -390,6 +412,7 @@ Carlos Castillo <cookieo9@gmail.com> Carlos Cirello <uldericofilho@gmail.com> Carlos Eduardo <me@carlosedp.com> Carlos Eduardo Seo <cseo@linux.vnet.ibm.com> +Carlos Iriarte <ciriarte@gmail.com> Carlos Souza <carloshrsouza@gmail.com> Carolyn Van Slyck <me@carolynvanslyck.com> Carrie Bynon <cbynon@gmail.com> @@ -405,6 +428,7 @@ Chad Rosier <mrosier.qdt@qualcommdatacenter.com> ChaiShushan <chaishushan@gmail.com> Changkun Ou <hi@changkun.us> Channing Kimble-Brown <channing@golang.org> +Chao Xu <xuchao@google.com> Charles Fenwick Elliott <Charles@FenwickElliott.io> Charles Kenney <charlesc.kenney@gmail.com> Charles L. Dorian <cldorian@gmail.com> @@ -426,6 +450,7 @@ Chris Howey <howeyc@gmail.com> Chris Hundt <hundt@google.com> Chris Jones <chris@cjones.org> <chris.jones.yar@gmail.com> Chris Kastorff <encryptio@gmail.com> +Chris Le Roy <brompwnie@users.noreply.github.com> Chris Lennert <calennert@gmail.com> Chris Liles <caveryliles@gmail.com> Chris Manghane <cmang@golang.org> @@ -475,6 +500,7 @@ Conrad Meyer <cemeyer@cs.washington.edu> Conrado Gouvea <conradoplg@gmail.com> Constantin Konstantinidis <constantinkonstantinidis@gmail.com> Corey Thomasson <cthom.lists@gmail.com> +Corne van der Plas <vdplas@gmail.com> Cosmos Nicolaou <cnicolaou@google.com> Costin Chirvasuta <ctin@google.com> Craig Citro <craigcitro@google.com> @@ -506,9 +532,11 @@ Daniel Ingram <ingramds@appstate.edu> Daniel Johansson <dajo2002@gmail.com> Daniel Kerwin <d.kerwin@gini.net> Daniel Krech <eikeon@eikeon.com> +Daniel Kumor <rdkumor@gmail.com> Daniel Langner <s8572327@gmail.com> Daniel Lidén <daniel.liden.87@gmail.com> Daniel Lublin <daniel@lublin.se> +Daniel Mangum <georgedanielmangum@gmail.com> Daniel Martí <mvdan@mvdan.cc> Daniel Morsing <daniel.morsing@gmail.com> Daniel Nadasi <dnadasi@google.com> @@ -519,6 +547,8 @@ Daniel Speichert <daniel@speichert.pl> Daniel Theophanes <kardianos@gmail.com> Daniel Upton <daniel@floppy.co> Daniela Petruzalek <daniela.petruzalek@gmail.com> +Danish Dua <danishdua@google.com> +Danish Prakash <grafitykoncept@gmail.com> Danny Rosseau <daniel.rosseau@gmail.com> Daria Kolistratova <daria.kolistratova@intel.com> Darien Raymond <admin@v2ray.com> @@ -542,6 +572,7 @@ David Brophy <dave@brophy.uk> David Bürgin <676c7473@gmail.com> David Calavera <david.calavera@gmail.com> David Carlier <devnexen@gmail.com> +David Carter <fresco.raja@gmail.com> David Chase <drchase@google.com> David Covert <davidhcovert@gmail.com> David Crawshaw <david.crawshaw@zentus.com> <crawshaw@google.com> <crawshaw@golang.org> @@ -550,6 +581,7 @@ David Finkel <david.finkel@gmail.com> David Forsythe <dforsythe@gmail.com> David G. Andersen <dave.andersen@gmail.com> David Glasser <glasser@meteor.com> +David Golden <david@autopragmatic.com> David Heuschmann <heuschmann.d@gmail.com> David Howden <dhowden@gmail.com> David Hubbard <dsp@google.com> @@ -574,6 +606,7 @@ David Volquartz Lebech <david@lebech.info> David Wimmer <davidlwimmer@gmail.com> Davies Liu <davies.liu@gmail.com> Davor Kapsa <davor.kapsa@gmail.com> +Dean Eigenmann <7621705+decanus@users.noreply.github.com> Dean Prichard <dean.prichard@gmail.com> Deepak Jois <deepak.jois@gmail.com> Denis Bernard <db047h@gmail.com> @@ -619,6 +652,7 @@ Dmitry Mottl <dmitry.mottl@gmail.com> Dmitry Neverov <dmitry.neverov@gmail.com> Dmitry Savintsev <dsavints@gmail.com> Dmitry Yakunin <nonamezeil@gmail.com> +Doga Fincan <doga@icloud.com> Domas Tamašauskas <puerdomus@gmail.com> Domen Ipavec <domen@ipavec.net> Dominic Green <dominicgreen1@gmail.com> @@ -642,6 +676,7 @@ Dustin Sallings <dsallings@gmail.com> Dustin Shields-Cloues <dcloues@gmail.com> Dvir Volk <dvir@everything.me> <dvirsky@gmail.com> Dylan Waits <dylan@waits.io> +Ed Schouten <ed@nuxi.nl> Edan Bedrik <3d4nb3@gmail.com> Eddie Scholtz <escholtz@google.com> Eden Li <eden.li@gmail.com> @@ -659,11 +694,13 @@ Elena Grahovac <elena@grahovac.me> Eli Bendersky <eliben@google.com> Elias Naur <mail@eliasnaur.com> <elias.naur@gmail.com> Elliot Morrison-Reed <elliotmr@gmail.com> +Ellison Leão <ellisonleao@gmail.com> Emerson Lin <linyintor@gmail.com> Emil Hessman <emil@hessman.se> Emil Mursalimov <mursalimovemeel@gmail.com> Emilien Kenler <hello@emilienkenler.com> Emmanuel Odeke <emm.odeke@gmail.com> <odeke@ualberta.ca> +Emrecan Bati <emrecanbati@gmail.com> Eno Compton <enocom@google.com> Eoghan Sherry <ejsherry@gmail.com> Eric Biggers <ebiggers@google.com> @@ -682,6 +719,7 @@ Eric Rescorla <ekr@rtfm.com> Eric Roshan-Eisner <eric.d.eisner@gmail.com> Eric Rutherford <erutherford@gmail.com> Eric Rykwalder <e.rykwalder@gmail.com> +Erick Tryzelaar <etryzelaar@google.com> Erik Aigner <aigner.erik@gmail.com> Erik Dubbelboer <erik@dubbelboer.com> Erik St. Martin <alakriti@gmail.com> @@ -694,6 +732,7 @@ Esko Luontola <esko.luontola@gmail.com> Ethan Burns <eaburns@google.com> Ethan Miller <eamiller@us.ibm.com> Euan Kemp <euank@euank.com> +Eugene Formanenko <mo4islona@gmail.com> Eugene Kalinin <e.v.kalinin@gmail.com> Evan Broder <evan@stripe.com> Evan Brown <evanbrown@google.com> @@ -705,6 +744,7 @@ Evan Kroske <evankroske@google.com> Evan Martin <evan.martin@gmail.com> Evan Phoenix <evan@phx.io> Evan Shaw <chickencha@gmail.com> +Evgeniy Kulikov <tuxuls@gmail.com> Evgeniy Polyakov <zbr@ioremap.net> Ewan Chou <coocood@gmail.com> Ewan Valentine <ewan.valentine89@gmail.com> @@ -725,8 +765,10 @@ Fedor Indutny <fedor@indutny.com> Fedor Korotkiy <dartslon@gmail.com> Felipe Oliveira <felipeweb.programador@gmail.com> Felix Bünemann <Felix.Buenemann@gmail.com> +Felix Cornelius <9767036+fcornelius@users.noreply.github.com> Felix Geisendörfer <haimuiba@gmail.com> Felix Kollmann <fk@konsorten.de> +Ferenc Szabo <frncmx@gmail.com> Filip Gruszczyński <gruszczy@gmail.com> Filip Haglund <drathier@users.noreply.github.com> Filip Stanis <fstanis@google.com> @@ -774,6 +816,7 @@ Gary Elliott <garyelliott@google.com> Gaurish Sharma <contact@gaurishsharma.com> Gautham Thambidorai <gautham.dorai@gmail.com> Gauthier Jolly <gauthier.jolly@gmail.com> +Gawen Arab <gawen.arab@c.zen.ly> Geert-Johan Riemer <gjr19912@gmail.com> Genevieve Luyt <genevieve.luyt@gmail.com> Gengliang Wang <ltnwgl@gmail.com> @@ -795,6 +838,7 @@ Gianguido Sora` <g.sora4@gmail.com> Gideon Jan-Wessel Redelinghuys <gjredelinghuys@gmail.com> Giles Lean <giles.lean@pobox.com> Giovanni Bajo <rasky@develer.com> +GitHub User @aca (50316549) <acadx0@gmail.com> GitHub User @ajnirp (1688456) <ajnirp@users.noreply.github.com> GitHub User @ajz01 (4744634) <ajzdenek@gmail.com> GitHub User @alkesh26 (1019076) <alkesh26@gmail.com> @@ -805,12 +849,18 @@ GitHub User @bakape (7851952) <bakape@gmail.com> GitHub User @bgadrian (830001) <aditza8@gmail.com> GitHub User @bontequero (2674999) <bontequero@gmail.com> GitHub User @cch123 (384546) <buaa.cch@gmail.com> +GitHub User @chainhelen (7046329) <chainhelen@gmail.com> GitHub User @chanxuehong (3416908) <chanxuehong@gmail.com> +GitHub User @cncal (23520240) <flycalvin@qq.com> GitHub User @DQNEO (188741) <dqneoo@gmail.com> +GitHub User @Dreamacro (8615343) <chuainian@gmail.com> GitHub User @dupoxy (1143957) <dupoxy@users.noreply.github.com> GitHub User @erifan (31343225) <eric.fang@arm.com> GitHub User @esell (9735165) <eujon.sellers@gmail.com> +GitHub User @fatedier (7346661) <fatedier@gmail.com> GitHub User @frennkie (6499251) <mail@rhab.de> +GitHub User @geedchin (11672310) <geedchin@gmail.com> +GitHub User @GrigoriyMikhalkin (3637857) <grigoriymikhalkin@gmail.com> GitHub User @hengwu0 (41297446) <41297446+hengwu0@users.noreply.github.com> GitHub User @itchyny (375258) <itchyny@hatena.ne.jp> GitHub User @jinmiaoluo (39730824) <jinmiaoluo@icloud.com> @@ -820,11 +870,13 @@ GitHub User @kc1212 (1093806) <kc1212@users.noreply.github.com> GitHub User @Kropekk (13366453) <kamilkropiewnicki@gmail.com> GitHub User @linguohua (3434367) <lghchinaidea@gmail.com> GitHub User @LotusFenn (13775899) <fenn.lotus@gmail.com> +GitHub User @ly303550688 (11519839) <yang.liu636@gmail.com> GitHub User @madiganz (18340029) <zacharywmadigan@gmail.com> GitHub User @maltalex (10195391) <code@bit48.net> GitHub User @Matts966 (28551465) <Matts966@users.noreply.github.com> GitHub User @micnncim (21333876) <micnncim@gmail.com> GitHub User @mkishere (224617) <224617+mkishere@users.noreply.github.com> +GitHub User @nu50218 (40682920) <nu_ll@icloud.com> GitHub User @OlgaVlPetrova (44112727) <OVPpetrova@gmail.com> GitHub User @pityonline (438222) <pityonline@gmail.com> GitHub User @po3rin (29445112) <abctail30@gmail.com> @@ -836,6 +888,7 @@ GitHub User @shogo-ma (9860598) <Choroma194@gmail.com> GitHub User @skanehira (7888591) <sho19921005@gmail.com> GitHub User @tatsumack (4510569) <tatsu.mack@gmail.com> GitHub User @tell-k (26263) <ffk2005@gmail.com> +GitHub User @tennashi (10219626) <tennashio@gmail.com> GitHub User @uhei (2116845) <uhei@users.noreply.github.com> GitHub User @uropek (39370426) <uropek@gmail.com> GitHub User @utkarsh-extc (53217283) <utkarsh.extc@gmail.com> @@ -861,6 +914,7 @@ Greg Thelen <gthelen@google.com> Greg Ward <greg@gerg.ca> Grégoire Delattre <gregoire.delattre@gmail.com> Gregory Man <man.gregory@gmail.com> +Gregory Petrosyan <gregory.petrosyan@gmail.com> Guilherme Caruso <gui.martinscaruso@gmail.com> Guilherme Garnier <guilherme.garnier@gmail.com> Guilherme Goncalves <guilhermeaugustosg@gmail.com> @@ -917,6 +971,7 @@ Hitoshi Mitake <mitake.hitoshi@gmail.com> Holden Huang <ttyh061@gmail.com> Hong Ruiqi <hongruiqi@gmail.com> Hongfei Tan <feilengcui008@gmail.com> +Horacio Duran <horacio.duran@gmail.com> Horst Rutter <hhrutter@gmail.com> Hossein Sheikh Attar <hattar@google.com> Howard Zhang <howard.zhang@arm.com> @@ -927,6 +982,7 @@ Huan Du <i@huandu.me> Hugues Bruant <hugues.bruant@gmail.com> Huy Le <huy.dinh.le.89@gmail.com> Hyang-Ah Hana Kim <hakim@google.com> <hyangah@gmail.com> +Hyoyoung Chang <hyoyoung@gmail.com> Ian Cottrell <iancottrell@google.com> Ian Davis <nospam@iandavis.com> Ian Gudger <ian@loosescre.ws> @@ -986,6 +1042,7 @@ Jake B <doogie1012@gmail.com> Jakob Borg <jakob@nym.se> Jakob Weisblat <jakobw@mit.edu> Jakub Čajka <jcajka@redhat.com> +Jakub Kaczmarzyk <jakubk@mit.edu> Jakub Ryszard Czarnowicz <j.czarnowicz@gmail.com> Jamal Carvalho <jamal.a.carvalho@gmail.com> James Aguilar <jaguilar@google.com> @@ -1032,6 +1089,7 @@ Jan Steinke <jan.steinke@gmail.com> Jan Ziak <0xe2.0x9a.0x9b@gmail.com> Jani Monoses <jani.monoses@ubuntu.com> <jani.monoses@gmail.com> Jannis Andrija Schnitzer <jannis@schnitzer.im> +Jared Allard <jaredallard@users.noreply.github.com> Jared Culp <jculp14@gmail.com> Jaroslavas Počepko <jp@webmaster.ms> Jason A. Donenfeld <Jason@zx2c4.com> @@ -1086,8 +1144,11 @@ Jerrin Shaji George <jerrinsg@gmail.com> Jess Frazelle <me@jessfraz.com> Jesse Szwedko <jesse.szwedko@gmail.com> Jesús Espino <jespinog@gmail.com> +Jia Zhan <jzhan@uber.com> +Jiacai Liu <jiacai2050@gmail.com> Jianing Yu <jnyu@google.com> Jianqiao Li <jianqiaoli@google.com> +Jie Ma <jienius@outlook.com> Jihyun Yu <yjh0502@gmail.com> Jim Cote <jfcote87@gmail.com> Jim Kingdon <jim@bolt.me> @@ -1135,6 +1196,7 @@ John Howard Palevich <jack.palevich@gmail.com> John Jeffery <jjeffery@sp.com.au> John Jenkins <twodopeshaggy@gmail.com> John Leidegren <john.leidegren@gmail.com> +John McCabe <john@johnmccabe.net> John Moore <johnkenneth.moore@gmail.com> John Newlin <jnewlin@google.com> John Papandriopoulos <jpap.code@gmail.com> @@ -1146,6 +1208,7 @@ John Tuley <john@tuley.org> John Weldon <johnweldon4@gmail.com> Johnny Luo <johnnyluo1980@gmail.com> Jon Chen <jchen@justin.tv> +Jon Johnson <jonjohnson@google.com> Jonas Bernoulli <jonas@bernoul.li> Jonathan Allie <jonallie@google.com> Jonathan Amsterdam <jba@google.com> @@ -1165,6 +1228,7 @@ Jonathon Lacher <jonathon.lacher@gmail.com> Jongmin Kim <atomaths@gmail.com> Joonas Kuorilehto <joneskoo@derbian.fi> Joop Kiefte <ikojba@gmail.com> <joop@kiefte.net> +Jordan Christiansen <xordspar0@gmail.com> Jordan Krage <jmank88@gmail.com> Jordan Lewis <jordanthelewis@gmail.com> Jordan Liggitt <liggitt@google.com> @@ -1177,6 +1241,7 @@ Josa Gesell <josa@gesell.me> Jose Luis Vázquez González <josvazg@gmail.com> Joseph Bonneau <jcb@google.com> Joseph Holsten <joseph@josephholsten.com> +Josh Baum <joshbaum@google.com> Josh Bleecher Snyder <josharian@gmail.com> Josh Chorlton <jchorlton@gmail.com> Josh Deprez <josh.deprez@gmail.com> @@ -1185,8 +1250,10 @@ Josh Hoak <jhoak@google.com> Josh Holland <jrh@joshh.co.uk> Josh Roppo <joshroppo@gmail.com> Josh Varga <josh.varga@gmail.com> +Joshua Bezaleel Abednego <joshua.bezaleel@gmail.com> Joshua Boelter <joshua.boelter@intel.com> Joshua Chase <jcjoshuachase@gmail.com> +Joshua Crowgey <jcrowgey@uw.edu> Joshua M. Clulow <josh.clulow@joyent.com> Joshua Rubin <joshua@rubixconsulting.com> Josselin Costanzi <josselin@costanzi.fr> @@ -1265,6 +1332,7 @@ Kenji Yano <kenji.yano@gmail.com> Kenneth Shaw <kenshaw@gmail.com> Kenny Grant <kennygrant@gmail.com> Kenta Mori <zoncoen@gmail.com> +Kerollos Magdy <kerolloz@yahoo.com> Ketan Parmar <ketanbparmar@gmail.com> Kevan Swanberg <kevswanberg@gmail.com> Kevin Ballard <kevin@sb.org> @@ -1277,10 +1345,14 @@ Kevin Malachowski <chowski@google.com> Kevin Ruffin <kruffin@gmail.com> Kevin Vu <kevin.m.vu@gmail.com> Kevin Zita <bleedgreenandgold@gmail.com> +Keyan Pishdadian <kpishdadian@gmail.com> +Kezhu Wang <kezhuw@gmail.com> +Khosrow Moossavi <khos2ow@gmail.com> Kieran Colford <kieran@kcolford.com> Kim Shrier <kshrier@racktopsystems.com> Kim Yongbin <kybinz@gmail.com> Kir Kolyshkin <kolyshkin@gmail.com> +Kirill Korotaev <kirillx@gmail.com> Kirill Motkov <Motkov.Kirill@gmail.com> Kirill Smelkov <kirr@nexedi.com> Kirill Tatchihin <kirabsuir@gmail.com> @@ -1308,6 +1380,7 @@ Kyle Consalus <consalus@gmail.com> Kyle Isom <kyle@gokyle.net> Kyle Jones <kyle@kyledj.com> Kyle Lemons <kyle@kylelemons.net> <kevlar@google.com> +Kyle Nusbaum <kyle@datadog.com> Kyle Shannon <kyle@pobox.com> Kyle Spiers <eiais@google.com> Kyle Wood <kyle@kylewood.cc> @@ -1339,6 +1412,8 @@ Leonardo Comelli <leonardo.comelli@gmail.com> Leonel Quinteros <leonel.quinteros@gmail.com> Lev Shamardin <shamardin@gmail.com> Lewin Bormann <lewin.bormann@gmail.com> +Liam Haworth <liam@haworth.id.au> +Lily Chung <lilithkchung@gmail.com> Lion Yang <lion@aosc.xyz> Liz Rice <liz@lizrice.com> Lloyd Dewolf <foolswisdom@gmail.com> @@ -1396,6 +1471,7 @@ Marcel van Lohuizen <mpvl@golang.org> Marcelo Cantos <marcelo.cantos@gmail.com> Marcelo E. Magallon <marcelo.magallon@gmail.com> Marco Hennings <marco.hennings@freiheit.com> +Marcus Weiner <marcus.weiner@gmail.com> Marcus Willock <crazcalm@gmail.com> Marga Manterola <marga@google.com> Mariano Cano <mariano@smallstep.com> @@ -1426,6 +1502,7 @@ Markus Duft <markus.duft@salomon.at> Markus Sonderegger <marraison@gmail.com> Markus Zimmermann <zimmski@gmail.com> Marten Seemann <martenseemann@gmail.com> +Martin Asquino <martin.asquino@gmail.com> Martin Bertschler <mbertschler@gmail.com> Martin Garton <garton@gmail.com> Martin Habbecke <marhab@google.com> @@ -1449,6 +1526,7 @@ Maryan Hratson <gmarik@gmail.com> Masahiro Furudate <masahiro.furudate@gmail.com> Masahiro Wakame <vvakame@gmail.com> Masaki Yoshida <yoshida.masaki@gmail.com> +Masaya Watanabe <sfbgwm30@gmail.com> Mat Byczkowski <mbyczkowski@gmail.com> Mat Ryer <thatmatryer@gmail.com> Máté Gulyás <mgulyas86@gmail.com> @@ -1495,6 +1573,7 @@ Max Ushakov <ushmax@gmail.com> Maxim Eryomenko <moeryomenko@gmail.com> Maxim Khitrov <max@mxcrypt.com> Maxim Pimenov <mpimenov@google.com> +Maxim Pugachev <pugachev.mm@gmail.com> Maxim Ushakov <ushakov@google.com> Maxime de Roucy <maxime.deroucy@gmail.com> Máximo Cuadros Ortiz <mcuadros@gmail.com> @@ -1549,6 +1628,7 @@ Michal Bohuslávek <mbohuslavek@gmail.com> Michal Cierniak <cierniak@google.com> Michał Derkacz <ziutek@lnet.pl> Michal Franc <lam.michal.franc@gmail.com> +Michał Łowicki <mlowicki@gmail.com> Michal Pristas <michal.pristas@gmail.com> Michal Rostecki <mrostecki@suse.de> Michalis Kargakis <michaliskargakis@gmail.com> @@ -1556,6 +1636,7 @@ Michel Lespinasse <walken@google.com> Mickael Kerjean <mickael.kerjean@gmail.com> Mickey Reiss <mickeyreiss@gmail.com> Miek Gieben <miek@miek.nl> <remigius.gieben@gmail.com> +Miguel Acero <acero@google.com> Miguel Mendez <stxmendez@gmail.com> Miguel Molina <hi@mvader.me> Mihai Borobocea <MihaiBorobocea@gmail.com> @@ -1582,6 +1663,7 @@ Mikio Hara <mikioh.mikioh@gmail.com> Mikkel Krautz <mikkel@krautz.dk> <krautz@gmail.com> Mikołaj Baranowski <mikolajb@gmail.com> Milan Knezevic <milan.knezevic@mips.com> +Milan Patel <bicelot3@gmail.com> Milutin Jovanović <jovanovic.milutin@gmail.com> MinJae Kwon <mingrammer@gmail.com> Miquel Sabaté Solà <mikisabate@gmail.com> @@ -1603,8 +1685,10 @@ Mrunal Patel <mrunalp@gmail.com> Muhammad Falak R Wani <falakreyaz@gmail.com> Muhammed Uluyol <uluyol0@gmail.com> Muir Manders <muir@mnd.rs> +Mukesh Sharma <sharma.mukesh439@gmail.com> Mura Li <mura_li@castech.com.tw> Mykhailo Lesyk <mikhail@lesyk.org> +Naman Aggarwal <aggarwal.nam@gmail.com> Nan Deng <monnand@gmail.com> Nao Yonashiro <owan.orisano@gmail.com> Naoki Kanatani <k12naoki@gmail.com> @@ -1612,6 +1696,7 @@ Nate Wilkinson <nathanwilk7@gmail.com> Nathan Cantelmo <n.cantelmo@gmail.com> Nathan Caza <mastercactapus@gmail.com> Nathan Dias <nathan.dias@orijtech.com> +Nathan Fiscaletti <nathan.fiscaletti@vrazo.com> Nathan Humphreys <nkhumphreys@gmail.com> Nathan John Youngman <nj@nathany.com> Nathan Otterness <otternes@cs.unc.edu> @@ -1621,6 +1706,7 @@ Nathan Youngman <git@nathany.com> Nathan(yinian) Hu <nathanhu@google.com> Nathaniel Cook <nvcook42@gmail.com> Naveen Kumar Sangi <naveenkumarsangi@protonmail.com> +Neeilan Selvalingam <neeilan96@gmail.com> Neelesh Chandola <neelesh.c98@gmail.com> Neil Lyons <nwjlyons@googlemail.com> Neuman Vong <neuman.vong@gmail.com> @@ -1661,17 +1747,20 @@ Nikita Vanyasin <nikita.vanyasin@gmail.com> Niklas Schnelle <niklas.schnelle@gmail.com> Niko Dziemba <niko@dziemba.com> Nikolay Turpitko <nikolay@turpitko.com> +Nikson Kanti Paul <nikson.sust@gmail.com> Nils Larsgård <nilsmagnus@gmail.com> Nir Soffer <nirsof@gmail.com> Niranjan Godbole <niranjan8192@gmail.com> Nishanth Shanmugham <nishanth.gerrard@gmail.com> Noah Campbell <noahcampbell@gmail.com> +Noah Goldman <noahg34@gmail.com> Noble Johnson <noblepoly@gmail.com> Nodir Turakulov <nodir@google.com> Noel Georgi <git@frezbo.com> Norberto Lopes <nlopes.ml@gmail.com> Norman B. Lancaster <qbradq@gmail.com> Nuno Cruces <ncruces@users.noreply.github.com> +Obeyda Djeffal <djefobey@gmail.com> Odin Ugedal <odin@ugedal.com> Oleg Bulatov <dmage@yandex-team.ru> Oleg Vakheta <helginet@gmail.com> @@ -1689,6 +1778,7 @@ Omar Jarjur <ojarjur@google.com> Oryan Moshe <iamoryanmoshe@gmail.com> Osamu TONOMORI <osamingo@gmail.com> Özgür Kesim <oec-go@kesim.org> +Pablo Caderno <kaderno@gmail.com> Pablo Lalloni <plalloni@gmail.com> Pablo Rozas Larraondo <pablo.larraondo@anu.edu.au> Pablo Santiago Blum de Aguiar <scorphus@gmail.com> @@ -1702,6 +1792,8 @@ Parker Moore <parkrmoore@gmail.com> Parminder Singh <parmsingh101@gmail.com> Pascal Dierich <pascal@pascaldierich.com> Pascal S. de Kloe <pascal@quies.net> +Paschalis Tsilias <paschalis.tsilias@gmail.com> +Pasi Tähkäpää <pasi.tahkapaa@gmail.com> Pat Moroney <pat@pat.email> Patrick Barker <barkerp@vmware.com> Patrick Crosby <patrick@stathat.com> @@ -1718,6 +1810,7 @@ Paul A Querna <paul.querna@gmail.com> Paul Borman <borman@google.com> Paul Boyd <boyd.paul2@gmail.com> Paul Chang <paulchang@google.com> +Paul D. Weber <x0bdev@gmail.com> Paul Hammond <paul@paulhammond.org> Paul Hankin <paulhankin@google.com> Paul Jolly <paul@myitcv.org.uk> @@ -1743,8 +1836,10 @@ Pavel Zinovkin <pavel.zinovkin@gmail.com> Pavlo Sumkin <ymkins@gmail.com> Pawel Knap <pawelknap88@gmail.com> Pawel Szczur <filemon@google.com> +Pei Xian Chee <luciolas1991@gmail.com> Percy Wegmann <ox.to.a.cart@gmail.com> Perry Abbott <perry.j.abbott@gmail.com> +Petar Dambovaliev <petar.atanasov.1987@gmail.com> Petar Maymounkov <petarm@gmail.com> Peter Armitage <peter.armitage@gmail.com> Peter Bourgon <peter@bourgon.org> @@ -1781,6 +1876,7 @@ Philip Hofer <phofer@umich.edu> Philip K. Warren <pkwarren@gmail.com> Philip Nelson <me@pnelson.ca> Philipp Stephani <phst@google.com> +Pierre Carru <pierre.carru@eshard.com> Pierre Durand <pierredurand@gmail.com> Pierre Prinetti <pierreprinetti@gmail.com> Pierre Roullon <pierre.roullon@gmail.com> @@ -1789,11 +1885,14 @@ Pieter Droogendijk <pieter@binky.org.uk> Pietro Gagliardi <pietro10@mac.com> Piyush Mishra <piyush@codeitout.com> Plekhanov Maxim <kishtatix@gmail.com> +Polina Osadcha <polliosa@google.com> Pontus Leitzler <leitzler@gmail.com> +Povilas Versockas <p.versockas@gmail.com> Prasanga Siripala <pj@pjebs.com.au> Prasanna Swaminathan <prasanna@mediamath.com> Prashant Agrawal <prashant.a.vjti@gmail.com> Prashant Varanasi <prashant@prashantv.com> +Praveen Kumar <praveen+git@kumar.in> Pravendra Singh <hackpravj@gmail.com> Preetam Jinka <pj@preet.am> Pure White <wu.purewhite@gmail.com> @@ -1804,6 +1903,7 @@ Quan Yong Zhai <qyzhai@gmail.com> Quentin Perez <qperez@ocs.online.net> Quentin Renard <contact@asticode.com> Quentin Smith <quentin@golang.org> +Quey-Liang Kao <s101062801@m101.nthu.edu.tw> Quinn Slack <sqs@sourcegraph.com> Quinten Yearsley <qyearsley@chromium.org> Quoc-Viet Nguyen <afelion@gmail.com> @@ -1831,6 +1931,7 @@ Reilly Watson <reillywatson@gmail.com> Reinaldo de Souza Jr <juniorz@gmail.com> Remi Gillig <remigillig@gmail.com> Rémy Oudompheng <oudomphe@phare.normalesup.org> <remyoudompheng@gmail.com> +Ren Ogaki <re.yuz77777@gmail.com> Rens Rikkerink <Ikkerens@users.noreply.github.com> Rhys Hiltner <rhys@justin.tv> Ricardo Padilha <ricardospadilha@gmail.com> @@ -1842,6 +1943,8 @@ Richard Eric Gavaletz <gavaletz@gmail.com> Richard Gibson <richard.gibson@gmail.com> Richard Miller <miller.research@gmail.com> Richard Musiol <mail@richard-musiol.de> <neelance@gmail.com> +Richard Ulmer <codesoap@mailbox.org> +Richard Wilkes <wilkes@me.com> Rick Arnold <rickarnoldjr@gmail.com> Rick Hudson <rlh@golang.org> Rick Sayre <whorfin@gmail.com> @@ -1860,6 +1963,7 @@ Robert Figueiredo <robfig@gmail.com> Robert Griesemer <gri@golang.org> Robert Hencke <robert.hencke@gmail.com> Robert Iannucci <iannucci@google.com> +Robert Kuska <rkuska@gmail.com> Robert Obryk <robryk@gmail.com> Robert Sesek <rsesek@google.com> Robert Snedegar <roberts@google.com> @@ -1878,6 +1982,7 @@ Roger Pau Monné <royger@gmail.com> Roger Peppe <rogpeppe@gmail.com> Rohan Challa <rohan@golang.org> Rohan Verma <rohanverma2004@gmail.com> +Rohith Ravi <entombedvirus@gmail.com> Roland Illig <roland.illig@gmx.de> Roland Shoemaker <rolandshoemaker@gmail.com> Romain Baugue <romain.baugue@elwinar.com> @@ -1887,6 +1992,7 @@ Roman Shchekin <mrqtros@gmail.com> Ron Hashimoto <mail@h2so5.net> Ron Minnich <rminnich@gmail.com> Ross Chater <rdchater@gmail.com> +Ross Kinsey <rossikinsey@gmail.com> Ross Light <light@google.com> <rlight2@gmail.com> Ross Smith II <ross@smithii.com> Rowan Marshall <rowanajmarshall@gmail.com> @@ -1921,6 +2027,8 @@ Sakeven Jiang <jc5930@sina.cn> Salmān Aljammāz <s@0x65.net> Sam Arnold <sarnold64@bloomberg.net> Sam Boyer <tech@samboyer.org> +Sam Chen <chenxsan@gmail.com> +Sam Cross <samgcdev@gmail.com> Sam Ding <samding@ca.ibm.com> Sam Hug <samuel.b.hug@gmail.com> Sam Thorogood <thorogood@google.com> <sam.thorogood@gmail.com> @@ -1972,6 +2080,7 @@ Sergey 'SnakE' Gromov <snake.scaly@gmail.com> Sergey Arseev <sergey.arseev@intel.com> Sergey Dobrodey <sergey.dobrodey@synesis.ru> Sergey Frolov <sfrolov@google.com> +Sergey Glushchenko <gsserge@gmail.com> Sergey Ivanov <ser1325@gmail.com> Sergey Lukjanov <me@slukjanov.name> Sergey Mishin <sergeymishine@gmail.com> @@ -1987,7 +2096,9 @@ Seth Vargo <sethvargo@gmail.com> Shahar Kohanim <skohanim@gmail.com> Shamil Garatuev <garatuev@gmail.com> Shane Hansen <shanemhansen@gmail.com> +Shang Jian Ding <sding3@ncsu.edu> Shaozhen Ding <dsz0111@gmail.com> +Shaquille Wyan Que <shaqqywyan@gmail.com> Shaun Dunning <shaun.dunning@uservoice.com> Shawn Elliott <selliott@microsoft.com> Shawn Ledbetter <sledbetter@google.com> @@ -2008,6 +2119,7 @@ Shubham Sharma <shubham.sha12@gmail.com> Shun Fan <sfan@google.com> Silvan Jegen <s.jegen@gmail.com> Simarpreet Singh <simar@linux.com> +Simon Drake <simondrake1990@gmail.com> Simon Ferquel <simon.ferquel@docker.com> Simon Jefford <simon.jefford@gmail.com> Simon Rawet <simon@rawet.se> @@ -2018,6 +2130,8 @@ Sina Siadat <siadat@gmail.com> Sjoerd Siebinga <sjoerd.siebinga@gmail.com> Sokolov Yura <funny.falcon@gmail.com> Song Gao <song@gao.io> +Soojin Nam <jsunam@gmail.com> +Søren L. Hansen <soren@linux2go.dk> Spencer Kocot <spencerkocot@gmail.com> Spencer Nelson <s@spenczar.com> Spencer Tung <spencertung@google.com> @@ -2074,6 +2188,7 @@ Taavi Kivisik <taavi.kivisik@gmail.com> Tad Fisher <tadfisher@gmail.com> Tad Glines <tad.glines@gmail.com> Tadas Valiukas <tadovas@gmail.com> +Tadeo Kondrak <me@tadeo.ca> Taesu Pyo <pyotaesu@gmail.com> Tai Le <letientai299@gmail.com> Taj Khattra <taj.khattra@gmail.com> @@ -2083,6 +2198,7 @@ Takeshi YAMANASHI <9.nashi@gmail.com> Takuto Ikuta <tikuta@google.com> Takuya Ueda <uedatakuya@gmail.com> Tal Shprecher <tshprecher@gmail.com> +Tamás Gulácsi <tgulacsi78@gmail.com> Tamir Duberstein <tamird@gmail.com> Tao Qingyun <qingyunha@gmail.com> Tao Shen <shentaoskyking@gmail.com> @@ -2102,6 +2218,7 @@ Tetsuo Kiso <tetsuokiso9@gmail.com> Than McIntosh <thanm@google.com> Thanabodee Charoenpiriyakij <wingyminus@gmail.com> Thanatat Tamtan <acoshift@gmail.com> +The Hatsune Daishi <nao20010128@gmail.com> Thiago Avelino <t@avelino.xxx> Thiago Fransosi Farina <thiago.farina@gmail.com> <tfarina@chromium.org> Thomas Alan Copeland <talan.copeland@gmail.com> @@ -2128,9 +2245,11 @@ Tim Ebringer <tim.ebringer@gmail.com> Tim Heckman <t@heckman.io> Tim Henderson <tim.tadh@gmail.com> Tim Hockin <thockin@google.com> +Tim Möhlmann <muhlemmer@gmail.com> Tim Swast <swast@google.com> Tim Wright <tenortim@gmail.com> Tim Xu <xiaoxubeii@gmail.com> +Timmy Douglas <timmyd983@gmail.com> Timo Savola <timo.savola@gmail.com> Timo Truyts <alkaloid.btx@gmail.com> Timothy Studd <tim@timstudd.com> @@ -2149,6 +2268,7 @@ Tom Lanyon <tomlanyon@google.com> Tom Levy <tomlevy93@gmail.com> Tom Limoncelli <tal@whatexit.org> Tom Linford <tomlinford@gmail.com> +Tom Parkin <tom.parkin@gmail.com> Tom Payne <twpayne@gmail.com> Tom Szymanski <tgs@google.com> Tom Thorogood <me+google@tomthorogood.co.uk> @@ -2162,6 +2282,7 @@ Tony Reix <tony.reix@bull.net> Tony Walker <walkert.uk@gmail.com> Tooru Takahashi <tooru.takahashi134@gmail.com> Tor Andersson <tor.andersson@gmail.com> +Torben Schinke <torben.schinke@neotos.de> Tormod Erevik Lea <tormodlea@gmail.com> Toshihiro Shiino <shiino.toshihiro@gmail.com> Toshiki Shima <hayabusa1419@gmail.com> @@ -2178,12 +2299,15 @@ Tristan Ooohry <ooohry@gmail.com> Tristan Rice <rice@fn.lc> Troels Thomsen <troels@thomsen.io> Trung Nguyen <trung.n.k@gmail.com> +Tsuji Daishiro <dram.dt.shonan@gmail.com> Tudor Golubenco <tudor.g@gmail.com> Tugdual Saunier <tugdual.saunier@gmail.com> Tuo Shan <sturbo89@gmail.com> <shantuo@google.com> Tyler Bui-Palsulich <tpalsulich@google.com> Tyler Bunnell <tylerbunnell@gmail.com> Tyler Treat <ttreat31@gmail.com> +Tyson Andre <tysonandre775@gmail.com> +Tzach Shabtay <tzachshabtay@gmail.com> Tzu-Jung Lee <roylee17@currant.com> Udalov Max <re.udalov@gmail.com> Ugorji Nwoke <ugorji@gmail.com> @@ -2217,6 +2341,7 @@ Visweswara R <r.visweswara@gmail.com> Vitaly Zdanevich <zdanevich.vitaly@ya.ru> Vitor De Mario <vitordemario@gmail.com> Vivek Sekhar <vsekhar@google.com> +Vivian Liang <vliang88@gmail.com> Vlad Krasnov <vlad@cloudflare.com> Vladimir Evgrafov <evgrafov.vladimir@gmail.com> Vladimir Kovpak <cn007b@gmail.com> @@ -2231,6 +2356,7 @@ Volodymyr Paprotski <vpaprots@ca.ibm.com> W. Trevor King <wking@tremily.us> Wade Simmons <wade@wades.im> Wagner Riffel <wgrriffel@gmail.com> +Walt Della <walt@javins.net> Walter Poupore <wpoupore@google.com> Wander Lairson Costa <wcosta@mozilla.com> Wang Xuerui <git@xen0n.name> @@ -2274,12 +2400,15 @@ Xudong Zheng <7pkvm5aw@slicealias.com> Xuyang Kang <xuyangkang@gmail.com> Yamagishi Kazutoshi <ykzts@desire.sh> Yan Zou <yzou@google.com> +Yang Hau <vulxj0j8j8@gmail.com> Yang Tian <linuxty@gmail.com> Yann Hodique <yhodique@google.com> Yann Kerhervé <yann.kerherve@gmail.com> Yann Salaün <yannsalaun1@gmail.com> +Yannic Bonenberger <contact@yannic-bonenberger.com> Yao Zhang <lunaria21@gmail.com> Yaron de Leeuw <jarondl@google.com> +Yaroslav Vorobiov <yar.vorobiov@gmail.com> Yasha Bubnov <girokompass@gmail.com> Yasser Abdolmaleki <yasser@yasser.ca> Yasuharu Goto <matope.ono@gmail.com> @@ -2298,6 +2427,7 @@ Yoshiyuki Mineo <yoshiyuki.mineo@gmail.com> Yosuke Akatsuka <yosuke.akatsuka@gmail.com> Yu Heng Zhang <annita.zhang@cn.ibm.com> Yu Xuan Zhang <zyxsh@cn.ibm.com> +Yuichi Kishimoto <yk2220s@gmail.com> Yuichi Nishiwaki <yuichi.nishiwaki@gmail.com> Yuji Yaginuma <yuuji.yaginuma@gmail.com> Yuki OKUSHI <huyuumi.dev@gmail.com> @@ -2318,6 +2448,7 @@ Zak <zrjknill@gmail.com> Zakatell Kanda <hi@zkanda.io> Zellyn Hunter <zellyn@squareup.com> <zellyn@gmail.com> Zev Goldstein <zev.goldstein@gmail.com> +Zhang Boyang <zhangboyang.id@gmail.com> Zheng Dayu <davidzheng23@gmail.com> Zheng Xu <zheng.xu@arm.com> Zhengyu He <hzy@google.com> diff --git a/api/go1.15.txt b/api/go1.15.txt index b51837cf38..dd90506eba 100644 --- a/api/go1.15.txt +++ b/api/go1.15.txt @@ -112,8 +112,6 @@ pkg debug/pe, const IMAGE_SUBSYSTEM_WINDOWS_GUI = 2 pkg debug/pe, const IMAGE_SUBSYSTEM_WINDOWS_GUI ideal-int pkg debug/pe, const IMAGE_SUBSYSTEM_XBOX = 14 pkg debug/pe, const IMAGE_SUBSYSTEM_XBOX ideal-int -pkg go/printer, const StdFormat = 16 -pkg go/printer, const StdFormat Mode pkg math/big, method (*Int) FillBytes([]uint8) []uint8 pkg net, method (*Resolver) LookupIP(context.Context, string, string) ([]IP, error) pkg net/url, method (*URL) EscapedFragment() string diff --git a/doc/go1.15.html b/doc/go1.15.html index ce6894d2e0..8872d71138 100644 --- a/doc/go1.15.html +++ b/doc/go1.15.html @@ -14,13 +14,21 @@ Do not send CLs removing the interior tags from such phrases. main ul li { margin: 0.5em 0; } </style> -<h2 id="introduction">DRAFT RELEASE NOTES — Introduction to Go 1.15</h2> +<h2 id="introduction">Introduction to Go 1.15</h2> <p> - <strong> - Go 1.15 is not yet released. These are work-in-progress - release notes. Go 1.15 is expected to be released in August 2020. - </strong> + The latest Go release, version 1.15, arrives six months after <a href="go1.14">Go 1.14</a>. + Most of its changes are in the implementation of the toolchain, runtime, and libraries. + As always, the release maintains the Go 1 <a href="/doc/go1compat.html">promise of compatibility</a>. + We expect almost all Go programs to continue to compile and run as before. +</p> + +<p> + Go 1.15 includes <a href="#linker">substantial improvements to the linker</a>, + improves <a href="#runtime">allocation for small objects at high core counts</a>, and + deprecates <a href="#commonname">X.509 CommonName</a>. + <code>GOPROXY</code> now supports skipping proxies that return errors and + a new <a href="#time/tzdata">embedded tzdata package</a> has been added. </p> <h2 id="language">Changes to the language</h2> @@ -94,6 +102,16 @@ Do not send CLs removing the interior tags from such phrases. preemption. </p> +<h3 id="386">386</h3> + +<p><!-- golang.org/issue/40255 --> + Go 1.15 is the last release to support x87-only floating-point + hardware (<code>GO386=387</code>). Future releases will require at + least SSE2 support on 386, raising Go's + minimum <code>GOARCH=386</code> requirement to the Intel Pentium 4 + (released in 2000) or AMD Opteron/Athlon 64 (released in 2003). +</p> + <h2 id="tools">Tools</h2> <h3 id="go-command">Go command</h3> @@ -336,8 +354,10 @@ Do not send CLs removing the interior tags from such phrases. </p> <p><!-- CL 207877 --> - TODO: <a href="https://golang.org/cl/207877">https://golang.org/cl/207877</a>: Revert -buildmode=pie to internal linking. - The linker defaults to internal linking mode for PIE on linux/amd64 and linux/arm64, which does require a C linker. + The linker now defaults to internal linking mode + for <code>-buildmode=pie</code> on + <code>linux/amd64</code> and <code>linux/arm64</code>, so these + configurations no longer require a C linker. </p> <h2 id="objdump">Objdump</h2> @@ -374,6 +394,23 @@ Do not send CLs removing the interior tags from such phrases. documentation</a> for more information. </p> +<h3 id="commonname">X.509 CommonName deprecation</h3> + +<p><!-- CL 231379 --> + The deprecated, legacy behavior of treating the <code>CommonName</code> + field on X.509 certificates as a host name when no Subject Alternative Names + are present is now disabled by default. It can be temporarily re-enabled by + adding the value <code>x509ignoreCN=0</code> to the <code>GODEBUG</code> + environment variable. +</p> + +<p> + Note that if the <code>CommonName</code> is an invalid host name, it's always + ignored, regardless of <code>GODEBUG</code> settings. Invalid names include + those with any characters other than letters, digits, hyphens and underscores, + and those with empty labels or trailing dots. +</p> + <h3 id="minor_library_changes">Minor changes to the library</h3> <p> @@ -495,6 +532,17 @@ Do not send CLs removing the interior tags from such phrases. fields <code>OCSPResponse</code> and <code>SignedCertificateTimestamps</code> are now repopulated on client-side resumed connections. </p> + + <p><!-- CL 227840 --> + <a href="/pkg/crypto/tls/#Conn"><code>tls.Conn</code></a> + now returns an opaque error on permanently broken connections, wrapping + the temporary + <a href="/pkg/net/http/#Error"><code>net.Error</code></a>. To access the + original <code>net.Error</code>, use + <a href="/pkg/errors/#As"><code>errors.As</code></a> (or + <a href="/pkg/errors/#Unwrap"><code>errors.Unwrap</code></a>) instead of a + type assertion. + </p> </dd> </dl><!-- crypto/tls --> @@ -511,15 +559,6 @@ Do not send CLs removing the interior tags from such phrases. certificates with trailing dots. </p> - <p><!-- CL 231379 --> - The deprecated, legacy behavior of treating the <code>CommonName</code> - field as a hostname when no Subject Alternative Names are present is now - disabled by default. It can be temporarily re-enabled by adding the value - <code>x509ignoreCN=0</code> to the <code>GODEBUG</code> environment - variable. If the <code>CommonName</code> is an invalid hostname, it's - always ignored. - </p> - <p><!-- CL 217298 --> The new <a href="/pkg/crypto/x509/#CreateRevocationList"><code>CreateRevocationList</code></a> function and <a href="/pkg/crypto/x509/#RevocationList"><code>RevocationList</code></a> type @@ -618,11 +657,6 @@ Do not send CLs removing the interior tags from such phrases. <dl id="encoding/json"><dt><a href="/pkg/encoding/json/">encoding/json</a></dt> <dd> - <p><!-- CL 191783 --> - Decoding a JSON array into a slice no longer reuses any existing slice elements, - following the rules that the package documentation already stated. - </p> - <p><!-- CL 199837 --> The package now has an internal limit to the maximum depth of nesting when decoding. This reduces the possibility that a @@ -635,8 +669,8 @@ Do not send CLs removing the interior tags from such phrases. <dl id="flag"><dt><a href="/pkg/flag/">flag</a></dt> <dd> <p><!-- CL 221427 --> - When the flag package sees <code>-h</code> or <code>-help</code>, and - those flags are not defined, it now prints a usage message. + When the <code>flag</code> package sees <code>-h</code> or <code>-help</code>, + and those flags are not defined, it now prints a usage message. If the <a href="/pkg/flag/#FlagSet"><code>FlagSet</code></a> was created with <a href="/pkg/flag/#ExitOnError"><code>ExitOnError</code></a>, <a href="/pkg/flag/#FlagSet.Parse"><code>FlagSet.Parse</code></a> would then @@ -656,15 +690,18 @@ Do not send CLs removing the interior tags from such phrases. </dd> </dl><!-- fmt --> -<dl id="go/printer"><dt><a href="/pkg/go/printer/">go/printer</a></dt> +<dl id="go/format"><dt><a href="/pkg/go/format/">go/format</a></dt> <dd> - <p><!-- CL 231461 --> - The new <a href="/pkg/go/printer/#Mode"><code>Mode</code></a> - value <a href="/pkg/go/printer/#StdFormat"><code>StdFormat</code></a> - directs the printer to apply standard formatting changes while - printing the output. + <p><!-- golang.org/issue/37476, CL 231461, CL 240683 --> + The <a href="/pkg/go/format/#Source"><code>Source</code></a> and + <a href="/pkg/go/format/#Node"><code>Node</code></a> functions + now canonicalize number literal prefixes and exponents as part + of formatting Go source code. This matches the behavior of the + <a href="/pkg/cmd/gofmt/"><code>gofmt</code></a> command as it + was implemented <a href="/doc/go1.13#gofmt">since Go 1.13</a>. + </p> </dd> -</dl><!-- go/printer --> +</dl><!-- go/format --> <dl id="html/template"><dt><a href="/pkg/html/template/">html/template</a></dt> <dd> @@ -698,6 +735,16 @@ Do not send CLs removing the interior tags from such phrases. </dd> </dl><!-- math/big --> +<dl id="math/cmplx"><dt><a href="/pkg/math/cmplx/">math/cmplx</a></dt> + <dd> + <p><!-- CL 220689 --> + The functions in this package were updated to conform to the C99 standard + (Annex G IEC 60559-compatible complex arithmetic) with respect to handling + of special arguments such as infinity, NaN and signed zero. + </p> + </dd> +</dl><!-- math/cmplx--> + <dl id="net"><dt><a href="/pkg/net/">net</a></dt> <dd> <p><!-- CL 228645 --> @@ -868,9 +915,9 @@ Do not send CLs removing the interior tags from such phrases. <dl id="pkg-runtime-pprof"><dt><a href="/pkg/runtime/pprof/">runtime/pprof</a></dt> <dd> <p><!-- CL 189318 --> - The goroutine profile includes the profile labels associated with each goroutine - at the time of profiling. This feature is not yet implemented for the profile - reported with <code>debug=2</code>. + The goroutine profile now includes the profile labels associated with each + goroutine at the time of profiling. This feature is not yet implemented for + the profile reported with <code>debug=2</code>. </p> </dd> </dl> @@ -901,6 +948,7 @@ Do not send CLs removing the interior tags from such phrases. <a href="/pkg/sync/#Map.Delete"><code>Map.Delete</code></a> is more efficient. </p> + </dd> </dl><!-- sync --> <dl id="syscall"><dt><a href="/pkg/syscall/">syscall</a></dt> @@ -956,7 +1004,8 @@ Do not send CLs removing the interior tags from such phrases. </p> <p><!-- CL 229085 --> - TODO: <a href="https://golang.org/cl/229085">https://golang.org/cl/229085</a>: reformat test chatty output + <code>go</code> <code>test</code> <code>-v</code> now groups output by + test name, rather than printing the test name on each line. </p> </dd> </dl><!-- testing --> diff --git a/misc/cgo/test/test.go b/misc/cgo/test/test.go index 8c69ad91ac..35bc3a1447 100644 --- a/misc/cgo/test/test.go +++ b/misc/cgo/test/test.go @@ -901,6 +901,12 @@ typedef struct S32579 { unsigned char data[1]; } S32579; // issue 38649 // Test that #define'd type aliases work. #define netbsd_gid unsigned int + +// issue 40494 +// Inconsistent handling of tagged enum and union types. +enum Enum40494 { X_40494 }; +union Union40494 { int x; }; +void issue40494(enum Enum40494 e, union Union40494* up) {} */ import "C" @@ -2204,3 +2210,10 @@ var issue38649 C.netbsd_gid = 42 // issue 39877 var issue39877 *C.void = nil + +// issue 40494 +// No runtime test; just make sure it compiles. + +func Issue40494() { + C.issue40494(C.enum_Enum40494(C.X_40494), (*C.union_Union40494)(nil)) +} diff --git a/misc/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go index f8dabbe7a0..5e0893784b 100644 --- a/misc/cgo/testshared/shared_test.go +++ b/misc/cgo/testshared/shared_test.go @@ -462,6 +462,7 @@ func TestTrivialExecutable(t *testing.T) { run(t, "trivial executable", "../../bin/trivial") AssertIsLinkedTo(t, "../../bin/trivial", soname) AssertHasRPath(t, "../../bin/trivial", gorootInstallDir) + checkSize(t, "../../bin/trivial", 100000) // it is 19K on linux/amd64, 100K should be enough } // Build a trivial program in PIE mode that links against the shared runtime and check it runs. @@ -470,6 +471,18 @@ func TestTrivialExecutablePIE(t *testing.T) { run(t, "trivial executable", "./trivial.pie") AssertIsLinkedTo(t, "./trivial.pie", soname) AssertHasRPath(t, "./trivial.pie", gorootInstallDir) + checkSize(t, "./trivial.pie", 100000) // it is 19K on linux/amd64, 100K should be enough +} + +// Check that the file size does not exceed a limit. +func checkSize(t *testing.T, f string, limit int64) { + fi, err := os.Stat(f) + if err != nil { + t.Fatalf("stat failed: %v", err) + } + if sz := fi.Size(); sz > limit { + t.Errorf("file too large: got %d, want <= %d", sz, limit) + } } // Build a division test program and check it runs. diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 6c221473e0..4064f0ae41 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -123,7 +123,9 @@ func (p *Package) writeDefs() { // Moreover, empty file name makes compile emit no source debug info at all. var buf bytes.Buffer noSourceConf.Fprint(&buf, fset, def.Go) - if bytes.HasPrefix(buf.Bytes(), []byte("_Ctype_")) { + if bytes.HasPrefix(buf.Bytes(), []byte("_Ctype_")) || + strings.HasPrefix(name, "_Ctype_enum_") || + strings.HasPrefix(name, "_Ctype_union_") { // This typedef is of the form `typedef a b` and should be an alias. fmt.Fprintf(fgo2, "= ") } diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go index 47cb422ab1..9d8a0920b3 100644 --- a/src/cmd/compile/internal/amd64/ssa.go +++ b/src/cmd/compile/internal/amd64/ssa.go @@ -874,7 +874,11 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { ssa.OpAMD64SUBLloadidx1, ssa.OpAMD64SUBLloadidx4, ssa.OpAMD64SUBLloadidx8, ssa.OpAMD64SUBQloadidx1, ssa.OpAMD64SUBQloadidx8, ssa.OpAMD64ANDLloadidx1, ssa.OpAMD64ANDLloadidx4, ssa.OpAMD64ANDLloadidx8, ssa.OpAMD64ANDQloadidx1, ssa.OpAMD64ANDQloadidx8, ssa.OpAMD64ORLloadidx1, ssa.OpAMD64ORLloadidx4, ssa.OpAMD64ORLloadidx8, ssa.OpAMD64ORQloadidx1, ssa.OpAMD64ORQloadidx8, - ssa.OpAMD64XORLloadidx1, ssa.OpAMD64XORLloadidx4, ssa.OpAMD64XORLloadidx8, ssa.OpAMD64XORQloadidx1, ssa.OpAMD64XORQloadidx8: + ssa.OpAMD64XORLloadidx1, ssa.OpAMD64XORLloadidx4, ssa.OpAMD64XORLloadidx8, ssa.OpAMD64XORQloadidx1, ssa.OpAMD64XORQloadidx8, + ssa.OpAMD64ADDSSloadidx1, ssa.OpAMD64ADDSSloadidx4, ssa.OpAMD64ADDSDloadidx1, ssa.OpAMD64ADDSDloadidx8, + ssa.OpAMD64SUBSSloadidx1, ssa.OpAMD64SUBSSloadidx4, ssa.OpAMD64SUBSDloadidx1, ssa.OpAMD64SUBSDloadidx8, + ssa.OpAMD64MULSSloadidx1, ssa.OpAMD64MULSSloadidx4, ssa.OpAMD64MULSDloadidx1, ssa.OpAMD64MULSDloadidx8, + ssa.OpAMD64DIVSSloadidx1, ssa.OpAMD64DIVSSloadidx4, ssa.OpAMD64DIVSDloadidx1, ssa.OpAMD64DIVSDloadidx8: p := s.Prog(v.Op.Asm()) r, i := v.Args[1].Reg(), v.Args[2].Reg() diff --git a/src/cmd/compile/internal/ssa/addressingmodes.go b/src/cmd/compile/internal/ssa/addressingmodes.go index 78c979b7cb..97a5ab4f03 100644 --- a/src/cmd/compile/internal/ssa/addressingmodes.go +++ b/src/cmd/compile/internal/ssa/addressingmodes.go @@ -321,6 +321,23 @@ var combine = map[[2]Op]Op{ [2]Op{OpAMD64XORQconstmodify, OpAMD64LEAQ1}: OpAMD64XORQconstmodifyidx1, [2]Op{OpAMD64XORQconstmodify, OpAMD64LEAQ8}: OpAMD64XORQconstmodifyidx8, + [2]Op{OpAMD64ADDSSload, OpAMD64LEAQ1}: OpAMD64ADDSSloadidx1, + [2]Op{OpAMD64ADDSSload, OpAMD64LEAQ4}: OpAMD64ADDSSloadidx4, + [2]Op{OpAMD64ADDSDload, OpAMD64LEAQ1}: OpAMD64ADDSDloadidx1, + [2]Op{OpAMD64ADDSDload, OpAMD64LEAQ8}: OpAMD64ADDSDloadidx8, + [2]Op{OpAMD64SUBSSload, OpAMD64LEAQ1}: OpAMD64SUBSSloadidx1, + [2]Op{OpAMD64SUBSSload, OpAMD64LEAQ4}: OpAMD64SUBSSloadidx4, + [2]Op{OpAMD64SUBSDload, OpAMD64LEAQ1}: OpAMD64SUBSDloadidx1, + [2]Op{OpAMD64SUBSDload, OpAMD64LEAQ8}: OpAMD64SUBSDloadidx8, + [2]Op{OpAMD64MULSSload, OpAMD64LEAQ1}: OpAMD64MULSSloadidx1, + [2]Op{OpAMD64MULSSload, OpAMD64LEAQ4}: OpAMD64MULSSloadidx4, + [2]Op{OpAMD64MULSDload, OpAMD64LEAQ1}: OpAMD64MULSDloadidx1, + [2]Op{OpAMD64MULSDload, OpAMD64LEAQ8}: OpAMD64MULSDloadidx8, + [2]Op{OpAMD64DIVSSload, OpAMD64LEAQ1}: OpAMD64DIVSSloadidx1, + [2]Op{OpAMD64DIVSSload, OpAMD64LEAQ4}: OpAMD64DIVSSloadidx4, + [2]Op{OpAMD64DIVSDload, OpAMD64LEAQ1}: OpAMD64DIVSDloadidx1, + [2]Op{OpAMD64DIVSDload, OpAMD64LEAQ8}: OpAMD64DIVSDloadidx8, + // 386 [2]Op{Op386MOVBload, Op386ADDL}: Op386MOVBloadidx1, [2]Op{Op386MOVWload, Op386ADDL}: Op386MOVWloadidx1, diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go index cd9cb515c0..a3b29049df 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go @@ -149,14 +149,15 @@ func init() { gpstorexchg = regInfo{inputs: []regMask{gp, gpspsb, 0}, outputs: []regMask{gp}} cmpxchg = regInfo{inputs: []regMask{gp, ax, gp, 0}, outputs: []regMask{gp, 0}, clobbers: ax} - fp01 = regInfo{inputs: nil, outputs: fponly} - fp21 = regInfo{inputs: []regMask{fp, fp}, outputs: fponly} - fp31 = regInfo{inputs: []regMask{fp, fp, fp}, outputs: fponly} - fp21load = regInfo{inputs: []regMask{fp, gpspsb, 0}, outputs: fponly} - fpgp = regInfo{inputs: fponly, outputs: gponly} - gpfp = regInfo{inputs: gponly, outputs: fponly} - fp11 = regInfo{inputs: fponly, outputs: fponly} - fp2flags = regInfo{inputs: []regMask{fp, fp}} + fp01 = regInfo{inputs: nil, outputs: fponly} + fp21 = regInfo{inputs: []regMask{fp, fp}, outputs: fponly} + fp31 = regInfo{inputs: []regMask{fp, fp, fp}, outputs: fponly} + fp21load = regInfo{inputs: []regMask{fp, gpspsb, 0}, outputs: fponly} + fp21loadidx = regInfo{inputs: []regMask{fp, gpspsb, gpspsb, 0}, outputs: fponly} + fpgp = regInfo{inputs: fponly, outputs: gponly} + gpfp = regInfo{inputs: gponly, outputs: fponly} + fp11 = regInfo{inputs: fponly, outputs: fponly} + fp2flags = regInfo{inputs: []regMask{fp, fp}} fpload = regInfo{inputs: []regMask{gpspsb, 0}, outputs: fponly} fploadidx = regInfo{inputs: []regMask{gpspsb, gpsp, 0}, outputs: fponly} @@ -201,6 +202,23 @@ func init() { {name: "DIVSSload", argLength: 3, reg: fp21load, asm: "DIVSS", aux: "SymOff", resultInArg0: true, faultOnNilArg1: true, symEffect: "Read"}, // fp32 arg0 / tmp, tmp loaded from arg1+auxint+aux, arg2 = mem {name: "DIVSDload", argLength: 3, reg: fp21load, asm: "DIVSD", aux: "SymOff", resultInArg0: true, faultOnNilArg1: true, symEffect: "Read"}, // fp64 arg0 / tmp, tmp loaded from arg1+auxint+aux, arg2 = mem + {name: "ADDSSloadidx1", argLength: 4, reg: fp21loadidx, asm: "ADDSS", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 + tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem + {name: "ADDSSloadidx4", argLength: 4, reg: fp21loadidx, asm: "ADDSS", scale: 4, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 + tmp, tmp loaded from arg1+4*arg2+auxint+aux, arg3 = mem + {name: "ADDSDloadidx1", argLength: 4, reg: fp21loadidx, asm: "ADDSD", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 + tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem + {name: "ADDSDloadidx8", argLength: 4, reg: fp21loadidx, asm: "ADDSD", scale: 8, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 + tmp, tmp loaded from arg1+8*arg2+auxint+aux, arg3 = mem + {name: "SUBSSloadidx1", argLength: 4, reg: fp21loadidx, asm: "SUBSS", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 - tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem + {name: "SUBSSloadidx4", argLength: 4, reg: fp21loadidx, asm: "SUBSS", scale: 4, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 - tmp, tmp loaded from arg1+4*arg2+auxint+aux, arg3 = mem + {name: "SUBSDloadidx1", argLength: 4, reg: fp21loadidx, asm: "SUBSD", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 - tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem + {name: "SUBSDloadidx8", argLength: 4, reg: fp21loadidx, asm: "SUBSD", scale: 8, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 - tmp, tmp loaded from arg1+8*arg2+auxint+aux, arg3 = mem + {name: "MULSSloadidx1", argLength: 4, reg: fp21loadidx, asm: "MULSS", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 * tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem + {name: "MULSSloadidx4", argLength: 4, reg: fp21loadidx, asm: "MULSS", scale: 4, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 * tmp, tmp loaded from arg1+4*arg2+auxint+aux, arg3 = mem + {name: "MULSDloadidx1", argLength: 4, reg: fp21loadidx, asm: "MULSD", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 * tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem + {name: "MULSDloadidx8", argLength: 4, reg: fp21loadidx, asm: "MULSD", scale: 8, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 * tmp, tmp loaded from arg1+8*arg2+auxint+aux, arg3 = mem + {name: "DIVSSloadidx1", argLength: 4, reg: fp21loadidx, asm: "DIVSS", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 / tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem + {name: "DIVSSloadidx4", argLength: 4, reg: fp21loadidx, asm: "DIVSS", scale: 4, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp32 arg0 / tmp, tmp loaded from arg1+4*arg2+auxint+aux, arg3 = mem + {name: "DIVSDloadidx1", argLength: 4, reg: fp21loadidx, asm: "DIVSD", scale: 1, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 / tmp, tmp loaded from arg1+arg2+auxint+aux, arg3 = mem + {name: "DIVSDloadidx8", argLength: 4, reg: fp21loadidx, asm: "DIVSD", scale: 8, aux: "SymOff", resultInArg0: true, symEffect: "Read"}, // fp64 arg0 / tmp, tmp loaded from arg1+8*arg2+auxint+aux, arg3 = mem + // binary ops {name: "ADDQ", argLength: 2, reg: gp21sp, asm: "ADDQ", commutative: true, clobberFlags: true}, // arg0 + arg1 {name: "ADDL", argLength: 2, reg: gp21sp, asm: "ADDL", commutative: true, clobberFlags: true}, // arg0 + arg1 diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index d27682e3b3..9efa1bfcc4 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -572,6 +572,22 @@ const ( OpAMD64MULSDload OpAMD64DIVSSload OpAMD64DIVSDload + OpAMD64ADDSSloadidx1 + OpAMD64ADDSSloadidx4 + OpAMD64ADDSDloadidx1 + OpAMD64ADDSDloadidx8 + OpAMD64SUBSSloadidx1 + OpAMD64SUBSSloadidx4 + OpAMD64SUBSDloadidx1 + OpAMD64SUBSDloadidx8 + OpAMD64MULSSloadidx1 + OpAMD64MULSSloadidx4 + OpAMD64MULSDloadidx1 + OpAMD64MULSDloadidx8 + OpAMD64DIVSSloadidx1 + OpAMD64DIVSSloadidx4 + OpAMD64DIVSDloadidx1 + OpAMD64DIVSDloadidx8 OpAMD64ADDQ OpAMD64ADDL OpAMD64ADDQconst @@ -6577,6 +6593,310 @@ var opcodeTable = [...]opInfo{ }, }, { + name: "ADDSSloadidx1", + auxType: auxSymOff, + argLen: 4, + resultInArg0: true, + symEffect: SymRead, + asm: x86.AADDSS, + scale: 1, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + }, + outputs: []outputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + }, + }, + }, + { + name: "ADDSSloadidx4", + auxType: auxSymOff, + argLen: 4, + resultInArg0: true, + symEffect: SymRead, + asm: x86.AADDSS, + scale: 4, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + }, + outputs: []outputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + }, + }, + }, + { + name: "ADDSDloadidx1", + auxType: auxSymOff, + argLen: 4, + resultInArg0: true, + symEffect: SymRead, + asm: x86.AADDSD, + scale: 1, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + }, + outputs: []outputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + }, + }, + }, + { + name: "ADDSDloadidx8", + auxType: auxSymOff, + argLen: 4, + resultInArg0: true, + symEffect: SymRead, + asm: x86.AADDSD, + scale: 8, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + }, + outputs: []outputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + }, + }, + }, + { + name: "SUBSSloadidx1", + auxType: auxSymOff, + argLen: 4, + resultInArg0: true, + symEffect: SymRead, + asm: x86.ASUBSS, + scale: 1, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + }, + outputs: []outputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + }, + }, + }, + { + name: "SUBSSloadidx4", + auxType: auxSymOff, + argLen: 4, + resultInArg0: true, + symEffect: SymRead, + asm: x86.ASUBSS, + scale: 4, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + }, + outputs: []outputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + }, + }, + }, + { + name: "SUBSDloadidx1", + auxType: auxSymOff, + argLen: 4, + resultInArg0: true, + symEffect: SymRead, + asm: x86.ASUBSD, + scale: 1, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + }, + outputs: []outputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + }, + }, + }, + { + name: "SUBSDloadidx8", + auxType: auxSymOff, + argLen: 4, + resultInArg0: true, + symEffect: SymRead, + asm: x86.ASUBSD, + scale: 8, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + }, + outputs: []outputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + }, + }, + }, + { + name: "MULSSloadidx1", + auxType: auxSymOff, + argLen: 4, + resultInArg0: true, + symEffect: SymRead, + asm: x86.AMULSS, + scale: 1, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + }, + outputs: []outputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + }, + }, + }, + { + name: "MULSSloadidx4", + auxType: auxSymOff, + argLen: 4, + resultInArg0: true, + symEffect: SymRead, + asm: x86.AMULSS, + scale: 4, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + }, + outputs: []outputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + }, + }, + }, + { + name: "MULSDloadidx1", + auxType: auxSymOff, + argLen: 4, + resultInArg0: true, + symEffect: SymRead, + asm: x86.AMULSD, + scale: 1, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + }, + outputs: []outputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + }, + }, + }, + { + name: "MULSDloadidx8", + auxType: auxSymOff, + argLen: 4, + resultInArg0: true, + symEffect: SymRead, + asm: x86.AMULSD, + scale: 8, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + }, + outputs: []outputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + }, + }, + }, + { + name: "DIVSSloadidx1", + auxType: auxSymOff, + argLen: 4, + resultInArg0: true, + symEffect: SymRead, + asm: x86.ADIVSS, + scale: 1, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + }, + outputs: []outputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + }, + }, + }, + { + name: "DIVSSloadidx4", + auxType: auxSymOff, + argLen: 4, + resultInArg0: true, + symEffect: SymRead, + asm: x86.ADIVSS, + scale: 4, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + }, + outputs: []outputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + }, + }, + }, + { + name: "DIVSDloadidx1", + auxType: auxSymOff, + argLen: 4, + resultInArg0: true, + symEffect: SymRead, + asm: x86.ADIVSD, + scale: 1, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + }, + outputs: []outputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + }, + }, + }, + { + name: "DIVSDloadidx8", + auxType: auxSymOff, + argLen: 4, + resultInArg0: true, + symEffect: SymRead, + asm: x86.ADIVSD, + scale: 8, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + {1, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + {2, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB + }, + outputs: []outputInfo{ + {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 + }, + }, + }, + { name: "ADDQ", argLen: 2, commutative: true, diff --git a/src/cmd/compile/internal/ssa/prove.go b/src/cmd/compile/internal/ssa/prove.go index a8e43d0114..6c6be39d34 100644 --- a/src/cmd/compile/internal/ssa/prove.go +++ b/src/cmd/compile/internal/ssa/prove.go @@ -1051,6 +1051,11 @@ func addLocalInductiveFacts(ft *factsTable, b *Block) { // // If all of these conditions are true, then i1 < max and i1 >= min. + // To ensure this is a loop header node. + if len(b.Preds) != 2 { + return + } + for _, i1 := range b.Values { if i1.Op != OpPhi { continue @@ -1093,6 +1098,9 @@ func addLocalInductiveFacts(ft *factsTable, b *Block) { } br = negative } + if br == unknown { + continue + } tr, has := domainRelationTable[control.Op] if !has { diff --git a/src/cmd/go.mod b/src/cmd/go.mod index 274ef0d9f0..6d57ceee79 100644 --- a/src/cmd/go.mod +++ b/src/cmd/go.mod @@ -10,4 +10,5 @@ require ( golang.org/x/mod v0.3.0 golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 // indirect golang.org/x/tools v0.0.0-20200616133436-c1934b75d054 + golang.org/x/xerrors v0.0.0-20200806184451-1a77d5e9f316 // indirect ) diff --git a/src/cmd/go.sum b/src/cmd/go.sum index 30a0be03e4..3fc693e3bf 100644 --- a/src/cmd/go.sum +++ b/src/cmd/go.sum @@ -34,4 +34,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200806184451-1a77d5e9f316 h1:Jhw4VC65LaKnpq9FvcK+a8ZzrFm3D+UygvMMrhkOw70= +golang.org/x/xerrors v0.0.0-20200806184451-1a77d5e9f316/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 2316fb9991..68bad3cff1 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -1700,6 +1700,8 @@ // GOCACHE // The directory where the go command will store cached // information for reuse in future builds. +// GOMODCACHE +// The directory where the go command will store downloaded modules. // GODEBUG // Enable various debugging facilities. See 'go doc runtime' // for details. diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go index b937a6155e..e1f0521ea4 100644 --- a/src/cmd/go/internal/help/helpdoc.go +++ b/src/cmd/go/internal/help/helpdoc.go @@ -493,6 +493,8 @@ General-purpose environment variables: GOCACHE The directory where the go command will store cached information for reuse in future builds. + GOMODCACHE + The directory where the go command will store downloaded modules. GODEBUG Enable various debugging facilities. See 'go doc runtime' for details. diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go index e0bd71b40d..ddbb3b9be0 100644 --- a/src/cmd/go/internal/load/pkg.go +++ b/src/cmd/go/internal/load/pkg.go @@ -239,11 +239,25 @@ func (p *Package) setLoadPackageDataError(err error, path string, stk *ImportSta err = &NoGoError{Package: p} } + // Take only the first error from a scanner.ErrorList. PackageError only + // has room for one position, so we report the first error with a position + // instead of all of the errors without a position. + var pos string + var isScanErr bool + if scanErr, ok := err.(scanner.ErrorList); ok && len(scanErr) > 0 { + isScanErr = true // For stack push/pop below. + + scanPos := scanErr[0].Pos + scanPos.Filename = base.ShortPath(scanPos.Filename) + pos = scanPos.String() + err = errors.New(scanErr[0].Msg) + } + // Report the error on the importing package if the problem is with the import declaration // for example, if the package doesn't exist or if the import path is malformed. // On the other hand, don't include a position if the problem is with the imported package, // for example there are no Go files (NoGoError), or there's a problem in the imported - // package's source files themselves. + // package's source files themselves (scanner errors). // // TODO(matloob): Perhaps make each of those the errors in the first group // (including modload.ImportMissingError, and the corresponding @@ -254,22 +268,11 @@ func (p *Package) setLoadPackageDataError(err error, path string, stk *ImportSta // to make it easier to check for them? That would save us from having to // move the modload errors into this package to avoid a package import cycle, // and from having to export an error type for the errors produced in build. - if !isMatchErr && nogoErr != nil { + if !isMatchErr && (nogoErr != nil || isScanErr) { stk.Push(path) defer stk.Pop() } - // Take only the first error from a scanner.ErrorList. PackageError only - // has room for one position, so we report the first error with a position - // instead of all of the errors without a position. - var pos string - if scanErr, ok := err.(scanner.ErrorList); ok && len(scanErr) > 0 { - scanPos := scanErr[0].Pos - scanPos.Filename = base.ShortPath(scanPos.Filename) - pos = scanPos.String() - err = errors.New(scanErr[0].Msg) - } - p.Error = &PackageError{ ImportStack: stk.Copy(), Pos: pos, diff --git a/src/cmd/go/testdata/script/list_err_stack.txt b/src/cmd/go/testdata/script/list_err_stack.txt new file mode 100644 index 0000000000..a7be9fde6d --- /dev/null +++ b/src/cmd/go/testdata/script/list_err_stack.txt @@ -0,0 +1,27 @@ + +# golang.org/issue/40544: regression in error stacks for parse errors + +env GO111MODULE=off +cd sandbox/foo +go list -e -json . +stdout '"sandbox/foo"' +stdout '"sandbox/bar"' +stdout '"Pos": "..(/|\\\\)bar(/|\\\\)bar.go:1:1"' +stdout '"Err": "expected ''package'', found ackage"' + +env GO111MODULE=on +go list -e -json . +stdout '"sandbox/foo"' +stdout '"sandbox/bar"' +stdout '"Pos": "..(/|\\\\)bar(/|\\\\)bar.go:1:1"' +stdout '"Err": "expected ''package'', found ackage"' + +-- sandbox/go.mod -- +module sandbox + +-- sandbox/foo/foo.go -- +package pkg + +import "sandbox/bar" +-- sandbox/bar/bar.go -- +ackage bar
\ No newline at end of file diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index 679fdd7b8a..8c56af7559 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -40,7 +40,13 @@ var ( // Keep these in sync with go/format/format.go. const ( tabWidth = 8 - printerMode = printer.UseSpaces | printer.TabIndent | printer.StdFormat + printerMode = printer.UseSpaces | printer.TabIndent | printerNormalizeNumbers + + // printerNormalizeNumbers means to canonicalize number literal prefixes + // and exponents while printing. See https://golang.org/doc/go1.13#gofmt. + // + // This value is defined in go/printer specifically for go/format and cmd/gofmt. + printerNormalizeNumbers = 1 << 30 ) var ( diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index b1bfd6e96b..6197f4d99a 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -2192,17 +2192,6 @@ func ldshlibsyms(ctxt *Link, shlib string) { l.SetSymElfType(s, elf.ST_TYPE(elfsym.Info)) su.SetSize(int64(elfsym.Size)) if elfsym.Section != elf.SHN_UNDEF { - // If it's not undefined, mark the symbol as reachable - // so as to protect it from dead code elimination, - // even if there aren't any explicit references to it. - // Under the previous sym.Symbol based regime this - // wasn't necessary, but for the loader-based deadcode - // it is definitely needed. - // - // FIXME: have a more general/flexible mechanism for this? - // - l.SetAttrReachable(s, true) - // Set .File for the library that actually defines the symbol. l.SetSymPkg(s, libpath) diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go index dc7adcb5fb..8f417ec8db 100644 --- a/src/cmd/link/link_test.go +++ b/src/cmd/link/link_test.go @@ -541,6 +541,13 @@ func TestOldLink(t *testing.T) { testenv.MustHaveGoBuild(t) + // Check that the old linker exists (we don't ship it in binary releases, + // see issue 39509). + cmd := exec.Command(testenv.GoToolPath(t), "tool", "-n", "oldlink") + if err := cmd.Run(); err != nil { + t.Skip("skipping because cannot find installed cmd/oldlink binary") + } + tmpdir, err := ioutil.TempDir("", "TestOldLink") if err != nil { t.Fatal(err) @@ -553,7 +560,7 @@ func TestOldLink(t *testing.T) { t.Fatal(err) } - cmd := exec.Command(testenv.GoToolPath(t), "run", "-gcflags=all=-go115newobj=false", "-asmflags=all=-go115newobj=false", "-ldflags=-go115newobj=false", src) + cmd = exec.Command(testenv.GoToolPath(t), "run", "-gcflags=all=-go115newobj=false", "-asmflags=all=-go115newobj=false", "-ldflags=-go115newobj=false", src) if out, err := cmd.CombinedOutput(); err != nil { t.Errorf("%v: %v:\n%s", cmd.Args, err, out) } diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt index 7ee7478b19..21fc78c237 100644 --- a/src/cmd/vendor/modules.txt +++ b/src/cmd/vendor/modules.txt @@ -84,6 +84,7 @@ golang.org/x/tools/go/cfg golang.org/x/tools/go/types/objectpath golang.org/x/tools/go/types/typeutil golang.org/x/tools/internal/analysisinternal -# golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 +# golang.org/x/xerrors v0.0.0-20200806184451-1a77d5e9f316 +## explicit golang.org/x/xerrors golang.org/x/xerrors/internal diff --git a/src/compress/flate/deflate_test.go b/src/compress/flate/deflate_test.go index 4b1ed549a4..49a0345fd1 100644 --- a/src/compress/flate/deflate_test.go +++ b/src/compress/flate/deflate_test.go @@ -512,33 +512,57 @@ func TestWriterReset(t *testing.T) { t.Errorf("level %d Writer not reset after Reset", level) } } - testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriter(w, NoCompression) }) - testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriter(w, DefaultCompression) }) - testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriter(w, BestCompression) }) - dict := []byte("we are the world") - testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriterDict(w, NoCompression, dict) }) - testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriterDict(w, DefaultCompression, dict) }) - testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriterDict(w, BestCompression, dict) }) + + levels := []int{0, 1, 2, 5, 9} + for _, level := range levels { + t.Run(fmt.Sprint(level), func(t *testing.T) { + testResetOutput(t, level, nil) + }) + } + + t.Run("dict", func(t *testing.T) { + for _, level := range levels { + t.Run(fmt.Sprint(level), func(t *testing.T) { + testResetOutput(t, level, nil) + }) + } + }) } -func testResetOutput(t *testing.T, newWriter func(w io.Writer) (*Writer, error)) { +func testResetOutput(t *testing.T, level int, dict []byte) { + writeData := func(w *Writer) { + msg := []byte("now is the time for all good gophers") + w.Write(msg) + w.Flush() + + hello := []byte("hello world") + for i := 0; i < 1024; i++ { + w.Write(hello) + } + + fill := bytes.Repeat([]byte("x"), 65000) + w.Write(fill) + } + buf := new(bytes.Buffer) - w, err := newWriter(buf) + var w *Writer + var err error + if dict == nil { + w, err = NewWriter(buf, level) + } else { + w, err = NewWriterDict(buf, level, dict) + } if err != nil { t.Fatalf("NewWriter: %v", err) } - b := []byte("hello world") - for i := 0; i < 1024; i++ { - w.Write(b) - } + + writeData(w) w.Close() out1 := buf.Bytes() buf2 := new(bytes.Buffer) w.Reset(buf2) - for i := 0; i < 1024; i++ { - w.Write(b) - } + writeData(w) w.Close() out2 := buf2.Bytes() diff --git a/src/compress/flate/deflatefast.go b/src/compress/flate/deflatefast.go index 08298b76bb..24f8be9d5d 100644 --- a/src/compress/flate/deflatefast.go +++ b/src/compress/flate/deflatefast.go @@ -4,6 +4,8 @@ package flate +import "math" + // This encoding algorithm, which prioritizes speed over output size, is // based on Snappy's LZ77-style encoder: github.com/golang/snappy @@ -12,6 +14,13 @@ const ( tableSize = 1 << tableBits // Size of the table. tableMask = tableSize - 1 // Mask for table indices. Redundant, but can eliminate bounds checks. tableShift = 32 - tableBits // Right-shift to get the tableBits most significant bits of a uint32. + + // Reset the buffer offset when reaching this. + // Offsets are stored between blocks as int32 values. + // Since the offset we are checking against is at the beginning + // of the buffer, we need to subtract the current and input + // buffer to not risk overflowing the int32. + bufferReset = math.MaxInt32 - maxStoreBlockSize*2 ) func load32(b []byte, i int32) uint32 { @@ -59,8 +68,8 @@ func newDeflateFast() *deflateFast { // to dst and returns the result. func (e *deflateFast) encode(dst []token, src []byte) []token { // Ensure that e.cur doesn't wrap. - if e.cur > 1<<30 { - e.resetAll() + if e.cur >= bufferReset { + e.shiftOffsets() } // This check isn't in the Snappy implementation, but there, the caller @@ -264,22 +273,32 @@ func (e *deflateFast) reset() { e.cur += maxMatchOffset // Protect against e.cur wraparound. - if e.cur > 1<<30 { - e.resetAll() + if e.cur >= bufferReset { + e.shiftOffsets() } } -// resetAll resets the deflateFast struct and is only called in rare -// situations to prevent integer overflow. It manually resets each field -// to avoid causing large stack growth. +// shiftOffsets will shift down all match offset. +// This is only called in rare situations to prevent integer overflow. // -// See https://golang.org/issue/18636. -func (e *deflateFast) resetAll() { - // This is equivalent to: - // *e = deflateFast{cur: maxStoreBlockSize, prev: e.prev[:0]} - e.cur = maxStoreBlockSize - e.prev = e.prev[:0] - for i := range e.table { - e.table[i] = tableEntry{} +// See https://golang.org/issue/18636 and https://github.com/golang/go/issues/34121. +func (e *deflateFast) shiftOffsets() { + if len(e.prev) == 0 { + // We have no history; just clear the table. + for i := range e.table[:] { + e.table[i] = tableEntry{} + } + e.cur = maxMatchOffset + return + } + + // Shift down everything in the table that isn't already too far away. + for i := range e.table[:] { + v := e.table[i].offset - e.cur + maxMatchOffset + if v < 0 { + v = 0 + } + e.table[i].offset = v } + e.cur = maxMatchOffset } diff --git a/src/compress/flate/huffman_bit_writer.go b/src/compress/flate/huffman_bit_writer.go index 3e19061f8b..f111f9f592 100644 --- a/src/compress/flate/huffman_bit_writer.go +++ b/src/compress/flate/huffman_bit_writer.go @@ -634,6 +634,7 @@ func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte) { w.literalFreq[endBlockMarker] = 1 const numLiterals = endBlockMarker + 1 + w.offsetFreq[0] = 1 const numOffsets = 1 w.literalEncoding.generate(w.literalFreq, 15) diff --git a/src/compress/flate/writer_test.go b/src/compress/flate/writer_test.go index c4d36aa37e..881cb71cc3 100644 --- a/src/compress/flate/writer_test.go +++ b/src/compress/flate/writer_test.go @@ -173,3 +173,66 @@ func testDeterministic(i int, t *testing.T) { t.Errorf("level %d did not produce deterministic result, result mismatch, len(a) = %d, len(b) = %d", i, len(b1b), len(b2b)) } } + +// TestDeflateFast_Reset will test that encoding is consistent +// across a warparound of the table offset. +// See https://github.com/golang/go/issues/34121 +func TestDeflateFast_Reset(t *testing.T) { + buf := new(bytes.Buffer) + n := 65536 + + for i := 0; i < n; i++ { + fmt.Fprintf(buf, "asdfasdfasdfasdf%d%dfghfgujyut%dyutyu\n", i, i, i) + } + // This is specific to level 1. + const level = 1 + in := buf.Bytes() + offset := 1 + if testing.Short() { + offset = 256 + } + + // We do an encode with a clean buffer to compare. + var want bytes.Buffer + w, err := NewWriter(&want, level) + if err != nil { + t.Fatalf("NewWriter: level %d: %v", level, err) + } + + // Output written 3 times. + w.Write(in) + w.Write(in) + w.Write(in) + w.Close() + + for ; offset <= 256; offset *= 2 { + w, err := NewWriter(ioutil.Discard, level) + if err != nil { + t.Fatalf("NewWriter: level %d: %v", level, err) + } + + // Reset until we are right before the wraparound. + // Each reset adds maxMatchOffset to the offset. + for i := 0; i < (bufferReset-len(in)-offset-maxMatchOffset)/maxMatchOffset; i++ { + // skip ahead to where we are close to wrap around... + w.d.reset(nil) + } + var got bytes.Buffer + w.Reset(&got) + + // Write 3 times, close. + for i := 0; i < 3; i++ { + _, err = w.Write(in) + if err != nil { + t.Fatal(err) + } + } + err = w.Close() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(got.Bytes(), want.Bytes()) { + t.Fatalf("output did not match at wraparound, len(want) = %d, len(got) = %d", want.Len(), got.Len()) + } + } +} diff --git a/src/crypto/ed25519/ed25519.go b/src/crypto/ed25519/ed25519.go index 5766970f82..6f59bb5cff 100644 --- a/src/crypto/ed25519/ed25519.go +++ b/src/crypto/ed25519/ed25519.go @@ -154,7 +154,7 @@ func Sign(privateKey PrivateKey, message []byte) []byte { return signature } -func signGeneric(signature, privateKey, message []byte) { +func sign(signature, privateKey, message []byte) { if l := len(privateKey); l != PrivateKeySize { panic("ed25519: bad private key length: " + strconv.Itoa(l)) } @@ -201,10 +201,6 @@ func signGeneric(signature, privateKey, message []byte) { // Verify reports whether sig is a valid signature of message by publicKey. It // will panic if len(publicKey) is not PublicKeySize. func Verify(publicKey PublicKey, message, sig []byte) bool { - return verify(publicKey, message, sig) -} - -func verifyGeneric(publicKey PublicKey, message, sig []byte) bool { if l := len(publicKey); l != PublicKeySize { panic("ed25519: bad public key length: " + strconv.Itoa(l)) } diff --git a/src/crypto/ed25519/ed25519_noasm.go b/src/crypto/ed25519/ed25519_noasm.go deleted file mode 100644 index caa84f74fb..0000000000 --- a/src/crypto/ed25519/ed25519_noasm.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2020 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. - -// +build !s390x - -package ed25519 - -func sign(signature, privateKey, message []byte) { - signGeneric(signature, privateKey, message) -} - -func verify(publicKey PublicKey, message, sig []byte) bool { - return verifyGeneric(publicKey, message, sig) -} diff --git a/src/crypto/ed25519/ed25519_s390x.go b/src/crypto/ed25519/ed25519_s390x.go deleted file mode 100644 index c8627a0652..0000000000 --- a/src/crypto/ed25519/ed25519_s390x.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ed25519 - -import ( - "internal/cpu" - "strconv" -) - -//go:noescape -func kdsaSign(message, signature, privateKey []byte) bool - -//go:noescape -func kdsaVerify(message, signature, publicKey []byte) bool - -// sign does a check to see if hardware has Edwards Curve instruction available. -// If it does, use the hardware implementation. Otherwise, use the generic version. -func sign(signature, privateKey, message []byte) { - if cpu.S390X.HasEDDSA { - if l := len(privateKey); l != PrivateKeySize { - panic("ed25519: bad private key length: " + strconv.Itoa(l)) - } - - ret := kdsaSign(message, signature, privateKey[:32]) - if !ret { - panic("ed25519: kdsa sign has a failure") - } - return - } - signGeneric(signature, privateKey, message) -} - -// verify does a check to see if hardware has Edwards Curve instruction available. -// If it does, use the hardware implementation for eddsa verfication. Otherwise, the generic -// version is used -func verify(publicKey PublicKey, message, sig []byte) bool { - if cpu.S390X.HasEDDSA { - if l := len(publicKey); l != PublicKeySize { - panic("ed25519: bad public key length: " + strconv.Itoa(l)) - } - - if len(sig) != SignatureSize || sig[63]&224 != 0 { - return false - } - - return kdsaVerify(message, sig, publicKey) - } - return verifyGeneric(publicKey, message, sig) -} diff --git a/src/crypto/ed25519/ed25519_s390x.s b/src/crypto/ed25519/ed25519_s390x.s deleted file mode 100644 index 1c77b51a78..0000000000 --- a/src/crypto/ed25519/ed25519_s390x.s +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2020 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. - -#include "textflag.h" - -// func kdsaSign(message, signature, privateKey []byte) bool -TEXT ·kdsaSign(SB), $4096-73 - // The kdsa instruction takes function code, - // buffer's location, message's location and message len - // as parameters. Out of those, the function code and buffer's location - // should be placed in R0 and R1 respectively. The message's location - // and message length should be placed in an even-odd register pair. (e.g: R2 and R3) - - // The content of parameter block(buffer) looks like the following: - // Signature R, Signature S and Private Key all take 32 bytes. - // In the signing case, the signatures(R and S) will be generated by - // the signing instruction and get placed in the locations shown in the parameter block. - // 0 +---------------+ - // | Signature(R) | - // 32 +---------------+ - // | Signature(S) | - // 64 +---------------+ - // | Private Key | - // 96 +---------------+ - // | Reserved | - // 112+---------------+ - // | | - // | ... | - // | | - // 4088 +---------------+ - - // The following code section setups the buffer from stack: - // Get the address of the buffer stack variable. - MOVD $buffer-4096(SP), R1 - - // Zero the buffer. - MOVD R1, R2 - MOVD $(4096/256), R0 // number of 256 byte chunks to clear - -clear: - XC $256, (R2), (R2) - MOVD $256(R2), R2 - BRCTG R0, clear - - MOVD $40, R0 // EDDSA-25519 sign has a function code of 40 - LMG message+0(FP), R2, R3 // R2=base R3=len - LMG signature+24(FP), R4, R5 // R4=base R5=len - LMG privateKey+48(FP), R6, R7 // R6=base R7=len - - // Checks the length of signature and private key - CMPBNE R5, $64, panic - CMPBNE R7, $32, panic - - // The instruction uses RFC 8032's private key, which is the first 32 bytes - // of the private key in this package. So we copy that into the buffer. - MVC $32, (R6), 64(R1) - -loop: - WORD $0xB93A0002 // The KDSA instruction - BVS loop // The instruction is exectued by hardware and can be interrupted. This does a retry when that happens. - BNE error - -success: - // The signatures generated are in big-endian form, so we - // need to reverse the bytes of Signature(R) and Signature(S) in the buffers to transform - // them from big-endian to little-endian. - - // Transform Signature(R) from big endian to little endian and copy into the signature - MVCIN $32, 31(R1), (R4) - - // Transform Signature(S) from big endian to little endian and copy into the signature - MVCIN $32, 63(R1), 32(R4) - - MOVB $1, ret+72(FP) - RET - -error: - // return false - MOVB $0, ret+72(FP) - RET - -panic: - UNDEF - -// func kdsaVerify(message, signature, publicKey []byte) bool -TEXT ·kdsaVerify(SB), $4096-73 - // The kdsa instruction takes function code, - // buffer's location, message's location and message len - // as parameters. Out of those, the function code and buffer's location - // should be placed in R0 and R1 respectively. The message's location - // and message length should be placed in an even-odd register pair. (e.g: R2 and R3) - - // The parameter block(buffer) is similar to that of signing, except that - // we use public key for verification, and Signatures(R and S) are provided - // as input parameters to the parameter block. - // 0 +---------------+ - // | Signature(R) | - // 32 +---------------+ - // | Signature(S) | - // 64 +---------------+ - // | Public Key | - // 96 +---------------+ - // | Reserved | - // 112+---------------+ - // | | - // | ... | - // | | - // 4088 +---------------+ - - // The following code section setups the buffer from stack: - // Get the address of the buffer stack variable. - MOVD $buffer-4096(SP), R1 - - // Zero the buffer. - MOVD R1, R2 - MOVD $(4096/256), R0 // number of 256 byte chunks to clear - -clear: - XC $256, (R2), (R2) - MOVD $256(R2), R2 - BRCTG R0, clear - - MOVD $32, R0 // EDDSA-25519 verify has a function code of 32 - LMG message+0(FP), R2, R3 // R2=base R3=len - LMG signature+24(FP), R4, R5 // R4=base R5=len - LMG publicKey+48(FP), R6, R7 // R6=base R7=len - - // Checks the length of public key and signature - CMPBNE R5, $64, panic - CMPBNE R7, $32, panic - -verify: - // The instruction needs Signature(R), Signature(S) and public key - // to be in big-endian form during computation. Therefore, - // we do the transformation (from little endian to big endian) and copy those into the buffer. - - // Transform Signature(R) from little endian to big endian and copy into the buffer - MVCIN $32, 31(R4), (R1) - - // Transform Signature(S) from little endian to big endian and copy into the buffer - MVCIN $32, 63(R4), 32(R1) - - // Transform Public Key from little endian to big endian and copy into the buffer - MVCIN $32, 31(R6), 64(R1) - -verifyLoop: - WORD $0xB93A0002 // KDSA instruction - BVS verifyLoop // Retry upon hardware interrupt - BNE error - -success: - MOVB $1, ret+72(FP) - RET - -error: - MOVB $0, ret+72(FP) - RET - -panic: - UNDEF diff --git a/src/crypto/ed25519/ed25519_test.go b/src/crypto/ed25519/ed25519_test.go index f77d463721..adb09e409a 100644 --- a/src/crypto/ed25519/ed25519_test.go +++ b/src/crypto/ed25519/ed25519_test.go @@ -26,14 +26,6 @@ func (zeroReader) Read(buf []byte) (int, error) { return len(buf), nil } -// signGenericWrapper is identical to Sign except that it unconditionally calls signGeneric directly -// rather than going through the sign function that might call assembly code. -func signGenericWrapper(privateKey PrivateKey, msg []byte) []byte { - sig := make([]byte, SignatureSize) - signGeneric(sig, privateKey, msg) - return sig -} - func TestUnmarshalMarshal(t *testing.T) { pub, _, _ := GenerateKey(rand.Reader) @@ -53,33 +45,22 @@ func TestUnmarshalMarshal(t *testing.T) { } func TestSignVerify(t *testing.T) { - t.Run("Generic", func(t *testing.T) { testSignVerify(t, signGenericWrapper, verifyGeneric) }) - t.Run("Native", func(t *testing.T) { testSignVerify(t, Sign, Verify) }) -} - -func testSignVerify(t *testing.T, signImpl func(privateKey PrivateKey, message []byte) []byte, - verifyImpl func(publicKey PublicKey, message, sig []byte) bool) { var zero zeroReader public, private, _ := GenerateKey(zero) message := []byte("test message") - sig := signImpl(private, message) - if !verifyImpl(public, message, sig) { + sig := Sign(private, message) + if !Verify(public, message, sig) { t.Errorf("valid signature rejected") } wrongMessage := []byte("wrong message") - if verifyImpl(public, wrongMessage, sig) { + if Verify(public, wrongMessage, sig) { t.Errorf("signature of different message accepted") } } func TestCryptoSigner(t *testing.T) { - t.Run("Generic", func(t *testing.T) { testCryptoSigner(t, verifyGeneric) }) - t.Run("Native", func(t *testing.T) { testCryptoSigner(t, Verify) }) -} - -func testCryptoSigner(t *testing.T, verifyImpl func(publicKey PublicKey, message, sig []byte) bool) { var zero zeroReader public, private, _ := GenerateKey(zero) @@ -102,7 +83,7 @@ func testCryptoSigner(t *testing.T, verifyImpl func(publicKey PublicKey, message t.Fatalf("error from Sign(): %s", err) } - if !verifyImpl(public, message, signature) { + if !Verify(public, message, signature) { t.Errorf("Verify failed on signature from Sign()") } } @@ -130,12 +111,6 @@ func TestEqual(t *testing.T) { } func TestGolden(t *testing.T) { - t.Run("Generic", func(t *testing.T) { testGolden(t, signGenericWrapper, verifyGeneric) }) - t.Run("Native", func(t *testing.T) { testGolden(t, Sign, Verify) }) -} - -func testGolden(t *testing.T, signImpl func(privateKey PrivateKey, message []byte) []byte, - verifyImpl func(publicKey PublicKey, message, sig []byte) bool) { // sign.input.gz is a selection of test cases from // https://ed25519.cr.yp.to/python/sign.input testDataZ, err := os.Open("testdata/sign.input.gz") @@ -177,12 +152,12 @@ func testGolden(t *testing.T, signImpl func(privateKey PrivateKey, message []byt copy(priv[:], privBytes) copy(priv[32:], pubKey) - sig2 := signImpl(priv[:], msg) + sig2 := Sign(priv[:], msg) if !bytes.Equal(sig, sig2[:]) { t.Errorf("different signature result on line %d: %x vs %x", lineNo, sig, sig2) } - if !verifyImpl(pubKey, msg, sig2) { + if !Verify(pubKey, msg, sig2) { t.Errorf("signature failed to verify on line %d", lineNo) } @@ -206,11 +181,6 @@ func testGolden(t *testing.T, signImpl func(privateKey PrivateKey, message []byt } func TestMalleability(t *testing.T) { - t.Run("Generic", func(t *testing.T) { testMalleability(t, verifyGeneric) }) - t.Run("Native", func(t *testing.T) { testMalleability(t, Verify) }) -} - -func testMalleability(t *testing.T, verifyImpl func(publicKey PublicKey, message, sig []byte) bool) { // https://tools.ietf.org/html/rfc8032#section-5.1.7 adds an additional test // that s be in [0, order). This prevents someone from adding a multiple of // order to s and obtaining a second valid signature for the same message. @@ -229,7 +199,7 @@ func testMalleability(t *testing.T, verifyImpl func(publicKey PublicKey, message 0xb1, 0x08, 0xc3, 0xbd, 0xae, 0x36, 0x9e, 0xf5, 0x49, 0xfa, } - if verifyImpl(publicKey, msg, sig) { + if Verify(publicKey, msg, sig) { t.Fatal("non-canonical signature accepted") } } diff --git a/src/crypto/x509/internal/macOS/corefoundation.go b/src/crypto/x509/internal/macos/corefoundation.go index 359694fabf..359694fabf 100644 --- a/src/crypto/x509/internal/macOS/corefoundation.go +++ b/src/crypto/x509/internal/macos/corefoundation.go diff --git a/src/crypto/x509/internal/macOS/corefoundation.s b/src/crypto/x509/internal/macos/corefoundation.s index 8f6be47e4b..8f6be47e4b 100644 --- a/src/crypto/x509/internal/macOS/corefoundation.s +++ b/src/crypto/x509/internal/macos/corefoundation.s diff --git a/src/crypto/x509/internal/macOS/security.go b/src/crypto/x509/internal/macos/security.go index 64fe206390..64fe206390 100644 --- a/src/crypto/x509/internal/macOS/security.go +++ b/src/crypto/x509/internal/macos/security.go diff --git a/src/crypto/x509/internal/macOS/security.s b/src/crypto/x509/internal/macos/security.s index 1630c55bab..1630c55bab 100644 --- a/src/crypto/x509/internal/macOS/security.s +++ b/src/crypto/x509/internal/macos/security.s diff --git a/src/crypto/x509/root_darwin_amd64.go b/src/crypto/x509/root_darwin_amd64.go index ccc37b8ec5..ce88de025e 100644 --- a/src/crypto/x509/root_darwin_amd64.go +++ b/src/crypto/x509/root_darwin_amd64.go @@ -8,7 +8,7 @@ package x509 import ( "bytes" - "crypto/x509/internal/macOS" + macOS "crypto/x509/internal/macos" "fmt" "os" "strings" diff --git a/src/crypto/x509/root_windows.go b/src/crypto/x509/root_windows.go index 34d585318d..1e0f3acb67 100644 --- a/src/crypto/x509/root_windows.go +++ b/src/crypto/x509/root_windows.go @@ -88,6 +88,9 @@ func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) e switch status { case syscall.CERT_TRUST_IS_NOT_TIME_VALID: return CertificateInvalidError{c, Expired, ""} + case syscall.CERT_TRUST_IS_NOT_VALID_FOR_USAGE: + return CertificateInvalidError{c, IncompatibleUsage, ""} + // TODO(filippo): surface more error statuses. default: return UnknownAuthorityError{c, nil, nil} } @@ -138,11 +141,19 @@ func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContex return nil } +// windowsExtKeyUsageOIDs are the C NUL-terminated string representations of the +// OIDs for use with the Windows API. +var windowsExtKeyUsageOIDs = make(map[ExtKeyUsage][]byte, len(extKeyUsageOIDs)) + +func init() { + for _, eku := range extKeyUsageOIDs { + windowsExtKeyUsageOIDs[eku.extKeyUsage] = []byte(eku.oid.String() + "\x00") + } +} + // systemVerify is like Verify, except that it uses CryptoAPI calls // to build certificate chains and verify them. func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { - hasDNSName := opts != nil && len(opts.DNSName) > 0 - storeCtx, err := createStoreContext(c, opts) if err != nil { return nil, err @@ -152,17 +163,26 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate para := new(syscall.CertChainPara) para.Size = uint32(unsafe.Sizeof(*para)) - // If there's a DNSName set in opts, assume we're verifying - // a certificate from a TLS server. - if hasDNSName { - oids := []*byte{ - &syscall.OID_PKIX_KP_SERVER_AUTH[0], - // Both IE and Chrome allow certificates with - // Server Gated Crypto as well. Some certificates - // in the wild require them. - &syscall.OID_SERVER_GATED_CRYPTO[0], - &syscall.OID_SGC_NETSCAPE[0], + keyUsages := opts.KeyUsages + if len(keyUsages) == 0 { + keyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth} + } + oids := make([]*byte, 0, len(keyUsages)) + for _, eku := range keyUsages { + if eku == ExtKeyUsageAny { + oids = nil + break + } + if oid, ok := windowsExtKeyUsageOIDs[eku]; ok { + oids = append(oids, &oid[0]) } + // Like the standard verifier, accept SGC EKUs as equivalent to ServerAuth. + if eku == ExtKeyUsageServerAuth { + oids = append(oids, &syscall.OID_SERVER_GATED_CRYPTO[0]) + oids = append(oids, &syscall.OID_SGC_NETSCAPE[0]) + } + } + if oids != nil { para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR para.RequestedUsage.Usage.Length = uint32(len(oids)) para.RequestedUsage.Usage.UsageIdentifiers = &oids[0] @@ -208,7 +228,7 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate return nil, err } - if hasDNSName { + if opts != nil && len(opts.DNSName) > 0 { err = checkChainSSLServerPolicy(c, chainCtx, opts) if err != nil { return nil, err diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go index d47f8382ae..50f4d4ac80 100644 --- a/src/crypto/x509/verify.go +++ b/src/crypto/x509/verify.go @@ -199,7 +199,7 @@ type VerifyOptions struct { IsBoring func(*Certificate) bool // DNSName, if set, is checked against the leaf certificate with - // Certificate.VerifyHostname. + // Certificate.VerifyHostname or the platform verifier. DNSName string // Intermediates is an optional pool of certificates that are not trust @@ -214,20 +214,16 @@ type VerifyOptions struct { // chain. If zero, the current time is used. CurrentTime time.Time - // KeyUsage specifies which Extended Key Usage values are acceptable. A leaf - // certificate is accepted if it contains any of the listed values. An empty - // list means ExtKeyUsageServerAuth. To accept any key usage, include - // ExtKeyUsageAny. - // - // Certificate chains are required to nest these extended key usage values. - // (This matches the Windows CryptoAPI behavior, but not the spec.) + // KeyUsages specifies which Extended Key Usage values are acceptable. A + // chain is accepted if it allows any of the listed values. An empty list + // means ExtKeyUsageServerAuth. To accept any key usage, include ExtKeyUsageAny. KeyUsages []ExtKeyUsage // MaxConstraintComparisions is the maximum number of comparisons to // perform when checking a given certificate's name constraints. If // zero, a sensible default is used. This limit prevents pathological // certificates from consuming excessive amounts of CPU time when - // validating. + // validating. It does not apply to the platform verifier. MaxConstraintComparisions int } @@ -747,8 +743,9 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V // needed. If successful, it returns one or more chains where the first // element of the chain is c and the last element is from opts.Roots. // -// If opts.Roots is nil and system roots are unavailable the returned error -// will be of type SystemRootsError. +// If opts.Roots is nil, the platform verifier might be used, and +// verification details might differ from what is described below. If system +// roots are unavailable the returned error will be of type SystemRootsError. // // Name constraints in the intermediates will be applied to all names claimed // in the chain, not just opts.DNSName. Thus it is invalid for a leaf to claim @@ -762,9 +759,10 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V // it indicates that at least one additional label must be prepended to // the constrained name to be considered valid. // -// Extended Key Usage values are enforced down a chain, so an intermediate or -// root that enumerates EKUs prevents a leaf from asserting an EKU not in that -// list. +// Extended Key Usage values are enforced nested down a chain, so an intermediate +// or root that enumerates EKUs prevents a leaf from asserting an EKU not in that +// list. (While this is not specified, it is common practice in order to limit +// the types of certificates a CA can issue.) // // WARNING: this function doesn't do any revocation checking. func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) { diff --git a/src/crypto/x509/verify_test.go b/src/crypto/x509/verify_test.go index 650b2d2fc6..76d1ab9a47 100644 --- a/src/crypto/x509/verify_test.go +++ b/src/crypto/x509/verify_test.go @@ -21,34 +21,24 @@ import ( ) type verifyTest struct { - leaf string - intermediates []string - roots []string - currentTime int64 - dnsName string - systemSkip bool - keyUsages []ExtKeyUsage - testSystemRootsError bool - sha2 bool - ignoreCN bool - - errorCallback func(*testing.T, int, error) bool + name string + leaf string + intermediates []string + roots []string + currentTime int64 + dnsName string + systemSkip bool + systemLax bool + keyUsages []ExtKeyUsage + ignoreCN bool + + errorCallback func(*testing.T, error) expectedChains [][]string } var verifyTests = []verifyTest{ { - leaf: googleLeaf, - intermediates: []string{giag2Intermediate}, - currentTime: 1395785200, - dnsName: "www.google.com", - testSystemRootsError: true, - - // Without any roots specified we should get a system roots - // error. - errorCallback: expectSystemRootsError, - }, - { + name: "Valid", leaf: googleLeaf, intermediates: []string{giag2Intermediate}, roots: []string{geoTrustRoot}, @@ -60,6 +50,7 @@ var verifyTests = []verifyTest{ }, }, { + name: "MixedCase", leaf: googleLeaf, intermediates: []string{giag2Intermediate}, roots: []string{geoTrustRoot}, @@ -71,6 +62,7 @@ var verifyTests = []verifyTest{ }, }, { + name: "HostnameMismatch", leaf: googleLeaf, intermediates: []string{giag2Intermediate}, roots: []string{geoTrustRoot}, @@ -80,6 +72,7 @@ var verifyTests = []verifyTest{ errorCallback: expectHostnameError("certificate is valid for"), }, { + name: "IPMissing", leaf: googleLeaf, intermediates: []string{giag2Intermediate}, roots: []string{geoTrustRoot}, @@ -89,6 +82,7 @@ var verifyTests = []verifyTest{ errorCallback: expectHostnameError("doesn't contain any IP SANs"), }, { + name: "Expired", leaf: googleLeaf, intermediates: []string{giag2Intermediate}, roots: []string{geoTrustRoot}, @@ -98,6 +92,7 @@ var verifyTests = []verifyTest{ errorCallback: expectExpired, }, { + name: "MissingIntermediate", leaf: googleLeaf, roots: []string{geoTrustRoot}, currentTime: 1395785200, @@ -109,6 +104,7 @@ var verifyTests = []verifyTest{ errorCallback: expectAuthorityUnknown, }, { + name: "RootInIntermediates", leaf: googleLeaf, intermediates: []string{geoTrustRoot, giag2Intermediate}, roots: []string{geoTrustRoot}, @@ -119,31 +115,50 @@ var verifyTests = []verifyTest{ {"Google", "Google Internet Authority", "GeoTrust"}, }, // CAPI doesn't build the chain with the duplicated GeoTrust - // entry so the results don't match. Thus we skip this test - // until that's fixed. + // entry so the results don't match. + systemLax: true, + }, + { + name: "dnssec-exp", + leaf: dnssecExpLeaf, + intermediates: []string{startComIntermediate}, + roots: []string{startComRoot}, + currentTime: 1302726541, + + // The StartCom root is not trusted by Windows when the default + // ServerAuth EKU is requested. systemSkip: true, + + expectedChains: [][]string{ + {"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"}, + }, }, { + name: "dnssec-exp/AnyEKU", leaf: dnssecExpLeaf, intermediates: []string{startComIntermediate}, roots: []string{startComRoot}, currentTime: 1302726541, + keyUsages: []ExtKeyUsage{ExtKeyUsageAny}, expectedChains: [][]string{ {"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"}, }, }, { + name: "dnssec-exp/RootInIntermediates", leaf: dnssecExpLeaf, intermediates: []string{startComIntermediate, startComRoot}, roots: []string{startComRoot}, currentTime: 1302726541, + systemSkip: true, // see dnssec-exp test expectedChains: [][]string{ {"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"}, }, }, { + name: "InvalidHash", leaf: googleLeafWithInvalidHash, intermediates: []string{giag2Intermediate}, roots: []string{geoTrustRoot}, @@ -152,50 +167,52 @@ var verifyTests = []verifyTest{ // The specific error message may not occur when using system // verification. - systemSkip: true, + systemLax: true, errorCallback: expectHashError, }, + // EKULeaf tests use an unconstrained chain leading to a leaf certificate + // with an E-mail Protection EKU but not a Server Auth one, checking that + // the EKUs on the leaf are enforced. { - // The default configuration should reject an S/MIME chain. - leaf: smimeLeaf, - roots: []string{smimeIntermediate}, - currentTime: 1339436154, + name: "EKULeaf", + leaf: smimeLeaf, + intermediates: []string{smimeIntermediate}, + roots: []string{smimeRoot}, + currentTime: 1594673418, - // Key usage not implemented for Windows yet. - systemSkip: true, errorCallback: expectUsageError, }, { - leaf: smimeLeaf, - roots: []string{smimeIntermediate}, - currentTime: 1339436154, - keyUsages: []ExtKeyUsage{ExtKeyUsageServerAuth}, + name: "EKULeafExplicit", + leaf: smimeLeaf, + intermediates: []string{smimeIntermediate}, + roots: []string{smimeRoot}, + currentTime: 1594673418, + keyUsages: []ExtKeyUsage{ExtKeyUsageServerAuth}, - // Key usage not implemented for Windows yet. - systemSkip: true, errorCallback: expectUsageError, }, { - leaf: smimeLeaf, - roots: []string{smimeIntermediate}, - currentTime: 1339436154, - keyUsages: []ExtKeyUsage{ExtKeyUsageEmailProtection}, + name: "EKULeafValid", + leaf: smimeLeaf, + intermediates: []string{smimeIntermediate}, + roots: []string{smimeRoot}, + currentTime: 1594673418, + keyUsages: []ExtKeyUsage{ExtKeyUsageEmailProtection}, - // Key usage not implemented for Windows yet. - systemSkip: true, expectedChains: [][]string{ - {"Ryan Hurst", "GlobalSign PersonalSign 2 CA - G2"}, + {"CORPORATIVO FICTICIO ACTIVO", "EAEko Herri Administrazioen CA - CA AAPP Vascas (2)", "IZENPE S.A."}, }, }, { + name: "SGCIntermediate", leaf: megaLeaf, intermediates: []string{comodoIntermediate1}, roots: []string{comodoRoot}, currentTime: 1360431182, - // CryptoAPI can find alternative validation paths so we don't - // perform this test with system validation. - systemSkip: true, + // CryptoAPI can find alternative validation paths. + systemLax: true, expectedChains: [][]string{ {"mega.co.nz", "EssentialSSL CA", "COMODO Certification Authority"}, }, @@ -203,6 +220,7 @@ var verifyTests = []verifyTest{ { // Check that a name constrained intermediate works even when // it lists multiple constraints. + name: "MultipleConstraints", leaf: nameConstraintsLeaf, intermediates: []string{nameConstraintsIntermediate1, nameConstraintsIntermediate2}, roots: []string{globalSignRoot}, @@ -221,17 +239,16 @@ var verifyTests = []verifyTest{ { // Check that SHA-384 intermediates (which are popping up) // work. + name: "SHA-384", leaf: moipLeafCert, intermediates: []string{comodoIntermediateSHA384, comodoRSAAuthority}, roots: []string{addTrustRoot}, currentTime: 1397502195, dnsName: "api.moip.com.br", - // CryptoAPI can find alternative validation paths so we don't - // perform this test with system validation. - systemSkip: true, + // CryptoAPI can find alternative validation paths. + systemLax: true, - sha2: true, expectedChains: [][]string{ { "api.moip.com.br", @@ -244,11 +261,12 @@ var verifyTests = []verifyTest{ { // Putting a certificate as a root directly should work as a // way of saying “exactly this”. + name: "LeafInRoots", leaf: selfSigned, roots: []string{selfSigned}, currentTime: 1471624472, dnsName: "foo.example", - systemSkip: true, + systemSkip: true, // does not chain to a system root expectedChains: [][]string{ {"Acme Co"}, @@ -257,11 +275,12 @@ var verifyTests = []verifyTest{ { // Putting a certificate as a root directly should not skip // other checks however. + name: "LeafInRootsInvalid", leaf: selfSigned, roots: []string{selfSigned}, currentTime: 1471624472, dnsName: "notfoo.example", - systemSkip: true, + systemSkip: true, // does not chain to a system root errorCallback: expectHostnameError("certificate is valid for"), }, @@ -269,87 +288,95 @@ var verifyTests = []verifyTest{ // The issuer name in the leaf doesn't exactly match the // subject name in the root. Go does not perform // canonicalization and so should reject this. See issue 14955. + name: "IssuerSubjectMismatch", leaf: issuerSubjectMatchLeaf, roots: []string{issuerSubjectMatchRoot}, currentTime: 1475787715, - systemSkip: true, + systemSkip: true, // does not chain to a system root errorCallback: expectSubjectIssuerMismatcthError, }, { // An X.509 v1 certificate should not be accepted as an // intermediate. + name: "X509v1Intermediate", leaf: x509v1TestLeaf, intermediates: []string{x509v1TestIntermediate}, roots: []string{x509v1TestRoot}, currentTime: 1481753183, - systemSkip: true, + systemSkip: true, // does not chain to a system root errorCallback: expectNotAuthorizedError, }, { // If any SAN extension is present (even one without any DNS // names), the CN should be ignored. + name: "IgnoreCNWithSANs", leaf: ignoreCNWithSANLeaf, dnsName: "foo.example.com", roots: []string{ignoreCNWithSANRoot}, currentTime: 1486684488, - systemSkip: true, + systemSkip: true, // does not chain to a system root errorCallback: expectHostnameError("certificate is not valid for any names"), }, { // Test that excluded names are respected. + name: "ExcludedNames", leaf: excludedNamesLeaf, dnsName: "bender.local", intermediates: []string{excludedNamesIntermediate}, roots: []string{excludedNamesRoot}, currentTime: 1486684488, - systemSkip: true, + systemSkip: true, // does not chain to a system root errorCallback: expectNameConstraintsError, }, { // Test that unknown critical extensions in a leaf cause a // verify error. + name: "CriticalExtLeaf", leaf: criticalExtLeafWithExt, dnsName: "example.com", intermediates: []string{criticalExtIntermediate}, roots: []string{criticalExtRoot}, currentTime: 1486684488, - systemSkip: true, + systemSkip: true, // does not chain to a system root errorCallback: expectUnhandledCriticalExtension, }, { // Test that unknown critical extensions in an intermediate // cause a verify error. + name: "CriticalExtIntermediate", leaf: criticalExtLeaf, dnsName: "example.com", intermediates: []string{criticalExtIntermediateWithExt}, roots: []string{criticalExtRoot}, currentTime: 1486684488, - systemSkip: true, + systemSkip: true, // does not chain to a system root errorCallback: expectUnhandledCriticalExtension, }, { // Test that invalid CN are ignored. + name: "InvalidCN", leaf: invalidCNWithoutSAN, dnsName: "foo,invalid", roots: []string{invalidCNRoot}, currentTime: 1540000000, - systemSkip: true, + systemSkip: true, // does not chain to a system root errorCallback: expectHostnameError("Common Name is not a valid hostname"), }, { // Test that valid CN are respected. + name: "ValidCN", leaf: validCNWithoutSAN, dnsName: "foo.example.com", roots: []string{invalidCNRoot}, currentTime: 1540000000, - systemSkip: true, + systemSkip: true, // does not chain to a system root expectedChains: [][]string{ {"foo.example.com", "Test root"}, @@ -357,31 +384,34 @@ var verifyTests = []verifyTest{ }, // Replicate CN tests with ignoreCN = true { + name: "IgnoreCNWithSANs/ignoreCN", leaf: ignoreCNWithSANLeaf, dnsName: "foo.example.com", roots: []string{ignoreCNWithSANRoot}, currentTime: 1486684488, - systemSkip: true, + systemSkip: true, // does not chain to a system root ignoreCN: true, errorCallback: expectHostnameError("certificate is not valid for any names"), }, { + name: "InvalidCN/ignoreCN", leaf: invalidCNWithoutSAN, dnsName: "foo,invalid", roots: []string{invalidCNRoot}, currentTime: 1540000000, - systemSkip: true, + systemSkip: true, // does not chain to a system root ignoreCN: true, - errorCallback: expectHostnameError("not valid for any names"), + errorCallback: expectHostnameError("certificate is not valid for any names"), }, { + name: "ValidCN/ignoreCN", leaf: validCNWithoutSAN, dnsName: "foo.example.com", roots: []string{invalidCNRoot}, currentTime: 1540000000, - systemSkip: true, + systemSkip: true, // does not chain to a system root ignoreCN: true, errorCallback: expectHostnameError("certificate relies on legacy Common Name field"), @@ -389,11 +419,12 @@ var verifyTests = []verifyTest{ { // A certificate with an AKID should still chain to a parent without SKID. // See Issue 30079. + name: "AKIDNoSKID", leaf: leafWithAKID, roots: []string{rootWithoutSKID}, currentTime: 1550000000, dnsName: "example", - systemSkip: true, + systemSkip: true, // does not chain to a system root expectedChains: [][]string{ {"Acme LLC", "Acme Co"}, @@ -401,98 +432,70 @@ var verifyTests = []verifyTest{ }, } -func expectHostnameError(msg string) func(*testing.T, int, error) bool { - return func(t *testing.T, i int, err error) (ok bool) { +func expectHostnameError(msg string) func(*testing.T, error) { + return func(t *testing.T, err error) { if _, ok := err.(HostnameError); !ok { - t.Errorf("#%d: error was not a HostnameError: %v", i, err) - return false + t.Fatalf("error was not a HostnameError: %v", err) } if !strings.Contains(err.Error(), msg) { - t.Errorf("#%d: HostnameError did not contain %q: %v", i, msg, err) + t.Fatalf("HostnameError did not contain %q: %v", msg, err) } - return true } } -func expectExpired(t *testing.T, i int, err error) (ok bool) { +func expectExpired(t *testing.T, err error) { if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != Expired { - t.Errorf("#%d: error was not Expired: %v", i, err) - return false + t.Fatalf("error was not Expired: %v", err) } - return true } -func expectUsageError(t *testing.T, i int, err error) (ok bool) { +func expectUsageError(t *testing.T, err error) { if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != IncompatibleUsage { - t.Errorf("#%d: error was not IncompatibleUsage: %v", i, err) - return false + t.Fatalf("error was not IncompatibleUsage: %v", err) } - return true } -func expectAuthorityUnknown(t *testing.T, i int, err error) (ok bool) { +func expectAuthorityUnknown(t *testing.T, err error) { e, ok := err.(UnknownAuthorityError) if !ok { - t.Errorf("#%d: error was not UnknownAuthorityError: %v", i, err) - return false + t.Fatalf("error was not UnknownAuthorityError: %v", err) } if e.Cert == nil { - t.Errorf("#%d: error was UnknownAuthorityError, but missing Cert: %v", i, err) - return false - } - return true -} - -func expectSystemRootsError(t *testing.T, i int, err error) bool { - if _, ok := err.(SystemRootsError); !ok { - t.Errorf("#%d: error was not SystemRootsError: %v", i, err) - return false + t.Fatalf("error was UnknownAuthorityError, but missing Cert: %v", err) } - return true } -func expectHashError(t *testing.T, i int, err error) bool { +func expectHashError(t *testing.T, err error) { if err == nil { - t.Errorf("#%d: no error resulted from invalid hash", i) - return false + t.Fatalf("no error resulted from invalid hash") } if expected := "algorithm unimplemented"; !strings.Contains(err.Error(), expected) { - t.Errorf("#%d: error resulting from invalid hash didn't contain '%s', rather it was: %v", i, expected, err) - return false + t.Fatalf("error resulting from invalid hash didn't contain '%s', rather it was: %v", expected, err) } - return true } -func expectSubjectIssuerMismatcthError(t *testing.T, i int, err error) (ok bool) { +func expectSubjectIssuerMismatcthError(t *testing.T, err error) { if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != NameMismatch { - t.Errorf("#%d: error was not a NameMismatch: %v", i, err) - return false + t.Fatalf("error was not a NameMismatch: %v", err) } - return true } -func expectNameConstraintsError(t *testing.T, i int, err error) (ok bool) { +func expectNameConstraintsError(t *testing.T, err error) { if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != CANotAuthorizedForThisName { - t.Errorf("#%d: error was not a CANotAuthorizedForThisName: %v", i, err) - return false + t.Fatalf("error was not a CANotAuthorizedForThisName: %v", err) } - return true } -func expectNotAuthorizedError(t *testing.T, i int, err error) (ok bool) { +func expectNotAuthorizedError(t *testing.T, err error) { if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != NotAuthorizedToSign { - t.Errorf("#%d: error was not a NotAuthorizedToSign: %v", i, err) - return false + t.Fatalf("error was not a NotAuthorizedToSign: %v", err) } - return true } -func expectUnhandledCriticalExtension(t *testing.T, i int, err error) (ok bool) { +func expectUnhandledCriticalExtension(t *testing.T, err error) { if _, ok := err.(UnhandledCriticalExtension); !ok { - t.Errorf("#%d: error was not an UnhandledCriticalExtension: %v", i, err) - return false + t.Fatalf("error was not an UnhandledCriticalExtension: %v", err) } - return true } func certificateFromPEM(pemBytes string) (*Certificate, error) { @@ -503,107 +506,91 @@ func certificateFromPEM(pemBytes string) (*Certificate, error) { return ParseCertificate(block.Bytes) } -func testVerify(t *testing.T, useSystemRoots bool) { - defer func(savedIgnoreCN bool) { - ignoreCN = savedIgnoreCN - }(ignoreCN) - for i, test := range verifyTests { - if useSystemRoots && test.systemSkip { - continue - } - if runtime.GOOS == "windows" && test.testSystemRootsError { - continue - } - - ignoreCN = test.ignoreCN - opts := VerifyOptions{ - Intermediates: NewCertPool(), - DNSName: test.dnsName, - CurrentTime: time.Unix(test.currentTime, 0), - KeyUsages: test.keyUsages, - } +func testVerify(t *testing.T, test verifyTest, useSystemRoots bool) { + defer func(savedIgnoreCN bool) { ignoreCN = savedIgnoreCN }(ignoreCN) - if !useSystemRoots { - opts.Roots = NewCertPool() - for j, root := range test.roots { - ok := opts.Roots.AppendCertsFromPEM([]byte(root)) - if !ok { - t.Errorf("#%d: failed to parse root #%d", i, j) - return - } - } - } + ignoreCN = test.ignoreCN + opts := VerifyOptions{ + Intermediates: NewCertPool(), + DNSName: test.dnsName, + CurrentTime: time.Unix(test.currentTime, 0), + KeyUsages: test.keyUsages, + } - for j, intermediate := range test.intermediates { - ok := opts.Intermediates.AppendCertsFromPEM([]byte(intermediate)) + if !useSystemRoots { + opts.Roots = NewCertPool() + for j, root := range test.roots { + ok := opts.Roots.AppendCertsFromPEM([]byte(root)) if !ok { - t.Errorf("#%d: failed to parse intermediate #%d", i, j) - return + t.Fatalf("failed to parse root #%d", j) } } + } - leaf, err := certificateFromPEM(test.leaf) - if err != nil { - t.Errorf("#%d: failed to parse leaf: %v", i, err) - return - } - - var oldSystemRoots *CertPool - if test.testSystemRootsError { - oldSystemRoots = systemRootsPool() - systemRoots = nil - opts.Roots = nil + for j, intermediate := range test.intermediates { + ok := opts.Intermediates.AppendCertsFromPEM([]byte(intermediate)) + if !ok { + t.Fatalf("failed to parse intermediate #%d", j) } + } - chains, err := leaf.Verify(opts) + leaf, err := certificateFromPEM(test.leaf) + if err != nil { + t.Fatalf("failed to parse leaf: %v", err) + } - if test.testSystemRootsError { - systemRoots = oldSystemRoots - } + chains, err := leaf.Verify(opts) - if test.errorCallback == nil && err != nil { - t.Errorf("#%d: unexpected error: %v", i, err) - } - if test.errorCallback != nil { - if !test.errorCallback(t, i, err) { - return + if test.errorCallback == nil && err != nil { + t.Fatalf("unexpected error: %v", err) + } + if test.errorCallback != nil { + if useSystemRoots && test.systemLax { + if err == nil { + t.Fatalf("expected error") } + } else { + test.errorCallback(t, err) } + } - if len(chains) != len(test.expectedChains) { - t.Errorf("#%d: wanted %d chains, got %d", i, len(test.expectedChains), len(chains)) - } + if len(chains) != len(test.expectedChains) { + t.Errorf("wanted %d chains, got %d", len(test.expectedChains), len(chains)) + } - // We check that each returned chain matches a chain from - // expectedChains but an entry in expectedChains can't match - // two chains. - seenChains := make([]bool, len(chains)) - NextOutputChain: - for _, chain := range chains { - TryNextExpected: - for j, expectedChain := range test.expectedChains { - if seenChains[j] { - continue - } - if len(chain) != len(expectedChain) { - continue - } - for k, cert := range chain { - if !strings.Contains(nameToKey(&cert.Subject), expectedChain[k]) { - continue TryNextExpected - } + // We check that each returned chain matches a chain from + // expectedChains but an entry in expectedChains can't match + // two chains. + seenChains := make([]bool, len(chains)) +NextOutputChain: + for _, chain := range chains { + TryNextExpected: + for j, expectedChain := range test.expectedChains { + if seenChains[j] { + continue + } + if len(chain) != len(expectedChain) { + continue + } + for k, cert := range chain { + if !strings.Contains(nameToKey(&cert.Subject), expectedChain[k]) { + continue TryNextExpected } - // we matched - seenChains[j] = true - continue NextOutputChain } - t.Errorf("#%d: No expected chain matched %s", i, chainToDebugString(chain)) + // we matched + seenChains[j] = true + continue NextOutputChain } + t.Errorf("no expected chain matched %s", chainToDebugString(chain)) } } func TestGoVerify(t *testing.T) { - testVerify(t, false) + for _, test := range verifyTests { + t.Run(test.name, func(t *testing.T) { + testVerify(t, test, false) + }) + } } func TestSystemVerify(t *testing.T) { @@ -611,7 +598,14 @@ func TestSystemVerify(t *testing.T) { t.Skipf("skipping verify test using system APIs on %q", runtime.GOOS) } - testVerify(t, true) + for _, test := range verifyTests { + t.Run(test.name, func(t *testing.T) { + if test.systemSkip { + t.SkipNow() + } + testVerify(t, test, true) + }) + } } func chainToDebugString(chain []*Certificate) string { @@ -648,8 +642,7 @@ tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV 5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== ------END CERTIFICATE----- -` +-----END CERTIFICATE-----` const giag2Intermediate = `-----BEGIN CERTIFICATE----- MIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT @@ -674,8 +667,7 @@ zG+FA1jDaFETzf3I93k9mTXwVqO94FntT0QJo544evZG0R0SnU++0ED8Vf4GXjza HFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto WHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6 yuGnBXj8ytqU0CwIPX4WecigUCAkVDNx ------END CERTIFICATE----- -` +-----END CERTIFICATE-----` const googleLeaf = `-----BEGIN CERTIFICATE----- MIIEdjCCA16gAwIBAgIIcR5k4dkoe04wDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE @@ -702,8 +694,7 @@ tJAW0kYGJ+wqKm53wG/JaOADTnnq2Mt/j6F2uvjgN/ouns1nRHufIvd370N0LeH+ orKqTuAPzXK7imQk6+OycYABbqCtC/9qmwRd8wwn7sF97DtYfK8WuNHtFalCAwyi 8LxJJYJCLWoMhZ+V8GZm+FOex5qkQAjnZrtNlbQJ8ro4r+rpKXtmMFFhfa+7L+PA Kom08eUK8skxAzfDDijZPh10VtJ66uBoiDPdT+uCBehcBIcmSTrKjFGX ------END CERTIFICATE----- -` +-----END CERTIFICATE-----` // googleLeafWithInvalidHash is the same as googleLeaf, but the signature // algorithm in the certificate contains a nonsense OID. @@ -732,8 +723,7 @@ tJAW0kYGJ+wqKm53wG/JaOADTnnq2Mt/j6F2uvjgN/ouns1nRHufIvd370N0LeH+ orKqTuAPzXK7imQk6+OycYABbqCtC/9qmwRd8wwn7sF97DtYfK8WuNHtFalCAwyi 8LxJJYJCLWoMhZ+V8GZm+FOex5qkQAjnZrtNlbQJ8ro4r+rpKXtmMFFhfa+7L+PA Kom08eUK8skxAzfDDijZPh10VtJ66uBoiDPdT+uCBehcBIcmSTrKjFGX ------END CERTIFICATE----- -` +-----END CERTIFICATE-----` const dnssecExpLeaf = `-----BEGIN CERTIFICATE----- MIIGzTCCBbWgAwIBAgIDAdD6MA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ @@ -858,58 +848,127 @@ NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14= -----END CERTIFICATE-----` const smimeLeaf = `-----BEGIN CERTIFICATE----- -MIIFBjCCA+6gAwIBAgISESFvrjT8XcJTEe6rBlPptILlMA0GCSqGSIb3DQEBBQUA -MFQxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSowKAYD -VQQDEyFHbG9iYWxTaWduIFBlcnNvbmFsU2lnbiAyIENBIC0gRzIwHhcNMTIwMTIz -MTYzNjU5WhcNMTUwMTIzMTYzNjU5WjCBlDELMAkGA1UEBhMCVVMxFjAUBgNVBAgT -DU5ldyBIYW1zcGhpcmUxEzARBgNVBAcTClBvcnRzbW91dGgxGTAXBgNVBAoTEEds -b2JhbFNpZ24sIEluYy4xEzARBgNVBAMTClJ5YW4gSHVyc3QxKDAmBgkqhkiG9w0B -CQEWGXJ5YW4uaHVyc3RAZ2xvYmFsc2lnbi5jb20wggEiMA0GCSqGSIb3DQEBAQUA -A4IBDwAwggEKAoIBAQC4ASSTvavmsFQAob60ukSSwOAL9nT/s99ltNUCAf5fPH5j -NceMKxaQse2miOmRRIXaykcq1p/TbI70Ztce38r2mbOwqDHHPVi13GxJEyUXWgaR -BteDMu5OGyWNG1kchVsGWpbstT0Z4v0md5m1BYFnxB20ebJyOR2lXDxsFK28nnKV -+5eMj76U8BpPQ4SCH7yTMG6y0XXsB3cCrBKr2o3TOYgEKv+oNnbaoMt3UxMt9nSf -9jyIshjqfnT5Aew3CUNMatO55g5FXXdIukAweg1YSb1ls05qW3sW00T3d7dQs9/7 -NuxCg/A2elmVJSoy8+MLR8JSFEf/aMgjO/TyLg/jAgMBAAGjggGPMIIBizAOBgNV -HQ8BAf8EBAMCBaAwTQYDVR0gBEYwRDBCBgorBgEEAaAyASgKMDQwMgYIKwYBBQUH -AgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMCQGA1Ud -EQQdMBuBGXJ5YW4uaHVyc3RAZ2xvYmFsc2lnbi5jb20wCQYDVR0TBAIwADAdBgNV -HSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwQwYDVR0fBDwwOjA4oDagNIYyaHR0 -cDovL2NybC5nbG9iYWxzaWduLmNvbS9ncy9nc3BlcnNvbmFsc2lnbjJnMi5jcmww -VQYIKwYBBQUHAQEESTBHMEUGCCsGAQUFBzAChjlodHRwOi8vc2VjdXJlLmdsb2Jh -bHNpZ24uY29tL2NhY2VydC9nc3BlcnNvbmFsc2lnbjJnMi5jcnQwHQYDVR0OBBYE -FFWiECe0/L72eVYqcWYnLV6SSjzhMB8GA1UdIwQYMBaAFD8V0m18L+cxnkMKBqiU -bCw7xe5lMA0GCSqGSIb3DQEBBQUAA4IBAQAhQi6hLPeudmf3IBF4IDzCvRI0FaYd -BKfprSk/H0PDea4vpsLbWpA0t0SaijiJYtxKjlM4bPd+2chb7ejatDdyrZIzmDVy -q4c30/xMninGKokpYA11/Ve+i2dvjulu65qasrtQRGybAuuZ67lrp/K3OMFgjV5N -C3AHYLzvNU4Dwc4QQ1BaMOg6KzYSrKbABRZajfrpC9uiePsv7mDIXLx/toBPxWNl -a5vJm5DrZdn7uHdvBCE6kMykbOLN5pmEK0UIlwKh6Qi5XD0pzlVkEZliFkBMJgub -d/eF7xeg7TKPWC5xyOFp9SdMolJM7LTC3wnSO3frBAev+q/nGs9Xxyvs +MIIIPDCCBiSgAwIBAgIQaMDxFS0pOMxZZeOBxoTJtjANBgkqhkiG9w0BAQsFADCB +nTELMAkGA1UEBhMCRVMxFDASBgNVBAoMC0laRU5QRSBTLkEuMTowOAYDVQQLDDFB +WlogWml1cnRhZ2lyaSBwdWJsaWtvYSAtIENlcnRpZmljYWRvIHB1YmxpY28gU0NB +MTwwOgYDVQQDDDNFQUVrbyBIZXJyaSBBZG1pbmlzdHJhemlvZW4gQ0EgLSBDQSBB +QVBQIFZhc2NhcyAoMikwHhcNMTcwNzEyMDg1MzIxWhcNMjEwNzEyMDg1MzIxWjCC +AQwxDzANBgNVBAoMBklaRU5QRTE4MDYGA1UECwwvWml1cnRhZ2lyaSBrb3Jwb3Jh +dGlib2EtQ2VydGlmaWNhZG8gY29ycG9yYXRpdm8xQzBBBgNVBAsMOkNvbmRpY2lv +bmVzIGRlIHVzbyBlbiB3d3cuaXplbnBlLmNvbSBub2xhIGVyYWJpbGkgamFraXRl +a28xFzAVBgNVBC4TDi1kbmkgOTk5OTk5ODlaMSQwIgYDVQQDDBtDT1JQT1JBVElW +TyBGSUNUSUNJTyBBQ1RJVk8xFDASBgNVBCoMC0NPUlBPUkFUSVZPMREwDwYDVQQE +DAhGSUNUSUNJTzESMBAGA1UEBRMJOTk5OTk5ODlaMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAwVOMwUDfBtsH0XuxYnb+v/L774jMH8valX7RPH8cl2Lb +SiqSo0RchW2RGA2d1yuYHlpChC9jGmt0X/g66/E/+q2hUJlfJtqVDJFwtFYV4u2S +yzA3J36V4PRkPQrKxAsbzZriFXAF10XgiHQz9aVeMMJ9GBhmh9+DK8Tm4cMF6i8l ++AuC35KdngPF1x0ealTYrYZplpEJFO7CiW42aLi6vQkDR2R7nmZA4AT69teqBWsK +0DZ93/f0G/3+vnWwNTBF0lB6dIXoaz8OMSyHLqGnmmAtMrzbjAr/O/WWgbB/BqhR +qjJQ7Ui16cuDldXaWQ/rkMzsxmsAox0UF+zdQNvXUQIDAQABo4IDBDCCAwAwgccG +A1UdEgSBvzCBvIYVaHR0cDovL3d3dy5pemVucGUuY29tgQ9pbmZvQGl6ZW5wZS5j +b22kgZEwgY4xRzBFBgNVBAoMPklaRU5QRSBTLkEuIC0gQ0lGIEEwMTMzNzI2MC1S +TWVyYy5WaXRvcmlhLUdhc3RlaXogVDEwNTUgRjYyIFM4MUMwQQYDVQQJDDpBdmRh +IGRlbCBNZWRpdGVycmFuZW8gRXRvcmJpZGVhIDE0IC0gMDEwMTAgVml0b3JpYS1H +YXN0ZWl6MB4GA1UdEQQXMBWBE2ZpY3RpY2lvQGl6ZW5wZS5ldXMwDgYDVR0PAQH/ +BAQDAgXgMCkGA1UdJQQiMCAGCCsGAQUFBwMCBggrBgEFBQcDBAYKKwYBBAGCNxQC +AjAdBgNVHQ4EFgQUyeoOD4cgcljKY0JvrNuX2waFQLAwHwYDVR0jBBgwFoAUwKlK +90clh/+8taaJzoLSRqiJ66MwggEnBgNVHSAEggEeMIIBGjCCARYGCisGAQQB8zkB +AQEwggEGMDMGCCsGAQUFBwIBFidodHRwOi8vd3d3Lml6ZW5wZS5jb20vcnBhc2Nh +Y29ycG9yYXRpdm8wgc4GCCsGAQUFBwICMIHBGoG+Wml1cnRhZ2lyaWEgRXVza2Fs +IEF1dG9ub21pYSBFcmtpZGVnb2tvIHNla3RvcmUgcHVibGlrb2tvIGVyYWt1bmRl +ZW4gYmFybmUtc2FyZWV0YW4gYmFrYXJyaWsgZXJhYmlsIGRhaXRla2UuIFVzbyBy +ZXN0cmluZ2lkbyBhbCBhbWJpdG8gZGUgcmVkZXMgaW50ZXJuYXMgZGUgRW50aWRh +ZGVzIGRlbCBTZWN0b3IgUHVibGljbyBWYXNjbzAyBggrBgEFBQcBAQQmMCQwIgYI +KwYBBQUHMAGGFmh0dHA6Ly9vY3NwLml6ZW5wZS5jb20wOgYDVR0fBDMwMTAvoC2g +K4YpaHR0cDovL2NybC5pemVucGUuY29tL2NnaS1iaW4vY3JsaW50ZXJuYTIwDQYJ +KoZIhvcNAQELBQADggIBAIy5PQ+UZlCRq6ig43vpHwlwuD9daAYeejV0Q+ZbgWAE +GtO0kT/ytw95ZEJMNiMw3fYfPRlh27ThqiT0VDXZJDlzmn7JZd6QFcdXkCsiuv4+ +ZoXAg/QwnA3SGUUO9aVaXyuOIIuvOfb9MzoGp9xk23SMV3eiLAaLMLqwB5DTfBdt +BGI7L1MnGJBv8RfP/TL67aJ5bgq2ri4S8vGHtXSjcZ0+rCEOLJtmDNMnTZxancg3 +/H5edeNd+n6Z48LO+JHRxQufbC4mVNxVLMIP9EkGUejlq4E4w6zb5NwCQczJbSWL +i31rk2orsNsDlyaLGsWZp3JSNX6RmodU4KAUPor4jUJuUhrrm3Spb73gKlV/gcIw +bCE7mML1Kss3x1ySaXsis6SZtLpGWKkW2iguPWPs0ydV6RPhmsCxieMwPPIJ87vS +5IejfgyBae7RSuAIHyNFy4uI5xwvwUFf6OZ7az8qtW7ImFOgng3Ds+W9k1S2CNTx +d0cnKTfA6IpjGo8EeHcxnIXT8NPImWaRj0qqonvYady7ci6U4m3lkNSdXNn1afgw +mYust+gxVtOZs1gk2MUCgJ1V1X+g7r/Cg7viIn6TLkLrpS1kS1hvMqkl9M+7XqPo +Qd95nJKOkusQpy99X4dF/lfbYAQnnjnqh3DLD2gvYObXFaAYFaiBKTiMTV2X72F+ -----END CERTIFICATE-----` const smimeIntermediate = `-----BEGIN CERTIFICATE----- -MIIEFjCCAv6gAwIBAgILBAAAAAABL07hL1IwDQYJKoZIhvcNAQEFBQAwVzELMAkG -A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv -b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMTA0MTMxMDAw -MDBaFw0xOTA0MTMxMDAwMDBaMFQxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i -YWxTaWduIG52LXNhMSowKAYDVQQDEyFHbG9iYWxTaWduIFBlcnNvbmFsU2lnbiAy -IENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBa0H5Nez4 -En3dIlFpX7e5E0YndxQ74xOBbz7kdBd+DLX0LOQMjVPU3DAgKL9ujhH+ZhHkURbH -3X/94TQSUL/z2JjsaQvS0NqyZXHhM5eeuquzOJRzEQ8+odETzHg2G0Erv7yjSeww -gkwDWDJnYUDlOjYTDUEG6+i+8Mn425reo4I0E277wD542kmVWeW7+oHv5dZo9e1Q -yWwiKTEP6BEQVVSBgThXMG4traSSDRUt3T1eQTZx5EObpiBEBO4OTqiBTJfg4vEI -YgkXzKLpnfszTB6YMDpR9/QS6p3ANB3kfAb+t6udSO3WCst0DGrwHDLBFGDR4UeY -T5KGGnI7cWL7AgMBAAGjgeUwgeIwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI -MAYBAf8CAQAwHQYDVR0OBBYEFD8V0m18L+cxnkMKBqiUbCw7xe5lMEcGA1UdIARA -MD4wPAYEVR0gADA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWdu -LmNvbS9yZXBvc2l0b3J5LzAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLmds -b2JhbHNpZ24ubmV0L3Jvb3QuY3JsMB8GA1UdIwQYMBaAFGB7ZhpFDZfKiVAvfQTN -NKj//P1LMA0GCSqGSIb3DQEBBQUAA4IBAQBDc3nMpMxJMQMcYUCB3+C73UpvwDE8 -eCOr7t2F/uaQKKcyqqstqLZc6vPwI/rcE9oDHugY5QEjQzIBIEaTnN6P0vege2IX -eCOr7t2F/uaQKKcyqqstqLZc6vPwI/rcE9oDHugY5QEjQzIBIEaTnN6P0vege2IX -YEvTWbWwGdPytDFPYIl3/6OqNSXSnZ7DxPcdLJq2uyiga8PB/TTIIHYkdM2+1DE0 -7y3rH/7TjwDVD7SLu5/SdOfKskuMPTjOEvz3K161mymW06klVhubCIWOro/Gx1Q2 -2FQOZ7/2k4uYoOdBTSlb8kTAuzZNgIE0rB2BIYCTz/P6zZIKW0ogbRSH +MIIHNzCCBSGgAwIBAgIQJMXIqlZvjuhMvqcFXOFkpDALBgkqhkiG9w0BAQswODEL +MAkGA1UEBhMCRVMxFDASBgNVBAoMC0laRU5QRSBTLkEuMRMwEQYDVQQDDApJemVu +cGUuY29tMB4XDTEwMTAyMDA4MjMzM1oXDTM3MTIxMjIzMDAwMFowgZ0xCzAJBgNV +BAYTAkVTMRQwEgYDVQQKDAtJWkVOUEUgUy5BLjE6MDgGA1UECwwxQVpaIFppdXJ0 +YWdpcmkgcHVibGlrb2EgLSBDZXJ0aWZpY2FkbyBwdWJsaWNvIFNDQTE8MDoGA1UE +AwwzRUFFa28gSGVycmkgQWRtaW5pc3RyYXppb2VuIENBIC0gQ0EgQUFQUCBWYXNj +YXMgKDIpMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoIM7nEdI0N1h +rR5T4xuV/usKDoMIasaiKvfLhbwxaNtTt+a7W/6wV5bv3svQFIy3sUXjjdzV1nG2 +To2wo/YSPQiOt8exWvOapvL21ogiof+kelWnXFjWaKJI/vThHYLgIYEMj/y4HdtU +ojI646rZwqsb4YGAopwgmkDfUh5jOhV2IcYE3TgJAYWVkj6jku9PLaIsHiarAHjD +PY8dig8a4SRv0gm5Yk7FXLmW1d14oxQBDeHZ7zOEXfpafxdEDO2SNaRJjpkh8XRr +PGqkg2y1Q3gT6b4537jz+StyDIJ3omylmlJsGCwqT7p8mEqjGJ5kC5I2VnjXKuNn +soShc72khWZVUJiJo5SGuAkNE2ZXqltBVm5Jv6QweQKsX6bkcMc4IZok4a+hx8FM +8IBpGf/I94pU6HzGXqCyc1d46drJgDY9mXa+6YDAJFl3xeXOOW2iGCfwXqhiCrKL +MYvyMZzqF3QH5q4nb3ZnehYvraeMFXJXDn+Utqp8vd2r7ShfQJz01KtM4hgKdgSg +jtW+shkVVN5ng/fPN85ovfAH2BHXFfHmQn4zKsYnLitpwYM/7S1HxlT61cdQ7Nnk +3LZTYEgAoOmEmdheklT40WAYakksXGM5VrzG7x9S7s1Tm+Vb5LSThdHC8bxxwyTb +KsDRDNJ84N9fPDO6qHnzaL2upQ43PycCAwEAAaOCAdkwggHVMIHHBgNVHREEgb8w +gbyGFWh0dHA6Ly93d3cuaXplbnBlLmNvbYEPaW5mb0BpemVucGUuY29tpIGRMIGO +MUcwRQYDVQQKDD5JWkVOUEUgUy5BLiAtIENJRiBBMDEzMzcyNjAtUk1lcmMuVml0 +b3JpYS1HYXN0ZWl6IFQxMDU1IEY2MiBTODFDMEEGA1UECQw6QXZkYSBkZWwgTWVk +aXRlcnJhbmVvIEV0b3JiaWRlYSAxNCAtIDAxMDEwIFZpdG9yaWEtR2FzdGVpejAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUwKlK90cl +h/+8taaJzoLSRqiJ66MwHwYDVR0jBBgwFoAUHRxlDqjyJXu0kc/ksbHmvVV0bAUw +OgYDVR0gBDMwMTAvBgRVHSAAMCcwJQYIKwYBBQUHAgEWGWh0dHA6Ly93d3cuaXpl +bnBlLmNvbS9jcHMwNwYIKwYBBQUHAQEEKzApMCcGCCsGAQUFBzABhhtodHRwOi8v +b2NzcC5pemVucGUuY29tOjgwOTQwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2Ny +bC5pemVucGUuY29tL2NnaS1iaW4vYXJsMjALBgkqhkiG9w0BAQsDggIBAMbjc3HM +3DG9ubWPkzsF0QsktukpujbTTcGk4h20G7SPRy1DiiTxrRzdAMWGjZioOP3/fKCS +M539qH0M+gsySNie+iKlbSZJUyE635T1tKw+G7bDUapjlH1xyv55NC5I6wCXGC6E +3TEP5B/E7dZD0s9E4lS511ubVZivFgOzMYo1DO96diny/N/V1enaTCpRl1qH1OyL +xUYTijV4ph2gL6exwuG7pxfRcVNHYlrRaXWfTz3F6NBKyULxrI3P/y6JAtN1GqT4 +VF/+vMygx22n0DufGepBwTQz6/rr1ulSZ+eMnuJiTXgh/BzQnkUsXTb8mHII25iR +0oYF2qAsk6ecWbLiDpkHKIDHmML21MZE13MS8NSvTHoqJO4LyAmDe6SaeNHtrPlK +b6mzE1BN2ug+ZaX8wLA5IMPFaf0jKhb/Cxu8INsxjt00brsErCc9ip1VNaH0M4bi +1tGxfiew2436FaeyUxW7Pl6G5GgkNbuUc7QIoRy06DdU/U38BxW3uyJMY60zwHvS +FlKAn0OvYp4niKhAJwaBVN3kowmJuOU5Rid+TUnfyxbJ9cttSgzaF3hP/N4zgMEM +5tikXUskeckt8LUK96EH0QyssavAMECUEb/xrupyRdYWwjQGvNLq6T5+fViDGyOw +k+lzD44wofy8paAy9uC9Owae0zMEzhcsyRm7 +-----END CERTIFICATE-----` + +const smimeRoot = `-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 +MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 +ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD +VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq +scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO +xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H +LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX +uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD +yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ +JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q +rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN +BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L +hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB +QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ +HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu +Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg +QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB +BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA +A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb +laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 +awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo +JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw +LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT +VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk +LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb +UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ +QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ +naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls +QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== -----END CERTIFICATE-----` var megaLeaf = `-----BEGIN CERTIFICATE----- @@ -1315,50 +1374,7 @@ vRAvOtNiKtPzFeQVdbRPOskC4rcHyPeiDAMAMixeLi63+CFty4da3r5lRezeedCE cw3ESZzThBwWqvPOtJdpXdm+r57pDW8qD+/0lY8wfImMNkQAyCUCLg/1Lxt/hrBj -----END CERTIFICATE-----` -const issuerSubjectMatchRoot = ` -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 161640039802297062 (0x23e42c281e55ae6) - Signature Algorithm: sha256WithRSAEncryption - Issuer: O=Golang, CN=Root ca - Validity - Not Before: Jan 1 00:00:00 2015 GMT - Not After : Jan 1 00:00:00 2025 GMT - Subject: O=Golang, CN=Root ca - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (1024 bit) - Modulus: - 00:e9:0e:7f:11:0c:e6:5a:e6:86:83:70:f6:51:07: - 2e:02:78:11:f5:b2:24:92:38:ee:26:62:02:c7:94: - f1:3e:a1:77:6a:c0:8f:d5:22:68:b6:5d:e2:4c:da: - e0:85:11:35:c2:92:72:49:8d:81:b4:88:97:6b:b7: - fc:b2:44:5b:d9:4d:06:70:f9:0c:c6:8f:e9:b3:df: - a3:6a:84:6c:43:59:be:9d:b2:d0:76:9b:c3:d7:fa: - 99:59:c3:b8:e5:f3:53:03:bd:49:d6:b3:cc:a2:43: - fe:ad:c2:0b:b9:01:b8:56:29:94:03:24:a7:0d:28: - 21:29:a9:ae:94:5b:4a:f9:9f - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Certificate Sign - X509v3 Extended Key Usage: - TLS Web Server Authentication, TLS Web Client Authentication - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Subject Key Identifier: - 40:37:D7:01:FB:40:2F:B8:1C:7E:54:04:27:8C:59:01 - Signature Algorithm: sha256WithRSAEncryption - 6f:84:df:49:e0:99:d4:71:66:1d:32:86:56:cb:ea:5a:6b:0e: - 00:6a:d1:5a:6e:1f:06:23:07:ff:cb:d1:1a:74:e4:24:43:0b: - aa:2a:a0:73:75:25:82:bc:bf:3f:a9:f8:48:88:ac:ed:3a:94: - 3b:0d:d3:88:c8:67:44:61:33:df:71:6c:c5:af:ed:16:8c:bf: - 82:f9:49:bb:e3:2a:07:53:36:37:25:77:de:91:a4:77:09:7f: - 6f:b2:91:58:c4:05:89:ea:8e:fa:e1:3b:19:ef:f8:f6:94:b7: - 7b:27:e6:e4:84:dd:2b:f5:93:f5:3c:d8:86:c5:38:01:56:5c: - 9f:6d ------BEGIN CERTIFICATE----- +const issuerSubjectMatchRoot = `-----BEGIN CERTIFICATE----- MIICIDCCAYmgAwIBAgIIAj5CwoHlWuYwDQYJKoZIhvcNAQELBQAwIzEPMA0GA1UE ChMGR29sYW5nMRAwDgYDVQQDEwdSb290IGNhMB4XDTE1MDEwMTAwMDAwMFoXDTI1 MDEwMTAwMDAwMFowIzEPMA0GA1UEChMGR29sYW5nMRAwDgYDVQQDEwdSb290IGNh @@ -1373,53 +1389,7 @@ RGEz33Fsxa/tFoy/gvlJu+MqB1M2NyV33pGkdwl/b7KRWMQFieqO+uE7Ge/49pS3 eyfm5ITdK/WT9TzYhsU4AVZcn20= -----END CERTIFICATE-----` -const issuerSubjectMatchLeaf = ` -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 16785088708916013734 (0xe8f09d3fe25beaa6) - Signature Algorithm: sha256WithRSAEncryption - Issuer: O=Golang, CN=Root CA - Validity - Not Before: Jan 1 00:00:00 2015 GMT - Not After : Jan 1 00:00:00 2025 GMT - Subject: O=Golang, CN=Leaf - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (1024 bit) - Modulus: - 00:db:46:7d:93:2e:12:27:06:48:bc:06:28:21:ab: - 7e:c4:b6:a2:5d:fe:1e:52:45:88:7a:36:47:a5:08: - 0d:92:42:5b:c2:81:c0:be:97:79:98:40:fb:4f:6d: - 14:fd:2b:13:8b:c2:a5:2e:67:d8:d4:09:9e:d6:22: - 38:b7:4a:0b:74:73:2b:c2:34:f1:d1:93:e5:96:d9: - 74:7b:f3:58:9f:6c:61:3c:c0:b0:41:d4:d9:2b:2b: - 24:23:77:5b:1c:3b:bd:75:5d:ce:20:54:cf:a1:63: - 87:1d:1e:24:c4:f3:1d:1a:50:8b:aa:b6:14:43:ed: - 97:a7:75:62:f4:14:c8:52:d7 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Key Encipherment - X509v3 Extended Key Usage: - TLS Web Server Authentication, TLS Web Client Authentication - X509v3 Basic Constraints: critical - CA:FALSE - X509v3 Subject Key Identifier: - 9F:91:16:1F:43:43:3E:49:A6:DE:6D:B6:80:D7:9F:60 - X509v3 Authority Key Identifier: - keyid:40:37:D7:01:FB:40:2F:B8:1C:7E:54:04:27:8C:59:01 - - Signature Algorithm: sha256WithRSAEncryption - 8d:86:05:da:89:f5:1d:c5:16:14:41:b9:34:87:2b:5c:38:99: - e3:d9:5a:5b:7a:5b:de:0b:5c:08:45:09:6f:1c:9d:31:5f:08: - ca:7a:a3:99:da:83:0b:22:be:4f:02:35:91:4e:5d:5c:37:bf: - 89:22:58:7d:30:76:d2:2f:d0:a0:ee:77:9e:77:c0:d6:19:eb: - ec:a0:63:35:6a:80:9b:80:1a:80:de:64:bc:40:38:3c:22:69: - ad:46:26:a2:3d:ea:f4:c2:92:49:16:03:96:ae:64:21:b9:7c: - ee:64:91:47:81:aa:b4:0c:09:2b:12:1a:b2:f3:af:50:b3:b1: - ce:24 ------BEGIN CERTIFICATE----- +const issuerSubjectMatchLeaf = `-----BEGIN CERTIFICATE----- MIICODCCAaGgAwIBAgIJAOjwnT/iW+qmMA0GCSqGSIb3DQEBCwUAMCMxDzANBgNV BAoTBkdvbGFuZzEQMA4GA1UEAxMHUm9vdCBDQTAeFw0xNTAxMDEwMDAwMDBaFw0y NTAxMDEwMDAwMDBaMCAxDzANBgNVBAoTBkdvbGFuZzENMAsGA1UEAxMETGVhZjCB @@ -1432,11 +1402,9 @@ Q0M+SabebbaA159gMBsGA1UdIwQUMBKAEEA31wH7QC+4HH5UBCeMWQEwDQYJKoZI hvcNAQELBQADgYEAjYYF2on1HcUWFEG5NIcrXDiZ49laW3pb3gtcCEUJbxydMV8I ynqjmdqDCyK+TwI1kU5dXDe/iSJYfTB20i/QoO53nnfA1hnr7KBjNWqAm4AagN5k vEA4PCJprUYmoj3q9MKSSRYDlq5kIbl87mSRR4GqtAwJKxIasvOvULOxziQ= ------END CERTIFICATE----- -` +-----END CERTIFICATE-----` -const x509v1TestRoot = ` ------BEGIN CERTIFICATE----- +const x509v1TestRoot = `-----BEGIN CERTIFICATE----- MIICIDCCAYmgAwIBAgIIAj5CwoHlWuYwDQYJKoZIhvcNAQELBQAwIzEPMA0GA1UE ChMGR29sYW5nMRAwDgYDVQQDEwdSb290IENBMB4XDTE1MDEwMTAwMDAwMFoXDTI1 MDEwMTAwMDAwMFowIzEPMA0GA1UEChMGR29sYW5nMRAwDgYDVQQDEwdSb290IENB @@ -1451,8 +1419,7 @@ h2NtN34ard0hEfHc8qW8mkXdsysVmq6cPvFYaHz+dBtkHuHDoy8YQnC0zdN/WyYB /1JmacUUofl+HusHuLkDxmadogI= -----END CERTIFICATE-----` -const x509v1TestIntermediate = ` ------BEGIN CERTIFICATE----- +const x509v1TestIntermediate = `-----BEGIN CERTIFICATE----- MIIByjCCATMCCQCCdEMsT8ykqTANBgkqhkiG9w0BAQsFADAjMQ8wDQYDVQQKEwZH b2xhbmcxEDAOBgNVBAMTB1Jvb3QgQ0EwHhcNMTUwMTAxMDAwMDAwWhcNMjUwMTAx MDAwMDAwWjAwMQ8wDQYDVQQKEwZHb2xhbmcxHTAbBgNVBAMTFFguNTA5djEgaW50 @@ -1465,8 +1432,7 @@ zWE77kJDibzd141u21ZbLsKvEdUJXjla43bdyMmEqf5VGpC3D4sFt3QVH7lGeRur x5Wlq1u3YDL/j6s1nU2dQ3ySB/oP7J+vQ9V4QeM+ -----END CERTIFICATE-----` -const x509v1TestLeaf = ` ------BEGIN CERTIFICATE----- +const x509v1TestLeaf = `-----BEGIN CERTIFICATE----- MIICMzCCAZygAwIBAgIJAPo99mqJJrpJMA0GCSqGSIb3DQEBCwUAMDAxDzANBgNV BAoTBkdvbGFuZzEdMBsGA1UEAxMUWC41MDl2MSBpbnRlcm1lZGlhdGUwHhcNMTUw MTAxMDAwMDAwWhcNMjUwMTAxMDAwMDAwWjArMQ8wDQYDVQQKEwZHb2xhbmcxGDAW @@ -1481,8 +1447,7 @@ CwUAA4GBADYzYUvaToO/ucBskPdqXV16AaakIhhSENswYVSl97/sODaxsjishKq9 /jt8qszOXCv2vYdUTPNuPqufXLWMoirpuXrr1liJDmedCcAHepY/ -----END CERTIFICATE-----` -const ignoreCNWithSANRoot = ` ------BEGIN CERTIFICATE----- +const ignoreCNWithSANRoot = `-----BEGIN CERTIFICATE----- MIIDPzCCAiegAwIBAgIIJkzCwkNrPHMwDQYJKoZIhvcNAQELBQAwMDEQMA4GA1UE ChMHVEVTVElORzEcMBoGA1UEAxMTKipUZXN0aW5nKiogUm9vdCBDQTAeFw0xNTAx MDEwMDAwMDBaFw0yNTAxMDEwMDAwMDBaMDAxEDAOBgNVBAoTB1RFU1RJTkcxHDAa @@ -1503,8 +1468,7 @@ aSLjI/Ya0zwUARMmyZ3RRGCyhIarPb20mKSaMf1/Nb23pS3k1QgmZhk5pAnXYsWu BJ6bvwEAasFiLGP6Zbdmxb2hIA== -----END CERTIFICATE-----` -const ignoreCNWithSANLeaf = ` ------BEGIN CERTIFICATE----- +const ignoreCNWithSANLeaf = `-----BEGIN CERTIFICATE----- MIIDaTCCAlGgAwIBAgIJAONakvRTxgJhMA0GCSqGSIb3DQEBCwUAMDAxEDAOBgNV BAoTB1RFU1RJTkcxHDAaBgNVBAMTEyoqVGVzdGluZyoqIFJvb3QgQ0EwHhcNMTUw MTAxMDAwMDAwWhcNMjUwMTAxMDAwMDAwWjAsMRAwDgYDVQQKEwdURVNUSU5HMRgw @@ -1526,8 +1490,7 @@ j2kBQyvnyKsXHLAKUoUOpd6t/1PHrfXnGj+HmzZNloJ/BZ1kiWb4eLvMljoLGkZn xZbqP3Krgjj4XNaXjg== -----END CERTIFICATE-----` -const excludedNamesLeaf = ` ------BEGIN CERTIFICATE----- +const excludedNamesLeaf = `-----BEGIN CERTIFICATE----- MIID4DCCAsigAwIBAgIHDUSFtJknhzANBgkqhkiG9w0BAQsFADCBnjELMAkGA1UE BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBHYXRvczEU MBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNlY3VyaXR5 @@ -1549,11 +1512,9 @@ hDt8MCFJ8eSjCyKdtZh1MPMLrLVymmJV+Rc9JUUYM9TIeERkpl0rskcO1YGewkYt qKlWE+0S16+pzsWvKn831uylqwIb8ANBPsCX4aM4muFBHavSWAHgRO+P+yXVw8Q+ VQDnMHUe5PbZd1/+1KKVs1K/CkBCtoHNHp1d/JT+2zUQJphwja9CcgfFdVhSnHL4 oEEOFtqVMIuQfR2isi08qW/JGOHc4sFoLYB8hvdaxKWSE19A ------END CERTIFICATE----- -` +-----END CERTIFICATE-----` -const excludedNamesIntermediate = ` ------BEGIN CERTIFICATE----- +const excludedNamesIntermediate = `-----BEGIN CERTIFICATE----- MIIDzTCCArWgAwIBAgIHDUSFqYeczDANBgkqhkiG9w0BAQsFADCBmTELMAkGA1UE BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBHYXRvczEU MBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNlY3VyaXR5 @@ -1577,8 +1538,7 @@ LbIjZCSfgZnk/LK1KU1j91FI2bc2ULYZvAC1PAg8/zvIgxn6YM2Q7ZsdEgWw0FpS zMBX1/lk4wkFckeUIlkD55Y= -----END CERTIFICATE-----` -const excludedNamesRoot = ` ------BEGIN CERTIFICATE----- +const excludedNamesRoot = `-----BEGIN CERTIFICATE----- MIIEGTCCAwGgAwIBAgIHDUSFpInn/zANBgkqhkiG9w0BAQsFADCBozELMAkGA1UE BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBHYXRvczEU MBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNlY3VyaXR5 @@ -1603,46 +1563,16 @@ yU1yRHUqUYpN0DWFpsPbBqgM6uUAVO2ayBFhPgWUaqkmSbZ/Nq7isGvknaTmcIwT +NQCZDd5eFeU8PpNX7rgaYE4GPq+EEmLVCBYmdctr8QVdqJ//8Xu3+1phjDy -----END CERTIFICATE-----` -const invalidCNRoot = ` ------BEGIN CERTIFICATE----- +const invalidCNRoot = `-----BEGIN CERTIFICATE----- MIIBFjCBvgIJAIsu4r+jb70UMAoGCCqGSM49BAMCMBQxEjAQBgNVBAsMCVRlc3Qg cm9vdDAeFw0xODA3MTExODMyMzVaFw0yODA3MDgxODMyMzVaMBQxEjAQBgNVBAsM CVRlc3Qgcm9vdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABF6oDgMg0LV6YhPj QXaPXYCc2cIyCdqp0ROUksRz0pOLTc5iY2nraUheRUD1vRRneq7GeXOVNn7uXONg oCGMjNwwCgYIKoZIzj0EAwIDRwAwRAIgDSiwgIn8g1lpruYH0QD1GYeoWVunfmrI XzZZl0eW/ugCICgOfXeZ2GGy3wIC0352BaC3a8r5AAb2XSGNe+e9wNN6 ------END CERTIFICATE----- -` - -const invalidCNWithoutSAN = ` -Certificate: - Data: - Version: 1 (0x0) - Serial Number: - 07:ba:bc:b7:d9:ab:0c:02:fe:50:1d:4e:15:a3:0d:e4:11:16:14:a2 - Signature Algorithm: ecdsa-with-SHA256 - Issuer: OU = Test root - Validity - Not Before: Jul 11 18:35:21 2018 GMT - Not After : Jul 8 18:35:21 2028 GMT - Subject: CN = "foo,invalid" - Subject Public Key Info: - Public Key Algorithm: id-ecPublicKey - Public-Key: (256 bit) - pub: - 04:a7:a6:7c:22:33:a7:47:7f:08:93:2d:5f:61:35: - 2e:da:45:67:76:f2:97:73:18:b0:01:12:4a:1a:d5: - b7:6f:41:3c:bb:05:69:f4:06:5d:ff:eb:2b:a7:85: - 0b:4c:f7:45:4e:81:40:7a:a9:c6:1d:bb:ba:d9:b9: - 26:b3:ca:50:90 - ASN1 OID: prime256v1 - NIST CURVE: P-256 - Signature Algorithm: ecdsa-with-SHA256 - 30:45:02:21:00:85:96:75:b6:72:3c:67:12:a0:7f:86:04:81: - d2:dd:c8:67:50:d7:5f:85:c0:54:54:fc:e6:6b:45:08:93:d3: - 2a:02:20:60:86:3e:d6:28:a6:4e:da:dd:6e:95:89:cc:00:76: - 78:1c:03:80:85:a6:5a:0b:eb:c5:f3:9c:2e:df:ef:6e:fa ------BEGIN CERTIFICATE----- +-----END CERTIFICATE-----` + +const invalidCNWithoutSAN = `-----BEGIN CERTIFICATE----- MIIBJDCBywIUB7q8t9mrDAL+UB1OFaMN5BEWFKIwCgYIKoZIzj0EAwIwFDESMBAG A1UECwwJVGVzdCByb290MB4XDTE4MDcxMTE4MzUyMVoXDTI4MDcwODE4MzUyMVow FjEUMBIGA1UEAwwLZm9vLGludmFsaWQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC @@ -1650,38 +1580,9 @@ AASnpnwiM6dHfwiTLV9hNS7aRWd28pdzGLABEkoa1bdvQTy7BWn0Bl3/6yunhQtM 90VOgUB6qcYdu7rZuSazylCQMAoGCCqGSM49BAMCA0gAMEUCIQCFlnW2cjxnEqB/ hgSB0t3IZ1DXX4XAVFT85mtFCJPTKgIgYIY+1iimTtrdbpWJzAB2eBwDgIWmWgvr xfOcLt/vbvo= ------END CERTIFICATE----- -` - -const validCNWithoutSAN = ` -Certificate: - Data: - Version: 1 (0x0) - Serial Number: - 07:ba:bc:b7:d9:ab:0c:02:fe:50:1d:4e:15:a3:0d:e4:11:16:14:a4 - Signature Algorithm: ecdsa-with-SHA256 - Issuer: OU = Test root - Validity - Not Before: Jul 11 18:47:24 2018 GMT - Not After : Jul 8 18:47:24 2028 GMT - Subject: CN = foo.example.com - Subject Public Key Info: - Public Key Algorithm: id-ecPublicKey - Public-Key: (256 bit) - pub: - 04:a7:a6:7c:22:33:a7:47:7f:08:93:2d:5f:61:35: - 2e:da:45:67:76:f2:97:73:18:b0:01:12:4a:1a:d5: - b7:6f:41:3c:bb:05:69:f4:06:5d:ff:eb:2b:a7:85: - 0b:4c:f7:45:4e:81:40:7a:a9:c6:1d:bb:ba:d9:b9: - 26:b3:ca:50:90 - ASN1 OID: prime256v1 - NIST CURVE: P-256 - Signature Algorithm: ecdsa-with-SHA256 - 30:44:02:20:53:6c:d7:b7:59:61:51:72:a5:18:a3:4b:0d:52: - ea:15:fa:d0:93:30:32:54:4b:ed:0f:58:85:b8:a8:1a:82:3b: - 02:20:14:77:4b:0e:7e:4f:0a:4f:64:26:97:dc:d0:ed:aa:67: - 1d:37:85:da:b4:87:ba:25:1c:2a:58:f7:23:11:8b:3d ------BEGIN CERTIFICATE----- +-----END CERTIFICATE-----` + +const validCNWithoutSAN = `-----BEGIN CERTIFICATE----- MIIBJzCBzwIUB7q8t9mrDAL+UB1OFaMN5BEWFKQwCgYIKoZIzj0EAwIwFDESMBAG A1UECwwJVGVzdCByb290MB4XDTE4MDcxMTE4NDcyNFoXDTI4MDcwODE4NDcyNFow GjEYMBYGA1UEAwwPZm9vLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D @@ -1689,48 +1590,9 @@ AQcDQgAEp6Z8IjOnR38Iky1fYTUu2kVndvKXcxiwARJKGtW3b0E8uwVp9AZd/+sr p4ULTPdFToFAeqnGHbu62bkms8pQkDAKBggqhkjOPQQDAgNHADBEAiBTbNe3WWFR cqUYo0sNUuoV+tCTMDJUS+0PWIW4qBqCOwIgFHdLDn5PCk9kJpfc0O2qZx03hdq0 h7olHCpY9yMRiz0= ------END CERTIFICATE----- -` - -const ( - rootWithoutSKID = ` -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 78:29:2a:dc:2f:12:39:7f:c9:33:93:ea:61:39:7d:70 - Signature Algorithm: ecdsa-with-SHA256 - Issuer: O = Acme Co - Validity - Not Before: Feb 4 22:56:34 2019 GMT - Not After : Feb 1 22:56:34 2029 GMT - Subject: O = Acme Co - Subject Public Key Info: - Public Key Algorithm: id-ecPublicKey - Public-Key: (256 bit) - pub: - 04:84:a6:8c:69:53:af:87:4b:39:64:fe:04:24:e6: - d8:fc:d6:46:39:35:0e:92:dc:48:08:7e:02:5f:1e: - 07:53:5c:d9:e0:56:c5:82:07:f6:a3:e2:ad:f6:ad: - be:a0:4e:03:87:39:67:0c:9c:46:91:68:6b:0e:8e: - f8:49:97:9d:5b - ASN1 OID: prime256v1 - NIST CURVE: P-256 - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Key Encipherment, Certificate Sign - X509v3 Extended Key Usage: - TLS Web Server Authentication - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Subject Alternative Name: - DNS:example - Signature Algorithm: ecdsa-with-SHA256 - 30:46:02:21:00:c6:81:61:61:42:8d:37:e7:d0:c3:72:43:44: - 17:bd:84:ff:88:81:68:9a:99:08:ab:3c:3a:c0:1e:ea:8c:ba: - c0:02:21:00:de:c9:fa:e5:5e:c6:e2:db:23:64:43:a9:37:42: - 72:92:7f:6e:89:38:ea:9e:2a:a7:fd:2f:ea:9a:ff:20:21:e7 ------BEGIN CERTIFICATE----- +-----END CERTIFICATE-----` + +const rootWithoutSKID = `-----BEGIN CERTIFICATE----- MIIBbzCCARSgAwIBAgIQeCkq3C8SOX/JM5PqYTl9cDAKBggqhkjOPQQDAjASMRAw DgYDVQQKEwdBY21lIENvMB4XDTE5MDIwNDIyNTYzNFoXDTI5MDIwMTIyNTYzNFow EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABISm @@ -1739,49 +1601,9 @@ ZwycRpFoaw6O+EmXnVujTDBKMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MBIGA1UdEQQLMAmCB2V4YW1wbGUwCgYI KoZIzj0EAwIDSQAwRgIhAMaBYWFCjTfn0MNyQ0QXvYT/iIFompkIqzw6wB7qjLrA AiEA3sn65V7G4tsjZEOpN0Jykn9uiTjqniqn/S/qmv8gIec= ------END CERTIFICATE----- -` - leafWithAKID = ` - Certificate: - Data: - Version: 3 (0x2) - Serial Number: - f0:8a:62:f0:03:84:a2:cf:69:63:ad:71:3b:b6:5d:8c - Signature Algorithm: ecdsa-with-SHA256 - Issuer: O = Acme Co - Validity - Not Before: Feb 4 23:06:52 2019 GMT - Not After : Feb 1 23:06:52 2029 GMT - Subject: O = Acme LLC - Subject Public Key Info: - Public Key Algorithm: id-ecPublicKey - Public-Key: (256 bit) - pub: - 04:5a:4e:4d:fb:ff:17:f7:b6:13:e8:29:45:34:81: - 39:ff:8c:9c:d9:8c:0a:9f:dd:b5:97:4c:2b:20:91: - 1c:4f:6b:be:53:27:66:ec:4a:ad:08:93:6d:66:36: - 0c:02:70:5d:01:ca:7f:c3:29:e9:4f:00:ba:b4:14: - ec:c5:c3:34:b3 - ASN1 OID: prime256v1 - NIST CURVE: P-256 - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Key Encipherment - X509v3 Extended Key Usage: - TLS Web Server Authentication - X509v3 Basic Constraints: critical - CA:FALSE - X509v3 Authority Key Identifier: - keyid:C2:2B:5F:91:78:34:26:09:42:8D:6F:51:B2:C5:AF:4C:0B:DE:6A:42 - - X509v3 Subject Alternative Name: - DNS:example - Signature Algorithm: ecdsa-with-SHA256 - 30:44:02:20:64:e0:ba:56:89:63:ce:22:5e:4f:22:15:fd:3c: - 35:64:9a:3a:6b:7b:9a:32:a0:7f:f7:69:8c:06:f0:00:58:b8: - 02:20:09:e4:9f:6d:8b:9e:38:e1:b6:01:d5:ee:32:a4:94:65: - 93:2a:78:94:bb:26:57:4b:c7:dd:6c:3d:40:2b:63:90 ------BEGIN CERTIFICATE----- +-----END CERTIFICATE-----` + +const leafWithAKID = `-----BEGIN CERTIFICATE----- MIIBjTCCATSgAwIBAgIRAPCKYvADhKLPaWOtcTu2XYwwCgYIKoZIzj0EAwIwEjEQ MA4GA1UEChMHQWNtZSBDbzAeFw0xOTAyMDQyMzA2NTJaFw0yOTAyMDEyMzA2NTJa MBMxETAPBgNVBAoTCEFjbWUgTExDMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE @@ -1791,9 +1613,7 @@ CCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUwitfkXg0JglCjW9R ssWvTAveakIwEgYDVR0RBAswCYIHZXhhbXBsZTAKBggqhkjOPQQDAgNHADBEAiBk 4LpWiWPOIl5PIhX9PDVkmjpre5oyoH/3aYwG8ABYuAIgCeSfbYueOOG2AdXuMqSU ZZMqeJS7JldLx91sPUArY5A= ------END CERTIFICATE----- -` -) +-----END CERTIFICATE-----` var unknownAuthorityErrorTests = []struct { cert string @@ -2129,3 +1949,33 @@ func TestLongChain(t *testing.T) { } t.Logf("verification took %v", time.Since(start)) } + +func TestSystemRootsError(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Windows does not use (or support) systemRoots") + } + + defer func(oldSystemRoots *CertPool) { systemRoots = oldSystemRoots }(systemRootsPool()) + + opts := VerifyOptions{ + Intermediates: NewCertPool(), + DNSName: "www.google.com", + CurrentTime: time.Unix(1395785200, 0), + } + + if ok := opts.Intermediates.AppendCertsFromPEM([]byte(giag2Intermediate)); !ok { + t.Fatalf("failed to parse intermediate") + } + + leaf, err := certificateFromPEM(googleLeaf) + if err != nil { + t.Fatalf("failed to parse leaf: %v", err) + } + + systemRoots = nil + + _, err = leaf.Verify(opts) + if _, ok := err.(SystemRootsError); !ok { + t.Errorf("error was not SystemRootsError: %v", err) + } +} diff --git a/src/encoding/binary/varint.go b/src/encoding/binary/varint.go index bcb8ac9a45..38af61075c 100644 --- a/src/encoding/binary/varint.go +++ b/src/encoding/binary/varint.go @@ -106,13 +106,13 @@ var overflow = errors.New("binary: varint overflows a 64-bit integer") func ReadUvarint(r io.ByteReader) (uint64, error) { var x uint64 var s uint - for i := 0; ; i++ { + for i := 0; i < MaxVarintLen64; i++ { b, err := r.ReadByte() if err != nil { return x, err } if b < 0x80 { - if i > 9 || i == 9 && b > 1 { + if i == 9 && b > 1 { return x, overflow } return x | uint64(b)<<s, nil @@ -120,6 +120,7 @@ func ReadUvarint(r io.ByteReader) (uint64, error) { x |= uint64(b&0x7f) << s s += 7 } + return x, overflow } // ReadVarint reads an encoded signed integer from r and returns it as an int64. diff --git a/src/encoding/binary/varint_test.go b/src/encoding/binary/varint_test.go index ca411ecbd6..6ef4c99505 100644 --- a/src/encoding/binary/varint_test.go +++ b/src/encoding/binary/varint_test.go @@ -121,21 +121,27 @@ func TestBufferTooSmall(t *testing.T) { } } -func testOverflow(t *testing.T, buf []byte, n0 int, err0 error) { +func testOverflow(t *testing.T, buf []byte, x0 uint64, n0 int, err0 error) { x, n := Uvarint(buf) if x != 0 || n != n0 { t.Errorf("Uvarint(%v): got x = %d, n = %d; want 0, %d", buf, x, n, n0) } - x, err := ReadUvarint(bytes.NewReader(buf)) - if x != 0 || err != err0 { - t.Errorf("ReadUvarint(%v): got x = %d, err = %s; want 0, %s", buf, x, err, err0) + r := bytes.NewReader(buf) + len := r.Len() + x, err := ReadUvarint(r) + if x != x0 || err != err0 { + t.Errorf("ReadUvarint(%v): got x = %d, err = %s; want %d, %s", buf, x, err, x0, err0) + } + if read := len - r.Len(); read > MaxVarintLen64 { + t.Errorf("ReadUvarint(%v): read more than MaxVarintLen64 bytes, got %d", buf, read) } } func TestOverflow(t *testing.T) { - testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x2}, -10, overflow) - testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x1, 0, 0}, -13, overflow) + testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x2}, 0, -10, overflow) + testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x1, 0, 0}, 0, -13, overflow) + testOverflow(t, []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 1<<64-1, 0, overflow) // 11 bytes, should overflow } func TestNonCanonicalZero(t *testing.T) { diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 62d6e6296b..f8b152e5d4 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -10,6 +10,7 @@ package build import ( "bytes" "fmt" + "internal/testenv" "io/ioutil" "os" "path/filepath" @@ -385,7 +386,7 @@ var depsRules = ` < golang.org/x/crypto/poly1305 < golang.org/x/crypto/chacha20poly1305 < golang.org/x/crypto/hkdf - < crypto/x509/internal/macOS + < crypto/x509/internal/macos < crypto/x509/pkix < crypto/x509 < crypto/tls; @@ -517,8 +518,7 @@ func listStdPkgs(goroot string) ([]string, error) { } func TestDependencies(t *testing.T) { - iOS := runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" - if iOS { + if !testenv.HasSrc() { // Tests run in a limited file system and we do not // provide access to every source file. t.Skipf("skipping on %s/%s, missing full GOROOT", runtime.GOOS, runtime.GOARCH) @@ -806,3 +806,23 @@ func (p *depsParser) nextToken() string { } } } + +// TestStdlibLowercase tests that all standard library package names are +// lowercase. See Issue 40065. +func TestStdlibLowercase(t *testing.T) { + if !testenv.HasSrc() { + t.Skipf("skipping on %s/%s, missing full GOROOT", runtime.GOOS, runtime.GOARCH) + } + + ctxt := Default + all, err := listStdPkgs(ctxt.GOROOT) + if err != nil { + t.Fatal(err) + } + + for _, pkgname := range all { + if strings.ToLower(pkgname) != pkgname { + t.Errorf("package %q should not use upper-case path", pkgname) + } + } +} diff --git a/src/go/format/format.go b/src/go/format/format.go index 84afbb066a..a603d9630e 100644 --- a/src/go/format/format.go +++ b/src/go/format/format.go @@ -27,7 +27,13 @@ import ( // Keep these in sync with cmd/gofmt/gofmt.go. const ( tabWidth = 8 - printerMode = printer.UseSpaces | printer.TabIndent | printer.StdFormat + printerMode = printer.UseSpaces | printer.TabIndent | printerNormalizeNumbers + + // printerNormalizeNumbers means to canonicalize number literal prefixes + // and exponents while printing. See https://golang.org/doc/go1.13#gofmt. + // + // This value is defined in go/printer specifically for go/format and cmd/gofmt. + printerNormalizeNumbers = 1 << 30 ) var config = printer.Config{Mode: printerMode, Tabwidth: tabWidth} diff --git a/src/go/format/format_test.go b/src/go/format/format_test.go index aee51e2da1..58e088ede3 100644 --- a/src/go/format/format_test.go +++ b/src/go/format/format_test.go @@ -58,8 +58,8 @@ func TestNode(t *testing.T) { diff(t, buf.Bytes(), src) } -// Node is documented to not modify the AST. Test that it is so, even when -// formatting changes are applied due to printer.StdFormat mode being used. +// Node is documented to not modify the AST. +// Test that it is so even when numbers are normalized. func TestNodeNoModify(t *testing.T) { const ( src = "package p\n\nconst _ = 0000000123i\n" diff --git a/src/go/printer/nodes.go b/src/go/printer/nodes.go index 0360c4606e..95b9e91891 100644 --- a/src/go/printer/nodes.go +++ b/src/go/printer/nodes.go @@ -791,8 +791,8 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) { } case *ast.BasicLit: - if p.Config.Mode&StdFormat != 0 { - x = normalizeNumbers(x) + if p.Config.Mode&normalizeNumbers != 0 { + x = normalizedNumber(x) } p.print(x) @@ -974,11 +974,15 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) { } } -// normalizeNumbers rewrites base prefixes and exponents to -// use lower-case letters, and removes leading 0's from -// integer imaginary literals. It leaves hexadecimal digits -// alone. -func normalizeNumbers(lit *ast.BasicLit) *ast.BasicLit { +// normalizedNumber rewrites base prefixes and exponents +// of numbers to use lower-case letters (0X123 to 0x123 and 1.2E3 to 1.2e3), +// and removes leading 0's from integer imaginary literals (0765i to 765i). +// It leaves hexadecimal digits alone. +// +// normalizedNumber doesn't modify the ast.BasicLit value lit points to. +// If lit is not a number or a number in canonical format already, +// lit is returned as is. Otherwise a new ast.BasicLit is created. +func normalizedNumber(lit *ast.BasicLit) *ast.BasicLit { if lit.Kind != token.INT && lit.Kind != token.FLOAT && lit.Kind != token.IMAG { return lit // not a number - nothing to do } diff --git a/src/go/printer/performance_test.go b/src/go/printer/performance_test.go index 3f34bfcc32..2e67154e6b 100644 --- a/src/go/printer/performance_test.go +++ b/src/go/printer/performance_test.go @@ -20,7 +20,7 @@ import ( var testfile *ast.File func testprint(out io.Writer, file *ast.File) { - if err := (&Config{TabIndent | UseSpaces | StdFormat, 8, 0}).Fprint(out, fset, file); err != nil { + if err := (&Config{TabIndent | UseSpaces | normalizeNumbers, 8, 0}).Fprint(out, fset, file); err != nil { log.Fatalf("print error: %s", err) } } diff --git a/src/go/printer/printer.go b/src/go/printer/printer.go index 9d0add40b6..0077afeaff 100644 --- a/src/go/printer/printer.go +++ b/src/go/printer/printer.go @@ -1276,7 +1276,22 @@ const ( TabIndent // use tabs for indentation independent of UseSpaces UseSpaces // use spaces instead of tabs for alignment SourcePos // emit //line directives to preserve original source positions - StdFormat // apply standard formatting changes (exact byte output may change between versions of Go) +) + +// The mode below is not included in printer's public API because +// editing code text is deemed out of scope. Because this mode is +// unexported, it's also possible to modify or remove it based on +// the evolving needs of go/format and cmd/gofmt without breaking +// users. See discussion in CL 240683. +const ( + // normalizeNumbers means to canonicalize number + // literal prefixes and exponents while printing. + // + // This value is known in and used by go/format and cmd/gofmt. + // It is currently more convenient and performant for those + // packages to apply number normalization during printing, + // rather than by modifying the AST in advance. + normalizeNumbers Mode = 1 << 30 ) // A Config node controls the output of Fprint. diff --git a/src/go/printer/printer_test.go b/src/go/printer/printer_test.go index 1e9d47ce73..b64bc6bfb7 100644 --- a/src/go/printer/printer_test.go +++ b/src/go/printer/printer_test.go @@ -33,7 +33,7 @@ type checkMode uint const ( export checkMode = 1 << iota rawFormat - stdFormat + normNumber idempotent ) @@ -58,8 +58,8 @@ func format(src []byte, mode checkMode) ([]byte, error) { if mode&rawFormat != 0 { cfg.Mode |= RawFormat } - if mode&stdFormat != 0 { - cfg.Mode |= StdFormat + if mode&normNumber != 0 { + cfg.Mode |= normalizeNumbers } // print AST @@ -205,7 +205,7 @@ var data = []entry{ {"slow.input", "slow.golden", idempotent}, {"complit.input", "complit.x", export}, {"go2numbers.input", "go2numbers.golden", idempotent}, - {"go2numbers.input", "go2numbers.stdfmt", stdFormat | idempotent}, + {"go2numbers.input", "go2numbers.norm", normNumber | idempotent}, } func TestFiles(t *testing.T) { diff --git a/src/go/printer/testdata/go2numbers.stdfmt b/src/go/printer/testdata/go2numbers.norm index 855f0fc608..855f0fc608 100644 --- a/src/go/printer/testdata/go2numbers.stdfmt +++ b/src/go/printer/testdata/go2numbers.norm diff --git a/src/html/template/clone_test.go b/src/html/template/clone_test.go index e292321d93..c9c619f0d4 100644 --- a/src/html/template/clone_test.go +++ b/src/html/template/clone_test.go @@ -15,7 +15,7 @@ import ( "text/template/parse" ) -func TestAddParseTree(t *testing.T) { +func TestAddParseTreeHTML(t *testing.T) { root := Must(New("root").Parse(`{{define "a"}} {{.}} {{template "b"}} {{.}} "></a>{{end}}`)) tree, err := parse.Parse("t", `{{define "b"}}<a href="{{end}}`, "", "", nil, nil) if err != nil { diff --git a/src/html/template/content_test.go b/src/html/template/content_test.go index bd8652769b..b7a39d4814 100644 --- a/src/html/template/content_test.go +++ b/src/html/template/content_test.go @@ -401,11 +401,11 @@ func TestTypedContent(t *testing.T) { } // Test that we print using the String method. Was issue 3073. -type stringer struct { +type myStringer struct { v int } -func (s *stringer) String() string { +func (s *myStringer) String() string { return fmt.Sprintf("string=%d", s.v) } @@ -418,7 +418,7 @@ func (s *errorer) Error() string { } func TestStringer(t *testing.T) { - s := &stringer{3} + s := &myStringer{3} b := new(bytes.Buffer) tmpl := Must(New("x").Parse("{{.}}")) if err := tmpl.Execute(b, s); err != nil { diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go index c709660f06..fbc84a7592 100644 --- a/src/html/template/escape_test.go +++ b/src/html/template/escape_test.go @@ -1821,7 +1821,7 @@ func TestIndirectPrint(t *testing.T) { } // This is a test for issue 3272. -func TestEmptyTemplate(t *testing.T) { +func TestEmptyTemplateHTML(t *testing.T) { page := Must(New("page").ParseFiles(os.DevNull)) if err := page.ExecuteTemplate(os.Stdout, "page", "nothing"); err == nil { t.Fatal("expected error") diff --git a/src/html/template/exec_test.go b/src/html/template/exec_test.go new file mode 100644 index 0000000000..ec2bfcccab --- /dev/null +++ b/src/html/template/exec_test.go @@ -0,0 +1,1708 @@ +// Copyright 2011 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. + +// Tests for template execution, copied from text/template. + +package template + +import ( + "bytes" + "errors" + "flag" + "fmt" + "io/ioutil" + "reflect" + "strings" + "testing" + "text/template" +) + +var debug = flag.Bool("debug", false, "show the errors produced by the tests") + +// T has lots of interesting pieces to use to test execution. +type T struct { + // Basics + True bool + I int + U16 uint16 + X, S string + FloatZero float64 + ComplexZero complex128 + // Nested structs. + U *U + // Struct with String method. + V0 V + V1, V2 *V + // Struct with Error method. + W0 W + W1, W2 *W + // Slices + SI []int + SICap []int + SIEmpty []int + SB []bool + // Arrays + AI [3]int + // Maps + MSI map[string]int + MSIone map[string]int // one element, for deterministic output + MSIEmpty map[string]int + MXI map[interface{}]int + MII map[int]int + MI32S map[int32]string + MI64S map[int64]string + MUI32S map[uint32]string + MUI64S map[uint64]string + MI8S map[int8]string + MUI8S map[uint8]string + SMSI []map[string]int + // Empty interfaces; used to see if we can dig inside one. + Empty0 interface{} // nil + Empty1 interface{} + Empty2 interface{} + Empty3 interface{} + Empty4 interface{} + // Non-empty interfaces. + NonEmptyInterface I + NonEmptyInterfacePtS *I + NonEmptyInterfaceNil I + NonEmptyInterfaceTypedNil I + // Stringer. + Str fmt.Stringer + Err error + // Pointers + PI *int + PS *string + PSI *[]int + NIL *int + // Function (not method) + BinaryFunc func(string, string) string + VariadicFunc func(...string) string + VariadicFuncInt func(int, ...string) string + NilOKFunc func(*int) bool + ErrFunc func() (string, error) + PanicFunc func() string + // Template to test evaluation of templates. + Tmpl *Template + // Unexported field; cannot be accessed by template. + unexported int +} + +type S []string + +func (S) Method0() string { + return "M0" +} + +type U struct { + V string +} + +type V struct { + j int +} + +func (v *V) String() string { + if v == nil { + return "nilV" + } + return fmt.Sprintf("<%d>", v.j) +} + +type W struct { + k int +} + +func (w *W) Error() string { + if w == nil { + return "nilW" + } + return fmt.Sprintf("[%d]", w.k) +} + +var siVal = I(S{"a", "b"}) + +var tVal = &T{ + True: true, + I: 17, + U16: 16, + X: "x", + S: "xyz", + U: &U{"v"}, + V0: V{6666}, + V1: &V{7777}, // leave V2 as nil + W0: W{888}, + W1: &W{999}, // leave W2 as nil + SI: []int{3, 4, 5}, + SICap: make([]int, 5, 10), + AI: [3]int{3, 4, 5}, + SB: []bool{true, false}, + MSI: map[string]int{"one": 1, "two": 2, "three": 3}, + MSIone: map[string]int{"one": 1}, + MXI: map[interface{}]int{"one": 1}, + MII: map[int]int{1: 1}, + MI32S: map[int32]string{1: "one", 2: "two"}, + MI64S: map[int64]string{2: "i642", 3: "i643"}, + MUI32S: map[uint32]string{2: "u322", 3: "u323"}, + MUI64S: map[uint64]string{2: "ui642", 3: "ui643"}, + MI8S: map[int8]string{2: "i82", 3: "i83"}, + MUI8S: map[uint8]string{2: "u82", 3: "u83"}, + SMSI: []map[string]int{ + {"one": 1, "two": 2}, + {"eleven": 11, "twelve": 12}, + }, + Empty1: 3, + Empty2: "empty2", + Empty3: []int{7, 8}, + Empty4: &U{"UinEmpty"}, + NonEmptyInterface: &T{X: "x"}, + NonEmptyInterfacePtS: &siVal, + NonEmptyInterfaceTypedNil: (*T)(nil), + Str: bytes.NewBuffer([]byte("foozle")), + Err: errors.New("erroozle"), + PI: newInt(23), + PS: newString("a string"), + PSI: newIntSlice(21, 22, 23), + BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) }, + VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") }, + VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") }, + NilOKFunc: func(s *int) bool { return s == nil }, + ErrFunc: func() (string, error) { return "bla", nil }, + PanicFunc: func() string { panic("test panic") }, + Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X +} + +var tSliceOfNil = []*T{nil} + +// A non-empty interface. +type I interface { + Method0() string +} + +var iVal I = tVal + +// Helpers for creation. +func newInt(n int) *int { + return &n +} + +func newString(s string) *string { + return &s +} + +func newIntSlice(n ...int) *[]int { + p := new([]int) + *p = make([]int, len(n)) + copy(*p, n) + return p +} + +// Simple methods with and without arguments. +func (t *T) Method0() string { + return "M0" +} + +func (t *T) Method1(a int) int { + return a +} + +func (t *T) Method2(a uint16, b string) string { + return fmt.Sprintf("Method2: %d %s", a, b) +} + +func (t *T) Method3(v interface{}) string { + return fmt.Sprintf("Method3: %v", v) +} + +func (t *T) Copy() *T { + n := new(T) + *n = *t + return n +} + +func (t *T) MAdd(a int, b []int) []int { + v := make([]int, len(b)) + for i, x := range b { + v[i] = x + a + } + return v +} + +var myError = errors.New("my error") + +// MyError returns a value and an error according to its argument. +func (t *T) MyError(error bool) (bool, error) { + if error { + return true, myError + } + return false, nil +} + +// A few methods to test chaining. +func (t *T) GetU() *U { + return t.U +} + +func (u *U) TrueFalse(b bool) string { + if b { + return "true" + } + return "" +} + +func typeOf(arg interface{}) string { + return fmt.Sprintf("%T", arg) +} + +type execTest struct { + name string + input string + output string + data interface{} + ok bool +} + +// bigInt and bigUint are hex string representing numbers either side +// of the max int boundary. +// We do it this way so the test doesn't depend on ints being 32 bits. +var ( + bigInt = fmt.Sprintf("0x%x", int(1<<uint(reflect.TypeOf(0).Bits()-1)-1)) + bigUint = fmt.Sprintf("0x%x", uint(1<<uint(reflect.TypeOf(0).Bits()-1))) +) + +var execTests = []execTest{ + // Trivial cases. + {"empty", "", "", nil, true}, + {"text", "some text", "some text", nil, true}, + {"nil action", "{{nil}}", "", nil, false}, + + // Ideal constants. + {"ideal int", "{{typeOf 3}}", "int", 0, true}, + {"ideal float", "{{typeOf 1.0}}", "float64", 0, true}, + {"ideal exp float", "{{typeOf 1e1}}", "float64", 0, true}, + {"ideal complex", "{{typeOf 1i}}", "complex128", 0, true}, + {"ideal int", "{{typeOf " + bigInt + "}}", "int", 0, true}, + {"ideal too big", "{{typeOf " + bigUint + "}}", "", 0, false}, + {"ideal nil without type", "{{nil}}", "", 0, false}, + + // Fields of structs. + {".X", "-{{.X}}-", "-x-", tVal, true}, + {".U.V", "-{{.U.V}}-", "-v-", tVal, true}, + {".unexported", "{{.unexported}}", "", tVal, false}, + + // Fields on maps. + {"map .one", "{{.MSI.one}}", "1", tVal, true}, + {"map .two", "{{.MSI.two}}", "2", tVal, true}, + {"map .NO", "{{.MSI.NO}}", "", tVal, true}, // NOTE: <no value> in text/template + {"map .one interface", "{{.MXI.one}}", "1", tVal, true}, + {"map .WRONG args", "{{.MSI.one 1}}", "", tVal, false}, + {"map .WRONG type", "{{.MII.one}}", "", tVal, false}, + + // Dots of all kinds to test basic evaluation. + {"dot int", "<{{.}}>", "<13>", 13, true}, + {"dot uint", "<{{.}}>", "<14>", uint(14), true}, + {"dot float", "<{{.}}>", "<15.1>", 15.1, true}, + {"dot bool", "<{{.}}>", "<true>", true, true}, + {"dot complex", "<{{.}}>", "<(16.2-17i)>", 16.2 - 17i, true}, + {"dot string", "<{{.}}>", "<hello>", "hello", true}, + {"dot slice", "<{{.}}>", "<[-1 -2 -3]>", []int{-1, -2, -3}, true}, + {"dot map", "<{{.}}>", "<map[two:22]>", map[string]int{"two": 22}, true}, + {"dot struct", "<{{.}}>", "<{7 seven}>", struct { + a int + b string + }{7, "seven"}, true}, + + // Variables. + {"$ int", "{{$}}", "123", 123, true}, + {"$.I", "{{$.I}}", "17", tVal, true}, + {"$.U.V", "{{$.U.V}}", "v", tVal, true}, + {"declare in action", "{{$x := $.U.V}}{{$x}}", "v", tVal, true}, + {"simple assignment", "{{$x := 2}}{{$x = 3}}{{$x}}", "3", tVal, true}, + {"nested assignment", + "{{$x := 2}}{{if true}}{{$x = 3}}{{end}}{{$x}}", + "3", tVal, true}, + {"nested assignment changes the last declaration", + "{{$x := 1}}{{if true}}{{$x := 2}}{{if true}}{{$x = 3}}{{end}}{{end}}{{$x}}", + "1", tVal, true}, + + // Type with String method. + {"V{6666}.String()", "-{{.V0}}-", "-{6666}-", tVal, true}, // NOTE: -<6666>- in text/template + {"&V{7777}.String()", "-{{.V1}}-", "-<7777>-", tVal, true}, + {"(*V)(nil).String()", "-{{.V2}}-", "-nilV-", tVal, true}, + + // Type with Error method. + {"W{888}.Error()", "-{{.W0}}-", "-{888}-", tVal, true}, // NOTE: -[888] in text/template + {"&W{999}.Error()", "-{{.W1}}-", "-[999]-", tVal, true}, + {"(*W)(nil).Error()", "-{{.W2}}-", "-nilW-", tVal, true}, + + // Pointers. + {"*int", "{{.PI}}", "23", tVal, true}, + {"*string", "{{.PS}}", "a string", tVal, true}, + {"*[]int", "{{.PSI}}", "[21 22 23]", tVal, true}, + {"*[]int[1]", "{{index .PSI 1}}", "22", tVal, true}, + {"NIL", "{{.NIL}}", "<nil>", tVal, true}, + + // Empty interfaces holding values. + {"empty nil", "{{.Empty0}}", "", tVal, true}, // NOTE: <no value> in text/template + {"empty with int", "{{.Empty1}}", "3", tVal, true}, + {"empty with string", "{{.Empty2}}", "empty2", tVal, true}, + {"empty with slice", "{{.Empty3}}", "[7 8]", tVal, true}, + {"empty with struct", "{{.Empty4}}", "{UinEmpty}", tVal, true}, + {"empty with struct, field", "{{.Empty4.V}}", "UinEmpty", tVal, true}, + + // Edge cases with <no value> with an interface value + {"field on interface", "{{.foo}}", "", nil, true}, // NOTE: <no value> in text/template + {"field on parenthesized interface", "{{(.).foo}}", "", nil, true}, // NOTE: <no value> in text/template + + // Issue 31810: Parenthesized first element of pipeline with arguments. + // See also TestIssue31810. + {"unparenthesized non-function", "{{1 2}}", "", nil, false}, + {"parenthesized non-function", "{{(1) 2}}", "", nil, false}, + {"parenthesized non-function with no args", "{{(1)}}", "1", nil, true}, // This is fine. + + // Method calls. + {".Method0", "-{{.Method0}}-", "-M0-", tVal, true}, + {".Method1(1234)", "-{{.Method1 1234}}-", "-1234-", tVal, true}, + {".Method1(.I)", "-{{.Method1 .I}}-", "-17-", tVal, true}, + {".Method2(3, .X)", "-{{.Method2 3 .X}}-", "-Method2: 3 x-", tVal, true}, + {".Method2(.U16, `str`)", "-{{.Method2 .U16 `str`}}-", "-Method2: 16 str-", tVal, true}, + {".Method2(.U16, $x)", "{{if $x := .X}}-{{.Method2 .U16 $x}}{{end}}-", "-Method2: 16 x-", tVal, true}, + {".Method3(nil constant)", "-{{.Method3 nil}}-", "-Method3: <nil>-", tVal, true}, + {".Method3(nil value)", "-{{.Method3 .MXI.unset}}-", "-Method3: <nil>-", tVal, true}, + {"method on var", "{{if $x := .}}-{{$x.Method2 .U16 $x.X}}{{end}}-", "-Method2: 16 x-", tVal, true}, + {"method on chained var", + "{{range .MSIone}}{{if $.U.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}", + "true", tVal, true}, + {"chained method", + "{{range .MSIone}}{{if $.GetU.TrueFalse $.True}}{{$.U.TrueFalse $.True}}{{else}}WRONG{{end}}{{end}}", + "true", tVal, true}, + {"chained method on variable", + "{{with $x := .}}{{with .SI}}{{$.GetU.TrueFalse $.True}}{{end}}{{end}}", + "true", tVal, true}, + {".NilOKFunc not nil", "{{call .NilOKFunc .PI}}", "false", tVal, true}, + {".NilOKFunc nil", "{{call .NilOKFunc nil}}", "true", tVal, true}, + {"method on nil value from slice", "-{{range .}}{{.Method1 1234}}{{end}}-", "-1234-", tSliceOfNil, true}, + {"method on typed nil interface value", "{{.NonEmptyInterfaceTypedNil.Method0}}", "M0", tVal, true}, + + // Function call builtin. + {".BinaryFunc", "{{call .BinaryFunc `1` `2`}}", "[1=2]", tVal, true}, + {".VariadicFunc0", "{{call .VariadicFunc}}", "<>", tVal, true}, + {".VariadicFunc2", "{{call .VariadicFunc `he` `llo`}}", "<he+llo>", tVal, true}, + {".VariadicFuncInt", "{{call .VariadicFuncInt 33 `he` `llo`}}", "33=<he+llo>", tVal, true}, + {"if .BinaryFunc call", "{{ if .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{end}}", "[1=2]", tVal, true}, + {"if not .BinaryFunc call", "{{ if not .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{else}}No{{end}}", "No", tVal, true}, + {"Interface Call", `{{stringer .S}}`, "foozle", map[string]interface{}{"S": bytes.NewBufferString("foozle")}, true}, + {".ErrFunc", "{{call .ErrFunc}}", "bla", tVal, true}, + {"call nil", "{{call nil}}", "", tVal, false}, + + // Erroneous function calls (check args). + {".BinaryFuncTooFew", "{{call .BinaryFunc `1`}}", "", tVal, false}, + {".BinaryFuncTooMany", "{{call .BinaryFunc `1` `2` `3`}}", "", tVal, false}, + {".BinaryFuncBad0", "{{call .BinaryFunc 1 3}}", "", tVal, false}, + {".BinaryFuncBad1", "{{call .BinaryFunc `1` 3}}", "", tVal, false}, + {".VariadicFuncBad0", "{{call .VariadicFunc 3}}", "", tVal, false}, + {".VariadicFuncIntBad0", "{{call .VariadicFuncInt}}", "", tVal, false}, + {".VariadicFuncIntBad`", "{{call .VariadicFuncInt `x`}}", "", tVal, false}, + {".VariadicFuncNilBad", "{{call .VariadicFunc nil}}", "", tVal, false}, + + // Pipelines. + {"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 M0-", tVal, true}, + {"pipeline func", "-{{call .VariadicFunc `llo` | call .VariadicFunc `he` }}-", "-<he+<llo>>-", tVal, true}, + + // Nil values aren't missing arguments. + {"nil pipeline", "{{ .Empty0 | call .NilOKFunc }}", "true", tVal, true}, + {"nil call arg", "{{ call .NilOKFunc .Empty0 }}", "true", tVal, true}, + {"bad nil pipeline", "{{ .Empty0 | .VariadicFunc }}", "", tVal, false}, + + // Parenthesized expressions + {"parens in pipeline", "{{printf `%d %d %d` (1) (2 | add 3) (add 4 (add 5 6))}}", "1 5 15", tVal, true}, + + // Parenthesized expressions with field accesses + {"parens: $ in paren", "{{($).X}}", "x", tVal, true}, + {"parens: $.GetU in paren", "{{($.GetU).V}}", "v", tVal, true}, + {"parens: $ in paren in pipe", "{{($ | echo).X}}", "x", tVal, true}, + {"parens: spaces and args", `{{(makemap "up" "down" "left" "right").left}}`, "right", tVal, true}, + + // If. + {"if true", "{{if true}}TRUE{{end}}", "TRUE", tVal, true}, + {"if false", "{{if false}}TRUE{{else}}FALSE{{end}}", "FALSE", tVal, true}, + {"if nil", "{{if nil}}TRUE{{end}}", "", tVal, false}, + {"if on typed nil interface value", "{{if .NonEmptyInterfaceTypedNil}}TRUE{{ end }}", "", tVal, true}, + {"if 1", "{{if 1}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, + {"if 0", "{{if 0}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"if 1.5", "{{if 1.5}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, + {"if 0.0", "{{if .FloatZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"if 1.5i", "{{if 1.5i}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, + {"if 0.0i", "{{if .ComplexZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"if emptystring", "{{if ``}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"if string", "{{if `notempty`}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, + {"if emptyslice", "{{if .SIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"if slice", "{{if .SI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, + {"if emptymap", "{{if .MSIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"if map", "{{if .MSI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, + {"if map unset", "{{if .MXI.none}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"if map not unset", "{{if not .MXI.none}}ZERO{{else}}NON-ZERO{{end}}", "ZERO", tVal, true}, + {"if $x with $y int", "{{if $x := true}}{{with $y := .I}}{{$x}},{{$y}}{{end}}{{end}}", "true,17", tVal, true}, + {"if $x with $x int", "{{if $x := true}}{{with $x := .I}}{{$x}},{{end}}{{$x}}{{end}}", "17,true", tVal, true}, + {"if else if", "{{if false}}FALSE{{else if true}}TRUE{{end}}", "TRUE", tVal, true}, + {"if else chain", "{{if eq 1 3}}1{{else if eq 2 3}}2{{else if eq 3 3}}3{{end}}", "3", tVal, true}, + + // Print etc. + {"print", `{{print "hello, print"}}`, "hello, print", tVal, true}, + {"print 123", `{{print 1 2 3}}`, "1 2 3", tVal, true}, + {"print nil", `{{print nil}}`, "<nil>", tVal, true}, + {"println", `{{println 1 2 3}}`, "1 2 3\n", tVal, true}, + {"printf int", `{{printf "%04x" 127}}`, "007f", tVal, true}, + {"printf float", `{{printf "%g" 3.5}}`, "3.5", tVal, true}, + {"printf complex", `{{printf "%g" 1+7i}}`, "(1+7i)", tVal, true}, + {"printf string", `{{printf "%s" "hello"}}`, "hello", tVal, true}, + {"printf function", `{{printf "%#q" zeroArgs}}`, "`zeroArgs`", tVal, true}, + {"printf field", `{{printf "%s" .U.V}}`, "v", tVal, true}, + {"printf method", `{{printf "%s" .Method0}}`, "M0", tVal, true}, + {"printf dot", `{{with .I}}{{printf "%d" .}}{{end}}`, "17", tVal, true}, + {"printf var", `{{with $x := .I}}{{printf "%d" $x}}{{end}}`, "17", tVal, true}, + {"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) M0", tVal, true}, + + // HTML. + {"html", `{{html "<script>alert(\"XSS\");</script>"}}`, + "<script>alert("XSS");</script>", nil, true}, + {"html pipeline", `{{printf "<script>alert(\"XSS\");</script>" | html}}`, + "<script>alert("XSS");</script>", nil, true}, + {"html", `{{html .PS}}`, "a string", tVal, true}, + {"html typed nil", `{{html .NIL}}`, "<nil>", tVal, true}, + {"html untyped nil", `{{html .Empty0}}`, "<nil>", tVal, true}, // NOTE: "<no value>" in text/template + + // JavaScript. + {"js", `{{js .}}`, `It\'d be nice.`, `It'd be nice.`, true}, + + // URL query. + {"urlquery", `{{"http://www.example.org/"|urlquery}}`, "http%3A%2F%2Fwww.example.org%2F", nil, true}, + + // Booleans + {"not", "{{not true}} {{not false}}", "false true", nil, true}, + {"and", "{{and false 0}} {{and 1 0}} {{and 0 true}} {{and 1 1}}", "false 0 0 1", nil, true}, + {"or", "{{or 0 0}} {{or 1 0}} {{or 0 true}} {{or 1 1}}", "0 1 true 1", nil, true}, + {"boolean if", "{{if and true 1 `hi`}}TRUE{{else}}FALSE{{end}}", "TRUE", tVal, true}, + {"boolean if not", "{{if and true 1 `hi` | not}}TRUE{{else}}FALSE{{end}}", "FALSE", nil, true}, + + // Indexing. + {"slice[0]", "{{index .SI 0}}", "3", tVal, true}, + {"slice[1]", "{{index .SI 1}}", "4", tVal, true}, + {"slice[HUGE]", "{{index .SI 10}}", "", tVal, false}, + {"slice[WRONG]", "{{index .SI `hello`}}", "", tVal, false}, + {"slice[nil]", "{{index .SI nil}}", "", tVal, false}, + {"map[one]", "{{index .MSI `one`}}", "1", tVal, true}, + {"map[two]", "{{index .MSI `two`}}", "2", tVal, true}, + {"map[NO]", "{{index .MSI `XXX`}}", "0", tVal, true}, + {"map[nil]", "{{index .MSI nil}}", "", tVal, false}, + {"map[``]", "{{index .MSI ``}}", "0", tVal, true}, + {"map[WRONG]", "{{index .MSI 10}}", "", tVal, false}, + {"double index", "{{index .SMSI 1 `eleven`}}", "11", tVal, true}, + {"nil[1]", "{{index nil 1}}", "", tVal, false}, + {"map MI64S", "{{index .MI64S 2}}", "i642", tVal, true}, + {"map MI32S", "{{index .MI32S 2}}", "two", tVal, true}, + {"map MUI64S", "{{index .MUI64S 3}}", "ui643", tVal, true}, + {"map MI8S", "{{index .MI8S 3}}", "i83", tVal, true}, + {"map MUI8S", "{{index .MUI8S 2}}", "u82", tVal, true}, + {"index of an interface field", "{{index .Empty3 0}}", "7", tVal, true}, + + // Slicing. + {"slice[:]", "{{slice .SI}}", "[3 4 5]", tVal, true}, + {"slice[1:]", "{{slice .SI 1}}", "[4 5]", tVal, true}, + {"slice[1:2]", "{{slice .SI 1 2}}", "[4]", tVal, true}, + {"slice[-1:]", "{{slice .SI -1}}", "", tVal, false}, + {"slice[1:-2]", "{{slice .SI 1 -2}}", "", tVal, false}, + {"slice[1:2:-1]", "{{slice .SI 1 2 -1}}", "", tVal, false}, + {"slice[2:1]", "{{slice .SI 2 1}}", "", tVal, false}, + {"slice[2:2:1]", "{{slice .SI 2 2 1}}", "", tVal, false}, + {"out of range", "{{slice .SI 4 5}}", "", tVal, false}, + {"out of range", "{{slice .SI 2 2 5}}", "", tVal, false}, + {"len(s) < indexes < cap(s)", "{{slice .SICap 6 10}}", "[0 0 0 0]", tVal, true}, + {"len(s) < indexes < cap(s)", "{{slice .SICap 6 10 10}}", "[0 0 0 0]", tVal, true}, + {"indexes > cap(s)", "{{slice .SICap 10 11}}", "", tVal, false}, + {"indexes > cap(s)", "{{slice .SICap 6 10 11}}", "", tVal, false}, + {"array[:]", "{{slice .AI}}", "[3 4 5]", tVal, true}, + {"array[1:]", "{{slice .AI 1}}", "[4 5]", tVal, true}, + {"array[1:2]", "{{slice .AI 1 2}}", "[4]", tVal, true}, + {"string[:]", "{{slice .S}}", "xyz", tVal, true}, + {"string[0:1]", "{{slice .S 0 1}}", "x", tVal, true}, + {"string[1:]", "{{slice .S 1}}", "yz", tVal, true}, + {"string[1:2]", "{{slice .S 1 2}}", "y", tVal, true}, + {"out of range", "{{slice .S 1 5}}", "", tVal, false}, + {"3-index slice of string", "{{slice .S 1 2 2}}", "", tVal, false}, + {"slice of an interface field", "{{slice .Empty3 0 1}}", "[7]", tVal, true}, + + // Len. + {"slice", "{{len .SI}}", "3", tVal, true}, + {"map", "{{len .MSI }}", "3", tVal, true}, + {"len of int", "{{len 3}}", "", tVal, false}, + {"len of nothing", "{{len .Empty0}}", "", tVal, false}, + {"len of an interface field", "{{len .Empty3}}", "2", tVal, true}, + + // With. + {"with true", "{{with true}}{{.}}{{end}}", "true", tVal, true}, + {"with false", "{{with false}}{{.}}{{else}}FALSE{{end}}", "FALSE", tVal, true}, + {"with 1", "{{with 1}}{{.}}{{else}}ZERO{{end}}", "1", tVal, true}, + {"with 0", "{{with 0}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"with 1.5", "{{with 1.5}}{{.}}{{else}}ZERO{{end}}", "1.5", tVal, true}, + {"with 0.0", "{{with .FloatZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"with 1.5i", "{{with 1.5i}}{{.}}{{else}}ZERO{{end}}", "(0+1.5i)", tVal, true}, + {"with 0.0i", "{{with .ComplexZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"with emptystring", "{{with ``}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"with string", "{{with `notempty`}}{{.}}{{else}}EMPTY{{end}}", "notempty", tVal, true}, + {"with emptyslice", "{{with .SIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"with slice", "{{with .SI}}{{.}}{{else}}EMPTY{{end}}", "[3 4 5]", tVal, true}, + {"with emptymap", "{{with .MSIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"with map", "{{with .MSIone}}{{.}}{{else}}EMPTY{{end}}", "map[one:1]", tVal, true}, + {"with empty interface, struct field", "{{with .Empty4}}{{.V}}{{end}}", "UinEmpty", tVal, true}, + {"with $x int", "{{with $x := .I}}{{$x}}{{end}}", "17", tVal, true}, + {"with $x struct.U.V", "{{with $x := $}}{{$x.U.V}}{{end}}", "v", tVal, true}, + {"with variable and action", "{{with $x := $}}{{$y := $.U.V}}{{$y}}{{end}}", "v", tVal, true}, + {"with on typed nil interface value", "{{with .NonEmptyInterfaceTypedNil}}TRUE{{ end }}", "", tVal, true}, + + // Range. + {"range []int", "{{range .SI}}-{{.}}-{{end}}", "-3--4--5-", tVal, true}, + {"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true}, + {"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true}, + {"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true}, + {"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true}, + {"range map", "{{range .MSI}}-{{.}}-{{end}}", "-1--3--2-", tVal, true}, + {"range empty map no else", "{{range .MSIEmpty}}-{{.}}-{{end}}", "", tVal, true}, + {"range map else", "{{range .MSI}}-{{.}}-{{else}}EMPTY{{end}}", "-1--3--2-", tVal, true}, + {"range empty map else", "{{range .MSIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"range empty interface", "{{range .Empty3}}-{{.}}-{{else}}EMPTY{{end}}", "-7--8-", tVal, true}, + {"range empty nil", "{{range .Empty0}}-{{.}}-{{end}}", "", tVal, true}, + {"range $x SI", "{{range $x := .SI}}<{{$x}}>{{end}}", "<3><4><5>", tVal, true}, + {"range $x $y SI", "{{range $x, $y := .SI}}<{{$x}}={{$y}}>{{end}}", "<0=3><1=4><2=5>", tVal, true}, + {"range $x MSIone", "{{range $x := .MSIone}}<{{$x}}>{{end}}", "<1>", tVal, true}, + {"range $x $y MSIone", "{{range $x, $y := .MSIone}}<{{$x}}={{$y}}>{{end}}", "<one=1>", tVal, true}, + {"range $x PSI", "{{range $x := .PSI}}<{{$x}}>{{end}}", "<21><22><23>", tVal, true}, + {"declare in range", "{{range $x := .PSI}}<{{$foo:=$x}}{{$x}}>{{end}}", "<21><22><23>", tVal, true}, + {"range count", `{{range $i, $x := count 5}}[{{$i}}]{{$x}}{{end}}`, "[0]a[1]b[2]c[3]d[4]e", tVal, true}, + {"range nil count", `{{range $i, $x := count 0}}{{else}}empty{{end}}`, "empty", tVal, true}, + + // Cute examples. + {"or as if true", `{{or .SI "slice is empty"}}`, "[3 4 5]", tVal, true}, + {"or as if false", `{{or .SIEmpty "slice is empty"}}`, "slice is empty", tVal, true}, + + // Error handling. + {"error method, error", "{{.MyError true}}", "", tVal, false}, + {"error method, no error", "{{.MyError false}}", "false", tVal, true}, + + // Numbers + {"decimal", "{{print 1234}}", "1234", tVal, true}, + {"decimal _", "{{print 12_34}}", "1234", tVal, true}, + {"binary", "{{print 0b101}}", "5", tVal, true}, + {"binary _", "{{print 0b_1_0_1}}", "5", tVal, true}, + {"BINARY", "{{print 0B101}}", "5", tVal, true}, + {"octal0", "{{print 0377}}", "255", tVal, true}, + {"octal", "{{print 0o377}}", "255", tVal, true}, + {"octal _", "{{print 0o_3_7_7}}", "255", tVal, true}, + {"OCTAL", "{{print 0O377}}", "255", tVal, true}, + {"hex", "{{print 0x123}}", "291", tVal, true}, + {"hex _", "{{print 0x1_23}}", "291", tVal, true}, + {"HEX", "{{print 0X123ABC}}", "1194684", tVal, true}, + {"float", "{{print 123.4}}", "123.4", tVal, true}, + {"float _", "{{print 0_0_1_2_3.4}}", "123.4", tVal, true}, + {"hex float", "{{print +0x1.ep+2}}", "7.5", tVal, true}, + {"hex float _", "{{print +0x_1.e_0p+0_2}}", "7.5", tVal, true}, + {"HEX float", "{{print +0X1.EP+2}}", "7.5", tVal, true}, + {"print multi", "{{print 1_2_3_4 7.5_00_00_00}}", "1234 7.5", tVal, true}, + {"print multi2", "{{print 1234 0x0_1.e_0p+02}}", "1234 7.5", tVal, true}, + + // Fixed bugs. + // Must separate dot and receiver; otherwise args are evaluated with dot set to variable. + {"bug0", "{{range .MSIone}}{{if $.Method1 .}}X{{end}}{{end}}", "X", tVal, true}, + // Do not loop endlessly in indirect for non-empty interfaces. + // The bug appears with *interface only; looped forever. + {"bug1", "{{.Method0}}", "M0", &iVal, true}, + // Was taking address of interface field, so method set was empty. + {"bug2", "{{$.NonEmptyInterface.Method0}}", "M0", tVal, true}, + // Struct values were not legal in with - mere oversight. + {"bug3", "{{with $}}{{.Method0}}{{end}}", "M0", tVal, true}, + // Nil interface values in if. + {"bug4", "{{if .Empty0}}non-nil{{else}}nil{{end}}", "nil", tVal, true}, + // Stringer. + {"bug5", "{{.Str}}", "foozle", tVal, true}, + {"bug5a", "{{.Err}}", "erroozle", tVal, true}, + // Args need to be indirected and dereferenced sometimes. + {"bug6a", "{{vfunc .V0 .V1}}", "vfunc", tVal, true}, + {"bug6b", "{{vfunc .V0 .V0}}", "vfunc", tVal, true}, + {"bug6c", "{{vfunc .V1 .V0}}", "vfunc", tVal, true}, + {"bug6d", "{{vfunc .V1 .V1}}", "vfunc", tVal, true}, + // Legal parse but illegal execution: non-function should have no arguments. + {"bug7a", "{{3 2}}", "", tVal, false}, + {"bug7b", "{{$x := 1}}{{$x 2}}", "", tVal, false}, + {"bug7c", "{{$x := 1}}{{3 | $x}}", "", tVal, false}, + // Pipelined arg was not being type-checked. + {"bug8a", "{{3|oneArg}}", "", tVal, false}, + {"bug8b", "{{4|dddArg 3}}", "", tVal, false}, + // A bug was introduced that broke map lookups for lower-case names. + {"bug9", "{{.cause}}", "neglect", map[string]string{"cause": "neglect"}, true}, + // Field chain starting with function did not work. + {"bug10", "{{mapOfThree.three}}-{{(mapOfThree).three}}", "3-3", 0, true}, + // Dereferencing nil pointer while evaluating function arguments should not panic. Issue 7333. + {"bug11", "{{valueString .PS}}", "", T{}, false}, + // 0xef gave constant type float64. Issue 8622. + {"bug12xe", "{{printf `%T` 0xef}}", "int", T{}, true}, + {"bug12xE", "{{printf `%T` 0xEE}}", "int", T{}, true}, + {"bug12Xe", "{{printf `%T` 0Xef}}", "int", T{}, true}, + {"bug12XE", "{{printf `%T` 0XEE}}", "int", T{}, true}, + // Chained nodes did not work as arguments. Issue 8473. + {"bug13", "{{print (.Copy).I}}", "17", tVal, true}, + // Didn't protect against nil or literal values in field chains. + {"bug14a", "{{(nil).True}}", "", tVal, false}, + {"bug14b", "{{$x := nil}}{{$x.anything}}", "", tVal, false}, + {"bug14c", `{{$x := (1.0)}}{{$y := ("hello")}}{{$x.anything}}{{$y.true}}`, "", tVal, false}, + // Didn't call validateType on function results. Issue 10800. + {"bug15", "{{valueString returnInt}}", "", tVal, false}, + // Variadic function corner cases. Issue 10946. + {"bug16a", "{{true|printf}}", "", tVal, false}, + {"bug16b", "{{1|printf}}", "", tVal, false}, + {"bug16c", "{{1.1|printf}}", "", tVal, false}, + {"bug16d", "{{'x'|printf}}", "", tVal, false}, + {"bug16e", "{{0i|printf}}", "", tVal, false}, + {"bug16f", "{{true|twoArgs \"xxx\"}}", "", tVal, false}, + {"bug16g", "{{\"aaa\" |twoArgs \"bbb\"}}", "twoArgs=bbbaaa", tVal, true}, + {"bug16h", "{{1|oneArg}}", "", tVal, false}, + {"bug16i", "{{\"aaa\"|oneArg}}", "oneArg=aaa", tVal, true}, + {"bug16j", "{{1+2i|printf \"%v\"}}", "(1+2i)", tVal, true}, + {"bug16k", "{{\"aaa\"|printf }}", "aaa", tVal, true}, + {"bug17a", "{{.NonEmptyInterface.X}}", "x", tVal, true}, + {"bug17b", "-{{.NonEmptyInterface.Method1 1234}}-", "-1234-", tVal, true}, + {"bug17c", "{{len .NonEmptyInterfacePtS}}", "2", tVal, true}, + {"bug17d", "{{index .NonEmptyInterfacePtS 0}}", "a", tVal, true}, + {"bug17e", "{{range .NonEmptyInterfacePtS}}-{{.}}-{{end}}", "-a--b-", tVal, true}, + + // More variadic function corner cases. Some runes would get evaluated + // as constant floats instead of ints. Issue 34483. + {"bug18a", "{{eq . '.'}}", "true", '.', true}, + {"bug18b", "{{eq . 'e'}}", "true", 'e', true}, + {"bug18c", "{{eq . 'P'}}", "true", 'P', true}, +} + +func zeroArgs() string { + return "zeroArgs" +} + +func oneArg(a string) string { + return "oneArg=" + a +} + +func twoArgs(a, b string) string { + return "twoArgs=" + a + b +} + +func dddArg(a int, b ...string) string { + return fmt.Sprintln(a, b) +} + +// count returns a channel that will deliver n sequential 1-letter strings starting at "a" +func count(n int) chan string { + if n == 0 { + return nil + } + c := make(chan string) + go func() { + for i := 0; i < n; i++ { + c <- "abcdefghijklmnop"[i : i+1] + } + close(c) + }() + return c +} + +// vfunc takes a *V and a V +func vfunc(V, *V) string { + return "vfunc" +} + +// valueString takes a string, not a pointer. +func valueString(v string) string { + return "value is ignored" +} + +// returnInt returns an int +func returnInt() int { + return 7 +} + +func add(args ...int) int { + sum := 0 + for _, x := range args { + sum += x + } + return sum +} + +func echo(arg interface{}) interface{} { + return arg +} + +func makemap(arg ...string) map[string]string { + if len(arg)%2 != 0 { + panic("bad makemap") + } + m := make(map[string]string) + for i := 0; i < len(arg); i += 2 { + m[arg[i]] = arg[i+1] + } + return m +} + +func stringer(s fmt.Stringer) string { + return s.String() +} + +func mapOfThree() interface{} { + return map[string]int{"three": 3} +} + +func testExecute(execTests []execTest, template *Template, t *testing.T) { + b := new(bytes.Buffer) + funcs := FuncMap{ + "add": add, + "count": count, + "dddArg": dddArg, + "echo": echo, + "makemap": makemap, + "mapOfThree": mapOfThree, + "oneArg": oneArg, + "returnInt": returnInt, + "stringer": stringer, + "twoArgs": twoArgs, + "typeOf": typeOf, + "valueString": valueString, + "vfunc": vfunc, + "zeroArgs": zeroArgs, + } + for _, test := range execTests { + var tmpl *Template + var err error + if template == nil { + tmpl, err = New(test.name).Funcs(funcs).Parse(test.input) + } else { + tmpl, err = template.Clone() + if err != nil { + t.Errorf("%s: clone error: %s", test.name, err) + continue + } + tmpl, err = tmpl.New(test.name).Funcs(funcs).Parse(test.input) + } + if err != nil { + t.Errorf("%s: parse error: %s", test.name, err) + continue + } + b.Reset() + err = tmpl.Execute(b, test.data) + switch { + case !test.ok && err == nil: + t.Errorf("%s: expected error; got none", test.name) + continue + case test.ok && err != nil: + t.Errorf("%s: unexpected execute error: %s", test.name, err) + continue + case !test.ok && err != nil: + // expected error, got one + if *debug { + fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) + } + } + result := b.String() + if result != test.output { + t.Errorf("%s: expected\n\t%q\ngot\n\t%q", test.name, test.output, result) + } + } +} + +func TestExecute(t *testing.T) { + testExecute(execTests, nil, t) +} + +var delimPairs = []string{ + "", "", // default + "{{", "}}", // same as default + "|", "|", // same + "(日)", "(本)", // peculiar +} + +func TestDelims(t *testing.T) { + const hello = "Hello, world" + var value = struct{ Str string }{hello} + for i := 0; i < len(delimPairs); i += 2 { + text := ".Str" + left := delimPairs[i+0] + trueLeft := left + right := delimPairs[i+1] + trueRight := right + if left == "" { // default case + trueLeft = "{{" + } + if right == "" { // default case + trueRight = "}}" + } + text = trueLeft + text + trueRight + // Now add a comment + text += trueLeft + "/*comment*/" + trueRight + // Now add an action containing a string. + text += trueLeft + `"` + trueLeft + `"` + trueRight + // At this point text looks like `{{.Str}}{{/*comment*/}}{{"{{"}}`. + tmpl, err := New("delims").Delims(left, right).Parse(text) + if err != nil { + t.Fatalf("delim %q text %q parse err %s", left, text, err) + } + var b = new(bytes.Buffer) + err = tmpl.Execute(b, value) + if err != nil { + t.Fatalf("delim %q exec err %s", left, err) + } + if b.String() != hello+trueLeft { + t.Errorf("expected %q got %q", hello+trueLeft, b.String()) + } + } +} + +// Check that an error from a method flows back to the top. +func TestExecuteError(t *testing.T) { + b := new(bytes.Buffer) + tmpl := New("error") + _, err := tmpl.Parse("{{.MyError true}}") + if err != nil { + t.Fatalf("parse error: %s", err) + } + err = tmpl.Execute(b, tVal) + if err == nil { + t.Errorf("expected error; got none") + } else if !strings.Contains(err.Error(), myError.Error()) { + if *debug { + fmt.Printf("test execute error: %s\n", err) + } + t.Errorf("expected myError; got %s", err) + } +} + +const execErrorText = `line 1 +line 2 +line 3 +{{template "one" .}} +{{define "one"}}{{template "two" .}}{{end}} +{{define "two"}}{{template "three" .}}{{end}} +{{define "three"}}{{index "hi" $}}{{end}}` + +// Check that an error from a nested template contains all the relevant information. +func TestExecError(t *testing.T) { + tmpl, err := New("top").Parse(execErrorText) + if err != nil { + t.Fatal("parse error:", err) + } + var b bytes.Buffer + err = tmpl.Execute(&b, 5) // 5 is out of range indexing "hi" + if err == nil { + t.Fatal("expected error") + } + const want = `template: top:7:20: executing "three" at <index "hi" $>: error calling index: index out of range: 5` + got := err.Error() + if got != want { + t.Errorf("expected\n%q\ngot\n%q", want, got) + } +} + +func TestJSEscaping(t *testing.T) { + testCases := []struct { + in, exp string + }{ + {`a`, `a`}, + {`'foo`, `\'foo`}, + {`Go "jump" \`, `Go \"jump\" \\`}, + {`Yukihiro says "今日は世界"`, `Yukihiro says \"今日は世界\"`}, + {"unprintable \uFDFF", `unprintable \uFDFF`}, + {`<html>`, `\u003Chtml\u003E`}, + {`no = in attributes`, `no \u003D in attributes`}, + {`' does not become HTML entity`, `\u0026#x27; does not become HTML entity`}, + } + for _, tc := range testCases { + s := JSEscapeString(tc.in) + if s != tc.exp { + t.Errorf("JS escaping [%s] got [%s] want [%s]", tc.in, s, tc.exp) + } + } +} + +// A nice example: walk a binary tree. + +type Tree struct { + Val int + Left, Right *Tree +} + +// Use different delimiters to test Set.Delims. +// Also test the trimming of leading and trailing spaces. +const treeTemplate = ` + (- define "tree" -) + [ + (- .Val -) + (- with .Left -) + (template "tree" . -) + (- end -) + (- with .Right -) + (- template "tree" . -) + (- end -) + ] + (- end -) +` + +func TestTree(t *testing.T) { + var tree = &Tree{ + 1, + &Tree{ + 2, &Tree{ + 3, + &Tree{ + 4, nil, nil, + }, + nil, + }, + &Tree{ + 5, + &Tree{ + 6, nil, nil, + }, + nil, + }, + }, + &Tree{ + 7, + &Tree{ + 8, + &Tree{ + 9, nil, nil, + }, + nil, + }, + &Tree{ + 10, + &Tree{ + 11, nil, nil, + }, + nil, + }, + }, + } + tmpl, err := New("root").Delims("(", ")").Parse(treeTemplate) + if err != nil { + t.Fatal("parse error:", err) + } + var b bytes.Buffer + const expect = "[1[2[3[4]][5[6]]][7[8[9]][10[11]]]]" + // First by looking up the template. + err = tmpl.Lookup("tree").Execute(&b, tree) + if err != nil { + t.Fatal("exec error:", err) + } + result := b.String() + if result != expect { + t.Errorf("expected %q got %q", expect, result) + } + // Then direct to execution. + b.Reset() + err = tmpl.ExecuteTemplate(&b, "tree", tree) + if err != nil { + t.Fatal("exec error:", err) + } + result = b.String() + if result != expect { + t.Errorf("expected %q got %q", expect, result) + } +} + +func TestExecuteOnNewTemplate(t *testing.T) { + // This is issue 3872. + New("Name").Templates() + // This is issue 11379. + // new(Template).Templates() // TODO: crashes + // new(Template).Parse("") // TODO: crashes + // new(Template).New("abc").Parse("") // TODO: crashes + // new(Template).Execute(nil, nil) // TODO: crashes; returns an error (but does not crash) + // new(Template).ExecuteTemplate(nil, "XXX", nil) // TODO: crashes; returns an error (but does not crash) +} + +const testTemplates = `{{define "one"}}one{{end}}{{define "two"}}two{{end}}` + +func TestMessageForExecuteEmpty(t *testing.T) { + // Test a truly empty template. + tmpl := New("empty") + var b bytes.Buffer + err := tmpl.Execute(&b, 0) + if err == nil { + t.Fatal("expected initial error") + } + got := err.Error() + want := `template: "empty" is an incomplete or empty template` // NOTE: text/template has extra "empty: " in message + if got != want { + t.Errorf("expected error %s got %s", want, got) + } + + // Add a non-empty template to check that the error is helpful. + tmpl = New("empty") + tests, err := New("").Parse(testTemplates) + if err != nil { + t.Fatal(err) + } + tmpl.AddParseTree("secondary", tests.Tree) + err = tmpl.Execute(&b, 0) + if err == nil { + t.Fatal("expected second error") + } + got = err.Error() + if got != want { + t.Errorf("expected error %s got %s", want, got) + } + // Make sure we can execute the secondary. + err = tmpl.ExecuteTemplate(&b, "secondary", 0) + if err != nil { + t.Fatal(err) + } +} + +func TestFinalForPrintf(t *testing.T) { + tmpl, err := New("").Parse(`{{"x" | printf}}`) + if err != nil { + t.Fatal(err) + } + var b bytes.Buffer + err = tmpl.Execute(&b, 0) + if err != nil { + t.Fatal(err) + } +} + +type cmpTest struct { + expr string + truth string + ok bool +} + +var cmpTests = []cmpTest{ + {"eq true true", "true", true}, + {"eq true false", "false", true}, + {"eq 1+2i 1+2i", "true", true}, + {"eq 1+2i 1+3i", "false", true}, + {"eq 1.5 1.5", "true", true}, + {"eq 1.5 2.5", "false", true}, + {"eq 1 1", "true", true}, + {"eq 1 2", "false", true}, + {"eq `xy` `xy`", "true", true}, + {"eq `xy` `xyz`", "false", true}, + {"eq .Uthree .Uthree", "true", true}, + {"eq .Uthree .Ufour", "false", true}, + {"eq 3 4 5 6 3", "true", true}, + {"eq 3 4 5 6 7", "false", true}, + {"ne true true", "false", true}, + {"ne true false", "true", true}, + {"ne 1+2i 1+2i", "false", true}, + {"ne 1+2i 1+3i", "true", true}, + {"ne 1.5 1.5", "false", true}, + {"ne 1.5 2.5", "true", true}, + {"ne 1 1", "false", true}, + {"ne 1 2", "true", true}, + {"ne `xy` `xy`", "false", true}, + {"ne `xy` `xyz`", "true", true}, + {"ne .Uthree .Uthree", "false", true}, + {"ne .Uthree .Ufour", "true", true}, + {"lt 1.5 1.5", "false", true}, + {"lt 1.5 2.5", "true", true}, + {"lt 1 1", "false", true}, + {"lt 1 2", "true", true}, + {"lt `xy` `xy`", "false", true}, + {"lt `xy` `xyz`", "true", true}, + {"lt .Uthree .Uthree", "false", true}, + {"lt .Uthree .Ufour", "true", true}, + {"le 1.5 1.5", "true", true}, + {"le 1.5 2.5", "true", true}, + {"le 2.5 1.5", "false", true}, + {"le 1 1", "true", true}, + {"le 1 2", "true", true}, + {"le 2 1", "false", true}, + {"le `xy` `xy`", "true", true}, + {"le `xy` `xyz`", "true", true}, + {"le `xyz` `xy`", "false", true}, + {"le .Uthree .Uthree", "true", true}, + {"le .Uthree .Ufour", "true", true}, + {"le .Ufour .Uthree", "false", true}, + {"gt 1.5 1.5", "false", true}, + {"gt 1.5 2.5", "false", true}, + {"gt 1 1", "false", true}, + {"gt 2 1", "true", true}, + {"gt 1 2", "false", true}, + {"gt `xy` `xy`", "false", true}, + {"gt `xy` `xyz`", "false", true}, + {"gt .Uthree .Uthree", "false", true}, + {"gt .Uthree .Ufour", "false", true}, + {"gt .Ufour .Uthree", "true", true}, + {"ge 1.5 1.5", "true", true}, + {"ge 1.5 2.5", "false", true}, + {"ge 2.5 1.5", "true", true}, + {"ge 1 1", "true", true}, + {"ge 1 2", "false", true}, + {"ge 2 1", "true", true}, + {"ge `xy` `xy`", "true", true}, + {"ge `xy` `xyz`", "false", true}, + {"ge `xyz` `xy`", "true", true}, + {"ge .Uthree .Uthree", "true", true}, + {"ge .Uthree .Ufour", "false", true}, + {"ge .Ufour .Uthree", "true", true}, + // Mixing signed and unsigned integers. + {"eq .Uthree .Three", "true", true}, + {"eq .Three .Uthree", "true", true}, + {"le .Uthree .Three", "true", true}, + {"le .Three .Uthree", "true", true}, + {"ge .Uthree .Three", "true", true}, + {"ge .Three .Uthree", "true", true}, + {"lt .Uthree .Three", "false", true}, + {"lt .Three .Uthree", "false", true}, + {"gt .Uthree .Three", "false", true}, + {"gt .Three .Uthree", "false", true}, + {"eq .Ufour .Three", "false", true}, + {"lt .Ufour .Three", "false", true}, + {"gt .Ufour .Three", "true", true}, + {"eq .NegOne .Uthree", "false", true}, + {"eq .Uthree .NegOne", "false", true}, + {"ne .NegOne .Uthree", "true", true}, + {"ne .Uthree .NegOne", "true", true}, + {"lt .NegOne .Uthree", "true", true}, + {"lt .Uthree .NegOne", "false", true}, + {"le .NegOne .Uthree", "true", true}, + {"le .Uthree .NegOne", "false", true}, + {"gt .NegOne .Uthree", "false", true}, + {"gt .Uthree .NegOne", "true", true}, + {"ge .NegOne .Uthree", "false", true}, + {"ge .Uthree .NegOne", "true", true}, + {"eq (index `x` 0) 'x'", "true", true}, // The example that triggered this rule. + {"eq (index `x` 0) 'y'", "false", true}, + {"eq .V1 .V2", "true", true}, + {"eq .Ptr .Ptr", "true", true}, + {"eq .Ptr .NilPtr", "false", true}, + {"eq .NilPtr .NilPtr", "true", true}, + {"eq .Iface1 .Iface1", "true", true}, + {"eq .Iface1 .Iface2", "false", true}, + {"eq .Iface2 .Iface2", "true", true}, + // Errors + {"eq `xy` 1", "", false}, // Different types. + {"eq 2 2.0", "", false}, // Different types. + {"lt true true", "", false}, // Unordered types. + {"lt 1+0i 1+0i", "", false}, // Unordered types. + {"eq .Ptr 1", "", false}, // Incompatible types. + {"eq .Ptr .NegOne", "", false}, // Incompatible types. + {"eq .Map .Map", "", false}, // Uncomparable types. + {"eq .Map .V1", "", false}, // Uncomparable types. +} + +func TestComparison(t *testing.T) { + b := new(bytes.Buffer) + var cmpStruct = struct { + Uthree, Ufour uint + NegOne, Three int + Ptr, NilPtr *int + Map map[int]int + V1, V2 V + Iface1, Iface2 fmt.Stringer + }{ + Uthree: 3, + Ufour: 4, + NegOne: -1, + Three: 3, + Ptr: new(int), + Iface1: b, + } + for _, test := range cmpTests { + text := fmt.Sprintf("{{if %s}}true{{else}}false{{end}}", test.expr) + tmpl, err := New("empty").Parse(text) + if err != nil { + t.Fatalf("%q: %s", test.expr, err) + } + b.Reset() + err = tmpl.Execute(b, &cmpStruct) + if test.ok && err != nil { + t.Errorf("%s errored incorrectly: %s", test.expr, err) + continue + } + if !test.ok && err == nil { + t.Errorf("%s did not error", test.expr) + continue + } + if b.String() != test.truth { + t.Errorf("%s: want %s; got %s", test.expr, test.truth, b.String()) + } + } +} + +func TestMissingMapKey(t *testing.T) { + data := map[string]int{ + "x": 99, + } + tmpl, err := New("t1").Parse("{{.x}} {{.y}}") + if err != nil { + t.Fatal(err) + } + var b bytes.Buffer + // By default, just get "<no value>" // NOTE: not in html/template, get empty string + err = tmpl.Execute(&b, data) + if err != nil { + t.Fatal(err) + } + want := "99 " + got := b.String() + if got != want { + t.Errorf("got %q; expected %q", got, want) + } + // Same if we set the option explicitly to the default. + tmpl.Option("missingkey=default") + b.Reset() + err = tmpl.Execute(&b, data) + if err != nil { + t.Fatal("default:", err) + } + got = b.String() + if got != want { + t.Errorf("got %q; expected %q", got, want) + } + // Next we ask for a zero value + tmpl.Option("missingkey=zero") + b.Reset() + err = tmpl.Execute(&b, data) + if err != nil { + t.Fatal("zero:", err) + } + want = "99 0" + got = b.String() + if got != want { + t.Errorf("got %q; expected %q", got, want) + } + // Now we ask for an error. + tmpl.Option("missingkey=error") + err = tmpl.Execute(&b, data) + if err == nil { + t.Errorf("expected error; got none") + } + // same Option, but now a nil interface: ask for an error + err = tmpl.Execute(&b, nil) + t.Log(err) + if err == nil { + t.Errorf("expected error for nil-interface; got none") + } +} + +// Test that the error message for multiline unterminated string +// refers to the line number of the opening quote. +func TestUnterminatedStringError(t *testing.T) { + _, err := New("X").Parse("hello\n\n{{`unterminated\n\n\n\n}}\n some more\n\n") + if err == nil { + t.Fatal("expected error") + } + str := err.Error() + if !strings.Contains(str, "X:3: unexpected unterminated raw quoted string") { + t.Fatalf("unexpected error: %s", str) + } +} + +const alwaysErrorText = "always be failing" + +var alwaysError = errors.New(alwaysErrorText) + +type ErrorWriter int + +func (e ErrorWriter) Write(p []byte) (int, error) { + return 0, alwaysError +} + +func TestExecuteGivesExecError(t *testing.T) { + // First, a non-execution error shouldn't be an ExecError. + tmpl, err := New("X").Parse("hello") + if err != nil { + t.Fatal(err) + } + err = tmpl.Execute(ErrorWriter(0), 0) + if err == nil { + t.Fatal("expected error; got none") + } + if err.Error() != alwaysErrorText { + t.Errorf("expected %q error; got %q", alwaysErrorText, err) + } + // This one should be an ExecError. + tmpl, err = New("X").Parse("hello, {{.X.Y}}") + if err != nil { + t.Fatal(err) + } + err = tmpl.Execute(ioutil.Discard, 0) + if err == nil { + t.Fatal("expected error; got none") + } + eerr, ok := err.(template.ExecError) + if !ok { + t.Fatalf("did not expect ExecError %s", eerr) + } + expect := "field X in type int" + if !strings.Contains(err.Error(), expect) { + t.Errorf("expected %q; got %q", expect, err) + } +} + +func funcNameTestFunc() int { + return 0 +} + +func TestGoodFuncNames(t *testing.T) { + names := []string{ + "_", + "a", + "a1", + "a1", + "Ӵ", + } + for _, name := range names { + tmpl := New("X").Funcs( + FuncMap{ + name: funcNameTestFunc, + }, + ) + if tmpl == nil { + t.Fatalf("nil result for %q", name) + } + } +} + +func TestBadFuncNames(t *testing.T) { + names := []string{ + "", + "2", + "a-b", + } + for _, name := range names { + testBadFuncName(name, t) + } +} + +func testBadFuncName(name string, t *testing.T) { + t.Helper() + defer func() { + recover() + }() + New("X").Funcs( + FuncMap{ + name: funcNameTestFunc, + }, + ) + // If we get here, the name did not cause a panic, which is how Funcs + // reports an error. + t.Errorf("%q succeeded incorrectly as function name", name) +} + +func TestBlock(t *testing.T) { + const ( + input = `a({{block "inner" .}}bar({{.}})baz{{end}})b` + want = `a(bar(hello)baz)b` + overlay = `{{define "inner"}}foo({{.}})bar{{end}}` + want2 = `a(foo(goodbye)bar)b` + ) + tmpl, err := New("outer").Parse(input) + if err != nil { + t.Fatal(err) + } + tmpl2, err := Must(tmpl.Clone()).Parse(overlay) + if err != nil { + t.Fatal(err) + } + + var buf bytes.Buffer + if err := tmpl.Execute(&buf, "hello"); err != nil { + t.Fatal(err) + } + if got := buf.String(); got != want { + t.Errorf("got %q, want %q", got, want) + } + + buf.Reset() + if err := tmpl2.Execute(&buf, "goodbye"); err != nil { + t.Fatal(err) + } + if got := buf.String(); got != want2 { + t.Errorf("got %q, want %q", got, want2) + } +} + +func TestEvalFieldErrors(t *testing.T) { + tests := []struct { + name, src string + value interface{} + want string + }{ + { + // Check that calling an invalid field on nil pointer + // prints a field error instead of a distracting nil + // pointer error. https://golang.org/issue/15125 + "MissingFieldOnNil", + "{{.MissingField}}", + (*T)(nil), + "can't evaluate field MissingField in type *template.T", + }, + { + "MissingFieldOnNonNil", + "{{.MissingField}}", + &T{}, + "can't evaluate field MissingField in type *template.T", + }, + { + "ExistingFieldOnNil", + "{{.X}}", + (*T)(nil), + "nil pointer evaluating *template.T.X", + }, + { + "MissingKeyOnNilMap", + "{{.MissingKey}}", + (*map[string]string)(nil), + "nil pointer evaluating *map[string]string.MissingKey", + }, + { + "MissingKeyOnNilMapPtr", + "{{.MissingKey}}", + (*map[string]string)(nil), + "nil pointer evaluating *map[string]string.MissingKey", + }, + { + "MissingKeyOnMapPtrToNil", + "{{.MissingKey}}", + &map[string]string{}, + "<nil>", + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + tmpl := Must(New("tmpl").Parse(tc.src)) + err := tmpl.Execute(ioutil.Discard, tc.value) + got := "<nil>" + if err != nil { + got = err.Error() + } + if !strings.HasSuffix(got, tc.want) { + t.Fatalf("got error %q, want %q", got, tc.want) + } + }) + } +} + +func TestMaxExecDepth(t *testing.T) { + if testing.Short() { + t.Skip("skipping in -short mode") + } + tmpl := Must(New("tmpl").Parse(`{{template "tmpl" .}}`)) + err := tmpl.Execute(ioutil.Discard, nil) + got := "<nil>" + if err != nil { + got = err.Error() + } + const want = "exceeded maximum template depth" + if !strings.Contains(got, want) { + t.Errorf("got error %q; want %q", got, want) + } +} + +func TestAddrOfIndex(t *testing.T) { + // golang.org/issue/14916. + // Before index worked on reflect.Values, the .String could not be + // found on the (incorrectly unaddressable) V value, + // in contrast to range, which worked fine. + // Also testing that passing a reflect.Value to tmpl.Execute works. + texts := []string{ + `{{range .}}{{.String}}{{end}}`, + `{{with index . 0}}{{.String}}{{end}}`, + } + for _, text := range texts { + tmpl := Must(New("tmpl").Parse(text)) + var buf bytes.Buffer + err := tmpl.Execute(&buf, reflect.ValueOf([]V{{1}})) + if err != nil { + t.Fatalf("%s: Execute: %v", text, err) + } + if buf.String() != "<1>" { + t.Fatalf("%s: template output = %q, want %q", text, &buf, "<1>") + } + } +} + +func TestInterfaceValues(t *testing.T) { + // golang.org/issue/17714. + // Before index worked on reflect.Values, interface values + // were always implicitly promoted to the underlying value, + // except that nil interfaces were promoted to the zero reflect.Value. + // Eliminating a round trip to interface{} and back to reflect.Value + // eliminated this promotion, breaking these cases. + tests := []struct { + text string + out string + }{ + {`{{index .Nil 1}}`, "ERROR: index of untyped nil"}, + {`{{index .Slice 2}}`, "2"}, + {`{{index .Slice .Two}}`, "2"}, + {`{{call .Nil 1}}`, "ERROR: call of nil"}, + {`{{call .PlusOne 1}}`, "2"}, + {`{{call .PlusOne .One}}`, "2"}, + {`{{and (index .Slice 0) true}}`, "0"}, + {`{{and .Zero true}}`, "0"}, + {`{{and (index .Slice 1) false}}`, "false"}, + {`{{and .One false}}`, "false"}, + {`{{or (index .Slice 0) false}}`, "false"}, + {`{{or .Zero false}}`, "false"}, + {`{{or (index .Slice 1) true}}`, "1"}, + {`{{or .One true}}`, "1"}, + {`{{not (index .Slice 0)}}`, "true"}, + {`{{not .Zero}}`, "true"}, + {`{{not (index .Slice 1)}}`, "false"}, + {`{{not .One}}`, "false"}, + {`{{eq (index .Slice 0) .Zero}}`, "true"}, + {`{{eq (index .Slice 1) .One}}`, "true"}, + {`{{ne (index .Slice 0) .Zero}}`, "false"}, + {`{{ne (index .Slice 1) .One}}`, "false"}, + {`{{ge (index .Slice 0) .One}}`, "false"}, + {`{{ge (index .Slice 1) .Zero}}`, "true"}, + {`{{gt (index .Slice 0) .One}}`, "false"}, + {`{{gt (index .Slice 1) .Zero}}`, "true"}, + {`{{le (index .Slice 0) .One}}`, "true"}, + {`{{le (index .Slice 1) .Zero}}`, "false"}, + {`{{lt (index .Slice 0) .One}}`, "true"}, + {`{{lt (index .Slice 1) .Zero}}`, "false"}, + } + + for _, tt := range tests { + tmpl := Must(New("tmpl").Parse(tt.text)) + var buf bytes.Buffer + err := tmpl.Execute(&buf, map[string]interface{}{ + "PlusOne": func(n int) int { + return n + 1 + }, + "Slice": []int{0, 1, 2, 3}, + "One": 1, + "Two": 2, + "Nil": nil, + "Zero": 0, + }) + if strings.HasPrefix(tt.out, "ERROR:") { + e := strings.TrimSpace(strings.TrimPrefix(tt.out, "ERROR:")) + if err == nil || !strings.Contains(err.Error(), e) { + t.Errorf("%s: Execute: %v, want error %q", tt.text, err, e) + } + continue + } + if err != nil { + t.Errorf("%s: Execute: %v", tt.text, err) + continue + } + if buf.String() != tt.out { + t.Errorf("%s: template output = %q, want %q", tt.text, &buf, tt.out) + } + } +} + +// Check that panics during calls are recovered and returned as errors. +func TestExecutePanicDuringCall(t *testing.T) { + funcs := map[string]interface{}{ + "doPanic": func() string { + panic("custom panic string") + }, + } + tests := []struct { + name string + input string + data interface{} + wantErr string + }{ + { + "direct func call panics", + "{{doPanic}}", (*T)(nil), + `template: t:1:2: executing "t" at <doPanic>: error calling doPanic: custom panic string`, + }, + { + "indirect func call panics", + "{{call doPanic}}", (*T)(nil), + `template: t:1:7: executing "t" at <doPanic>: error calling doPanic: custom panic string`, + }, + { + "direct method call panics", + "{{.GetU}}", (*T)(nil), + `template: t:1:2: executing "t" at <.GetU>: error calling GetU: runtime error: invalid memory address or nil pointer dereference`, + }, + { + "indirect method call panics", + "{{call .GetU}}", (*T)(nil), + `template: t:1:7: executing "t" at <.GetU>: error calling GetU: runtime error: invalid memory address or nil pointer dereference`, + }, + { + "func field call panics", + "{{call .PanicFunc}}", tVal, + `template: t:1:2: executing "t" at <call .PanicFunc>: error calling call: test panic`, + }, + { + "method call on nil interface", + "{{.NonEmptyInterfaceNil.Method0}}", tVal, + `template: t:1:23: executing "t" at <.NonEmptyInterfaceNil.Method0>: nil pointer evaluating template.I.Method0`, + }, + } + for _, tc := range tests { + b := new(bytes.Buffer) + tmpl, err := New("t").Funcs(funcs).Parse(tc.input) + if err != nil { + t.Fatalf("parse error: %s", err) + } + err = tmpl.Execute(b, tc.data) + if err == nil { + t.Errorf("%s: expected error; got none", tc.name) + } else if !strings.Contains(err.Error(), tc.wantErr) { + if *debug { + fmt.Printf("%s: test execute error: %s\n", tc.name, err) + } + t.Errorf("%s: expected error:\n%s\ngot:\n%s", tc.name, tc.wantErr, err) + } + } +} + +// Issue 31810. Check that a parenthesized first argument behaves properly. +func TestIssue31810(t *testing.T) { + t.Skip("broken in html/template") + + // A simple value with no arguments is fine. + var b bytes.Buffer + const text = "{{ (.) }}" + tmpl, err := New("").Parse(text) + if err != nil { + t.Error(err) + } + err = tmpl.Execute(&b, "result") + if err != nil { + t.Error(err) + } + if b.String() != "result" { + t.Errorf("%s got %q, expected %q", text, b.String(), "result") + } + + // Even a plain function fails - need to use call. + f := func() string { return "result" } + b.Reset() + err = tmpl.Execute(&b, f) + if err == nil { + t.Error("expected error with no call, got none") + } + + // Works if the function is explicitly called. + const textCall = "{{ (call .) }}" + tmpl, err = New("").Parse(textCall) + b.Reset() + err = tmpl.Execute(&b, f) + if err != nil { + t.Error(err) + } + if b.String() != "result" { + t.Errorf("%s got %q, expected %q", textCall, b.String(), "result") + } +} diff --git a/src/html/template/multi_test.go b/src/html/template/multi_test.go new file mode 100644 index 0000000000..50526c5b65 --- /dev/null +++ b/src/html/template/multi_test.go @@ -0,0 +1,246 @@ +// Copyright 2011 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. + +// Tests for multiple-template execution, copied from text/template. + +package template + +import ( + "bytes" + "testing" + "text/template/parse" +) + +var multiExecTests = []execTest{ + {"empty", "", "", nil, true}, + {"text", "some text", "some text", nil, true}, + {"invoke x", `{{template "x" .SI}}`, "TEXT", tVal, true}, + {"invoke x no args", `{{template "x"}}`, "TEXT", tVal, true}, + {"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true}, + {"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true}, + {"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true}, + {"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true}, + {"variable declared by template", `{{template "nested" $x:=.SI}},{{index $x 1}}`, "[3 4 5],4", tVal, true}, + + // User-defined function: test argument evaluator. + {"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true}, + {"testFunc .", `{{oneArg .}}`, "oneArg=joe", "joe", true}, +} + +// These strings are also in testdata/*. +const multiText1 = ` + {{define "x"}}TEXT{{end}} + {{define "dotV"}}{{.V}}{{end}} +` + +const multiText2 = ` + {{define "dot"}}{{.}}{{end}} + {{define "nested"}}{{template "dot" .}}{{end}} +` + +func TestMultiExecute(t *testing.T) { + // Declare a couple of templates first. + template, err := New("root").Parse(multiText1) + if err != nil { + t.Fatalf("parse error for 1: %s", err) + } + _, err = template.Parse(multiText2) + if err != nil { + t.Fatalf("parse error for 2: %s", err) + } + testExecute(multiExecTests, template, t) +} + +func TestParseFiles(t *testing.T) { + _, err := ParseFiles("DOES NOT EXIST") + if err == nil { + t.Error("expected error for non-existent file; got none") + } + template := New("root") + _, err = template.ParseFiles("testdata/file1.tmpl", "testdata/file2.tmpl") + if err != nil { + t.Fatalf("error parsing files: %v", err) + } + testExecute(multiExecTests, template, t) +} + +func TestParseGlob(t *testing.T) { + _, err := ParseGlob("DOES NOT EXIST") + if err == nil { + t.Error("expected error for non-existent file; got none") + } + _, err = New("error").ParseGlob("[x") + if err == nil { + t.Error("expected error for bad pattern; got none") + } + template := New("root") + _, err = template.ParseGlob("testdata/file*.tmpl") + if err != nil { + t.Fatalf("error parsing files: %v", err) + } + testExecute(multiExecTests, template, t) +} + +// In these tests, actual content (not just template definitions) comes from the parsed files. + +var templateFileExecTests = []execTest{ + {"test", `{{template "tmpl1.tmpl"}}{{template "tmpl2.tmpl"}}`, "template1\n\ny\ntemplate2\n\nx\n", 0, true}, +} + +func TestParseFilesWithData(t *testing.T) { + template, err := New("root").ParseFiles("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl") + if err != nil { + t.Fatalf("error parsing files: %v", err) + } + testExecute(templateFileExecTests, template, t) +} + +func TestParseGlobWithData(t *testing.T) { + template, err := New("root").ParseGlob("testdata/tmpl*.tmpl") + if err != nil { + t.Fatalf("error parsing files: %v", err) + } + testExecute(templateFileExecTests, template, t) +} + +const ( + cloneText1 = `{{define "a"}}{{template "b"}}{{template "c"}}{{end}}` + cloneText2 = `{{define "b"}}b{{end}}` + cloneText3 = `{{define "c"}}root{{end}}` + cloneText4 = `{{define "c"}}clone{{end}}` +) + +// Issue 7032 +func TestAddParseTreeToUnparsedTemplate(t *testing.T) { + master := "{{define \"master\"}}{{end}}" + tmpl := New("master") + tree, err := parse.Parse("master", master, "", "", nil) + if err != nil { + t.Fatalf("unexpected parse err: %v", err) + } + masterTree := tree["master"] + tmpl.AddParseTree("master", masterTree) // used to panic +} + +func TestRedefinition(t *testing.T) { + var tmpl *Template + var err error + if tmpl, err = New("tmpl1").Parse(`{{define "test"}}foo{{end}}`); err != nil { + t.Fatalf("parse 1: %v", err) + } + if _, err = tmpl.Parse(`{{define "test"}}bar{{end}}`); err != nil { + t.Fatalf("got error %v, expected nil", err) + } + if _, err = tmpl.New("tmpl2").Parse(`{{define "test"}}bar{{end}}`); err != nil { + t.Fatalf("got error %v, expected nil", err) + } +} + +// Issue 10879 +func TestEmptyTemplateCloneCrash(t *testing.T) { + t1 := New("base") + t1.Clone() // used to panic +} + +// Issue 10910, 10926 +func TestTemplateLookUp(t *testing.T) { + t.Skip("broken on html/template") // TODO + t1 := New("foo") + if t1.Lookup("foo") != nil { + t.Error("Lookup returned non-nil value for undefined template foo") + } + t1.New("bar") + if t1.Lookup("bar") != nil { + t.Error("Lookup returned non-nil value for undefined template bar") + } + t1.Parse(`{{define "foo"}}test{{end}}`) + if t1.Lookup("foo") == nil { + t.Error("Lookup returned nil value for defined template") + } +} + +func TestParse(t *testing.T) { + // In multiple calls to Parse with the same receiver template, only one call + // can contain text other than space, comments, and template definitions + t1 := New("test") + if _, err := t1.Parse(`{{define "test"}}{{end}}`); err != nil { + t.Fatalf("parsing test: %s", err) + } + if _, err := t1.Parse(`{{define "test"}}{{/* this is a comment */}}{{end}}`); err != nil { + t.Fatalf("parsing test: %s", err) + } + if _, err := t1.Parse(`{{define "test"}}foo{{end}}`); err != nil { + t.Fatalf("parsing test: %s", err) + } +} + +func TestEmptyTemplate(t *testing.T) { + cases := []struct { + defn []string + in string + want string + }{ + {[]string{"x", "y"}, "", "y"}, + {[]string{""}, "once", ""}, + {[]string{"", ""}, "twice", ""}, + {[]string{"{{.}}", "{{.}}"}, "twice", "twice"}, + {[]string{"{{/* a comment */}}", "{{/* a comment */}}"}, "comment", ""}, + {[]string{"{{.}}", ""}, "twice", "twice"}, // TODO: should want "" not "twice" + } + + for i, c := range cases { + root := New("root") + + var ( + m *Template + err error + ) + for _, d := range c.defn { + m, err = root.New(c.in).Parse(d) + if err != nil { + t.Fatal(err) + } + } + buf := &bytes.Buffer{} + if err := m.Execute(buf, c.in); err != nil { + t.Error(i, err) + continue + } + if buf.String() != c.want { + t.Errorf("expected string %q: got %q", c.want, buf.String()) + } + } +} + +// Issue 19249 was a regression in 1.8 caused by the handling of empty +// templates added in that release, which got different answers depending +// on the order templates appeared in the internal map. +func TestIssue19294(t *testing.T) { + // The empty block in "xhtml" should be replaced during execution + // by the contents of "stylesheet", but if the internal map associating + // names with templates is built in the wrong order, the empty block + // looks non-empty and this doesn't happen. + var inlined = map[string]string{ + "stylesheet": `{{define "stylesheet"}}stylesheet{{end}}`, + "xhtml": `{{block "stylesheet" .}}{{end}}`, + } + all := []string{"stylesheet", "xhtml"} + for i := 0; i < 100; i++ { + res, err := New("title.xhtml").Parse(`{{template "xhtml" .}}`) + if err != nil { + t.Fatal(err) + } + for _, name := range all { + _, err := res.New(name).Parse(inlined[name]) + if err != nil { + t.Fatal(err) + } + } + var buf bytes.Buffer + res.Execute(&buf, 0) + if buf.String() != "stylesheet" { + t.Fatalf("iteration %d: got %q; expected %q", i, buf.String(), "stylesheet") + } + } +} diff --git a/src/html/template/testdata/file1.tmpl b/src/html/template/testdata/file1.tmpl new file mode 100644 index 0000000000..febf9d9f89 --- /dev/null +++ b/src/html/template/testdata/file1.tmpl @@ -0,0 +1,2 @@ +{{define "x"}}TEXT{{end}} +{{define "dotV"}}{{.V}}{{end}} diff --git a/src/html/template/testdata/file2.tmpl b/src/html/template/testdata/file2.tmpl new file mode 100644 index 0000000000..39bf6fb9ee --- /dev/null +++ b/src/html/template/testdata/file2.tmpl @@ -0,0 +1,2 @@ +{{define "dot"}}{{.}}{{end}} +{{define "nested"}}{{template "dot" .}}{{end}} diff --git a/src/html/template/testdata/tmpl1.tmpl b/src/html/template/testdata/tmpl1.tmpl new file mode 100644 index 0000000000..b72b3a340c --- /dev/null +++ b/src/html/template/testdata/tmpl1.tmpl @@ -0,0 +1,3 @@ +template1 +{{define "x"}}x{{end}} +{{template "y"}} diff --git a/src/html/template/testdata/tmpl2.tmpl b/src/html/template/testdata/tmpl2.tmpl new file mode 100644 index 0000000000..16beba6e7d --- /dev/null +++ b/src/html/template/testdata/tmpl2.tmpl @@ -0,0 +1,3 @@ +template2 +{{define "y"}}y{{end}} +{{template "x"}} diff --git a/src/net/http/server.go b/src/net/http/server.go index a995a50658..d41b5f6f48 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -425,6 +425,16 @@ type response struct { wants10KeepAlive bool // HTTP/1.0 w/ Connection "keep-alive" wantsClose bool // HTTP request has Connection "close" + // canWriteContinue is a boolean value accessed as an atomic int32 + // that says whether or not a 100 Continue header can be written + // to the connection. + // writeContinueMu must be held while writing the header. + // These two fields together synchronize the body reader + // (the expectContinueReader, which wants to write 100 Continue) + // against the main writer. + canWriteContinue atomicBool + writeContinueMu sync.Mutex + w *bufio.Writer // buffers output in chunks to chunkWriter cw chunkWriter @@ -515,6 +525,7 @@ type atomicBool int32 func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 } func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) } +func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) } // declareTrailer is called for each Trailer header when the // response header is written. It notes that a header will need to be @@ -878,21 +889,27 @@ type expectContinueReader struct { resp *response readCloser io.ReadCloser closed bool - sawEOF bool + sawEOF atomicBool } func (ecr *expectContinueReader) Read(p []byte) (n int, err error) { if ecr.closed { return 0, ErrBodyReadAfterClose } - if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked() { - ecr.resp.wroteContinue = true - ecr.resp.conn.bufw.WriteString("HTTP/1.1 100 Continue\r\n\r\n") - ecr.resp.conn.bufw.Flush() + w := ecr.resp + if !w.wroteContinue && w.canWriteContinue.isSet() && !w.conn.hijacked() { + w.wroteContinue = true + w.writeContinueMu.Lock() + if w.canWriteContinue.isSet() { + w.conn.bufw.WriteString("HTTP/1.1 100 Continue\r\n\r\n") + w.conn.bufw.Flush() + w.canWriteContinue.setFalse() + } + w.writeContinueMu.Unlock() } n, err = ecr.readCloser.Read(p) if err == io.EOF { - ecr.sawEOF = true + ecr.sawEOF.setTrue() } return } @@ -1311,7 +1328,7 @@ func (cw *chunkWriter) writeHeader(p []byte) { // because we don't know if the next bytes on the wire will be // the body-following-the-timer or the subsequent request. // See Issue 11549. - if ecr, ok := w.req.Body.(*expectContinueReader); ok && !ecr.sawEOF { + if ecr, ok := w.req.Body.(*expectContinueReader); ok && !ecr.sawEOF.isSet() { w.closeAfterReply = true } @@ -1561,6 +1578,17 @@ func (w *response) write(lenData int, dataB []byte, dataS string) (n int, err er } return 0, ErrHijacked } + + if w.canWriteContinue.isSet() { + // Body reader wants to write 100 Continue but hasn't yet. + // Tell it not to. The store must be done while holding the lock + // because the lock makes sure that there is not an active write + // this very moment. + w.writeContinueMu.Lock() + w.canWriteContinue.setFalse() + w.writeContinueMu.Unlock() + } + if !w.wroteHeader { w.WriteHeader(StatusOK) } @@ -1872,6 +1900,7 @@ func (c *conn) serve(ctx context.Context) { if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 { // Wrap the Body reader with one that replies on the connection req.Body = &expectContinueReader{readCloser: req.Body, resp: w} + w.canWriteContinue.setTrue() } } else if req.Header.get("Expect") != "" { w.sendExpectationFailed() diff --git a/src/net/http/transport.go b/src/net/http/transport.go index a41e732d98..d37b52b13d 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -100,7 +100,7 @@ type Transport struct { idleLRU connLRU reqMu sync.Mutex - reqCanceler map[*Request]func(error) + reqCanceler map[cancelKey]func(error) altMu sync.Mutex // guards changing altProto only altProto atomic.Value // of nil or map[string]RoundTripper, key is URI scheme @@ -273,6 +273,13 @@ type Transport struct { ForceAttemptHTTP2 bool } +// A cancelKey is the key of the reqCanceler map. +// We wrap the *Request in this type since we want to use the original request, +// not any transient one created by roundTrip. +type cancelKey struct { + req *Request +} + func (t *Transport) writeBufferSize() int { if t.WriteBufferSize > 0 { return t.WriteBufferSize @@ -433,9 +440,10 @@ func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, error) { // optional extra headers to write and stores any error to return // from roundTrip. type transportRequest struct { - *Request // original request, not to be mutated - extra Header // extra headers to write, or nil - trace *httptrace.ClientTrace // optional + *Request // original request, not to be mutated + extra Header // extra headers to write, or nil + trace *httptrace.ClientTrace // optional + cancelKey cancelKey mu sync.Mutex // guards err err error // first setError value for mapRoundTripError to consider @@ -512,6 +520,7 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) { } origReq := req + cancelKey := cancelKey{origReq} req = setupRewindBody(req) if altRT := t.alternateRoundTripper(req); altRT != nil { @@ -546,7 +555,7 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) { } // treq gets modified by roundTrip, so we need to recreate for each retry. - treq := &transportRequest{Request: req, trace: trace} + treq := &transportRequest{Request: req, trace: trace, cancelKey: cancelKey} cm, err := t.connectMethodForRequest(treq) if err != nil { req.closeBody() @@ -559,7 +568,7 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) { // to send it requests. pconn, err := t.getConn(treq, cm) if err != nil { - t.setReqCanceler(req, nil) + t.setReqCanceler(cancelKey, nil) req.closeBody() return nil, err } @@ -567,7 +576,7 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) { var resp *Response if pconn.alt != nil { // HTTP/2 path. - t.setReqCanceler(req, nil) // not cancelable with CancelRequest + t.setReqCanceler(cancelKey, nil) // not cancelable with CancelRequest resp, err = pconn.alt.RoundTrip(req) } else { resp, err = pconn.roundTrip(treq) @@ -753,14 +762,14 @@ func (t *Transport) CloseIdleConnections() { // cancelable context instead. CancelRequest cannot cancel HTTP/2 // requests. func (t *Transport) CancelRequest(req *Request) { - t.cancelRequest(req, errRequestCanceled) + t.cancelRequest(cancelKey{req}, errRequestCanceled) } // Cancel an in-flight request, recording the error value. -func (t *Transport) cancelRequest(req *Request, err error) { +func (t *Transport) cancelRequest(key cancelKey, err error) { t.reqMu.Lock() - cancel := t.reqCanceler[req] - delete(t.reqCanceler, req) + cancel := t.reqCanceler[key] + delete(t.reqCanceler, key) t.reqMu.Unlock() if cancel != nil { cancel(err) @@ -1093,16 +1102,16 @@ func (t *Transport) removeIdleConnLocked(pconn *persistConn) bool { return removed } -func (t *Transport) setReqCanceler(r *Request, fn func(error)) { +func (t *Transport) setReqCanceler(key cancelKey, fn func(error)) { t.reqMu.Lock() defer t.reqMu.Unlock() if t.reqCanceler == nil { - t.reqCanceler = make(map[*Request]func(error)) + t.reqCanceler = make(map[cancelKey]func(error)) } if fn != nil { - t.reqCanceler[r] = fn + t.reqCanceler[key] = fn } else { - delete(t.reqCanceler, r) + delete(t.reqCanceler, key) } } @@ -1110,17 +1119,17 @@ func (t *Transport) setReqCanceler(r *Request, fn func(error)) { // for the request, we don't set the function and return false. // Since CancelRequest will clear the canceler, we can use the return value to detect if // the request was canceled since the last setReqCancel call. -func (t *Transport) replaceReqCanceler(r *Request, fn func(error)) bool { +func (t *Transport) replaceReqCanceler(key cancelKey, fn func(error)) bool { t.reqMu.Lock() defer t.reqMu.Unlock() - _, ok := t.reqCanceler[r] + _, ok := t.reqCanceler[key] if !ok { return false } if fn != nil { - t.reqCanceler[r] = fn + t.reqCanceler[key] = fn } else { - delete(t.reqCanceler, r) + delete(t.reqCanceler, key) } return true } @@ -1324,12 +1333,12 @@ func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (pc *persi // set request canceler to some non-nil function so we // can detect whether it was cleared between now and when // we enter roundTrip - t.setReqCanceler(req, func(error) {}) + t.setReqCanceler(treq.cancelKey, func(error) {}) return pc, nil } cancelc := make(chan error, 1) - t.setReqCanceler(req, func(err error) { cancelc <- err }) + t.setReqCanceler(treq.cancelKey, func(err error) { cancelc <- err }) // Queue for permission to dial. t.queueForDial(w) @@ -2078,7 +2087,7 @@ func (pc *persistConn) readLoop() { } if !hasBody || bodyWritable { - pc.t.setReqCanceler(rc.req, nil) + pc.t.setReqCanceler(rc.cancelKey, nil) // Put the idle conn back into the pool before we send the response // so if they process it quickly and make another request, they'll @@ -2151,7 +2160,7 @@ func (pc *persistConn) readLoop() { // reading the response body. (or for cancellation or death) select { case bodyEOF := <-waitForBodyRead: - pc.t.setReqCanceler(rc.req, nil) // before pc might return to idle pool + pc.t.setReqCanceler(rc.cancelKey, nil) // before pc might return to idle pool alive = alive && bodyEOF && !pc.sawEOF && @@ -2165,7 +2174,7 @@ func (pc *persistConn) readLoop() { pc.t.CancelRequest(rc.req) case <-rc.req.Context().Done(): alive = false - pc.t.cancelRequest(rc.req, rc.req.Context().Err()) + pc.t.cancelRequest(rc.cancelKey, rc.req.Context().Err()) case <-pc.closech: alive = false } @@ -2408,9 +2417,10 @@ type responseAndError struct { } type requestAndChan struct { - _ incomparable - req *Request - ch chan responseAndError // unbuffered; always send in select on callerGone + _ incomparable + req *Request + cancelKey cancelKey + ch chan responseAndError // unbuffered; always send in select on callerGone // whether the Transport (as opposed to the user client code) // added the Accept-Encoding gzip header. If the Transport @@ -2472,7 +2482,7 @@ var ( func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err error) { testHookEnterRoundTrip() - if !pc.t.replaceReqCanceler(req.Request, pc.cancelRequest) { + if !pc.t.replaceReqCanceler(req.cancelKey, pc.cancelRequest) { pc.t.putOrCloseIdleConn(pc) return nil, errRequestCanceled } @@ -2524,7 +2534,7 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err defer func() { if err != nil { - pc.t.setReqCanceler(req.Request, nil) + pc.t.setReqCanceler(req.cancelKey, nil) } }() @@ -2540,6 +2550,7 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err resc := make(chan responseAndError) pc.reqch <- requestAndChan{ req: req.Request, + cancelKey: req.cancelKey, ch: resc, addedGzip: requestedGzip, continueCh: continueCh, @@ -2591,10 +2602,10 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err } return re.res, nil case <-cancelChan: - pc.t.CancelRequest(req.Request) + pc.t.cancelRequest(req.cancelKey, errRequestCanceled) cancelChan = nil case <-ctxDoneChan: - pc.t.cancelRequest(req.Request, req.Context().Err()) + pc.t.cancelRequest(req.cancelKey, req.Context().Err()) cancelChan = nil ctxDoneChan = nil } diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index 31a41f5351..0a47687d9a 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -2364,6 +2364,50 @@ func TestTransportCancelRequest(t *testing.T) { } } +func testTransportCancelRequestInDo(t *testing.T, body io.Reader) { + setParallel(t) + defer afterTest(t) + if testing.Short() { + t.Skip("skipping test in -short mode") + } + unblockc := make(chan bool) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + <-unblockc + })) + defer ts.Close() + defer close(unblockc) + + c := ts.Client() + tr := c.Transport.(*Transport) + + donec := make(chan bool) + req, _ := NewRequest("GET", ts.URL, body) + go func() { + defer close(donec) + c.Do(req) + }() + start := time.Now() + timeout := 10 * time.Second + for time.Since(start) < timeout { + time.Sleep(100 * time.Millisecond) + tr.CancelRequest(req) + select { + case <-donec: + return + default: + } + } + t.Errorf("Do of canceled request has not returned after %v", timeout) +} + +func TestTransportCancelRequestInDo(t *testing.T) { + testTransportCancelRequestInDo(t, nil) +} + +func TestTransportCancelRequestWithBodyInDo(t *testing.T) { + testTransportCancelRequestInDo(t, bytes.NewBuffer([]byte{0})) +} + func TestTransportCancelRequestInDial(t *testing.T) { defer afterTest(t) if testing.Short() { diff --git a/src/net/ipsock_plan9.go b/src/net/ipsock_plan9.go index 99d3e3971a..23082366aa 100644 --- a/src/net/ipsock_plan9.go +++ b/src/net/ipsock_plan9.go @@ -206,9 +206,9 @@ func dialPlan9Blocking(ctx context.Context, net string, laddr, raddr Addr) (fd * return nil, err } if la := plan9LocalAddr(laddr); la == "" { - _, err = f.WriteString("connect " + dest) + err = hangupCtlWrite(ctx, proto, f, "connect "+dest) } else { - _, err = f.WriteString("connect " + dest + " " + la) + err = hangupCtlWrite(ctx, proto, f, "connect "+dest+" "+la) } if err != nil { f.Close() @@ -339,3 +339,27 @@ func plan9LocalAddr(addr Addr) string { } return ip.String() + "!" + itoa(port) } + +func hangupCtlWrite(ctx context.Context, proto string, ctl *os.File, msg string) error { + if proto != "tcp" { + _, err := ctl.WriteString(msg) + return err + } + written := make(chan struct{}) + errc := make(chan error) + go func() { + select { + case <-ctx.Done(): + ctl.WriteString("hangup") + errc <- mapErr(ctx.Err()) + case <-written: + errc <- nil + } + }() + _, err := ctl.WriteString(msg) + close(written) + if e := <-errc; err == nil && e != nil { // we hung up + return e + } + return err +} diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go index d26e981ae4..a00fd2395f 100644 --- a/src/net/textproto/reader.go +++ b/src/net/textproto/reader.go @@ -88,7 +88,7 @@ func (r *Reader) readLineSlice() ([]byte, error) { // The first call to ReadContinuedLine will return "Line 1 continued..." // and the second will return "Line 2". // -// A line consisting of only white space is never continued. +// Empty lines are never continued. // func (r *Reader) ReadContinuedLine() (string, error) { line, err := r.readContinuedLineSlice(noValidation) diff --git a/src/os/pipe_test.go b/src/os/pipe_test.go index 2e93e3946a..429bd813c2 100644 --- a/src/os/pipe_test.go +++ b/src/os/pipe_test.go @@ -104,6 +104,25 @@ func TestStdPipe(t *testing.T) { } } } + + // Test redirecting stdout but not stderr. Issue 40076. + cmd := osexec.Command(os.Args[0], "-test.run", "TestStdPipeHelper") + cmd.Stdout = w + var stderr bytes.Buffer + cmd.Stderr = &stderr + cmd.Env = append(os.Environ(), "GO_TEST_STD_PIPE_HELPER=1") + if err := cmd.Run(); err == nil { + t.Errorf("unexpected success of write to closed stdout") + } else if ee, ok := err.(*osexec.ExitError); !ok { + t.Errorf("unexpected exec error type %T: %v", err, err) + } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { + t.Errorf("unexpected wait status type %T: %v", ee.Sys(), ee.Sys()) + } else if !ws.Signaled() || ws.Signal() != syscall.SIGPIPE { + t.Errorf("unexpected exit status %v for write to closed stdout", err) + } + if output := stderr.Bytes(); len(output) > 0 { + t.Errorf("unexpected output on stderr: %s", output) + } } // This is a helper for TestStdPipe. It's not a test in itself. diff --git a/src/os/readfrom_linux.go b/src/os/readfrom_linux.go index ed275e1ba6..63ea45cf65 100644 --- a/src/os/readfrom_linux.go +++ b/src/os/readfrom_linux.go @@ -32,6 +32,11 @@ func (f *File) readFrom(r io.Reader) (written int64, handled bool, err error) { if !ok { return 0, false, nil } + if src.checkValid("ReadFrom") != nil { + // Avoid returning the error as we report handled as false, + // leave further error handling as the responsibility of the caller. + return 0, false, nil + } written, handled, err = pollCopyFileRange(&f.pfd, &src.pfd, remain) if lr != nil { diff --git a/src/os/readfrom_linux_test.go b/src/os/readfrom_linux_test.go index b6f5cb7034..00faf39fe5 100644 --- a/src/os/readfrom_linux_test.go +++ b/src/os/readfrom_linux_test.go @@ -8,6 +8,7 @@ import ( "bytes" "internal/poll" "io" + "io/ioutil" "math/rand" . "os" "path/filepath" @@ -170,6 +171,35 @@ func TestCopyFileRange(t *testing.T) { mustContainData(t, dst, data) }) }) + t.Run("Nil", func(t *testing.T) { + var nilFile *File + anyFile, err := ioutil.TempFile("", "") + if err != nil { + t.Fatal(err) + } + defer Remove(anyFile.Name()) + defer anyFile.Close() + + if _, err := io.Copy(nilFile, nilFile); err != ErrInvalid { + t.Errorf("io.Copy(nilFile, nilFile) = %v, want %v", err, ErrInvalid) + } + if _, err := io.Copy(anyFile, nilFile); err != ErrInvalid { + t.Errorf("io.Copy(anyFile, nilFile) = %v, want %v", err, ErrInvalid) + } + if _, err := io.Copy(nilFile, anyFile); err != ErrInvalid { + t.Errorf("io.Copy(nilFile, anyFile) = %v, want %v", err, ErrInvalid) + } + + if _, err := nilFile.ReadFrom(nilFile); err != ErrInvalid { + t.Errorf("nilFile.ReadFrom(nilFile) = %v, want %v", err, ErrInvalid) + } + if _, err := anyFile.ReadFrom(nilFile); err != ErrInvalid { + t.Errorf("anyFile.ReadFrom(nilFile) = %v, want %v", err, ErrInvalid) + } + if _, err := nilFile.ReadFrom(anyFile); err != ErrInvalid { + t.Errorf("nilFile.ReadFrom(anyFile) = %v, want %v", err, ErrInvalid) + } + }) } func testCopyFileRange(t *testing.T, size int64, limit int64) { diff --git a/src/runtime/lockrank_off.go b/src/runtime/lockrank_off.go index 891589c0f2..425ca8dd93 100644 --- a/src/runtime/lockrank_off.go +++ b/src/runtime/lockrank_off.go @@ -1,3 +1,7 @@ +// Copyright 2020 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. + // +build !goexperiment.staticlockranking package runtime diff --git a/src/runtime/lockrank_on.go b/src/runtime/lockrank_on.go index cf4151ff46..fbc5ff58b7 100644 --- a/src/runtime/lockrank_on.go +++ b/src/runtime/lockrank_on.go @@ -1,3 +1,7 @@ +// Copyright 2020 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. + // +build goexperiment.staticlockranking package runtime diff --git a/src/runtime/mpagealloc.go b/src/runtime/mpagealloc.go index 60f7f9ff58..8b3c62c375 100644 --- a/src/runtime/mpagealloc.go +++ b/src/runtime/mpagealloc.go @@ -233,16 +233,12 @@ type pageAlloc struct { // The address to start an allocation search with. It must never // point to any memory that is not contained in inUse, i.e. - // inUse.contains(searchAddr) must always be true. + // inUse.contains(searchAddr.addr()) must always be true. The one + // exception to this rule is that it may take on the value of + // maxOffAddr to indicate that the heap is exhausted. // - // When added with arenaBaseOffset, we guarantee that - // all valid heap addresses (when also added with - // arenaBaseOffset) below this value are allocated and - // not worth searching. - // - // Note that adding in arenaBaseOffset transforms addresses - // to a new address space with a linear view of the full address - // space on architectures with segmented address spaces. + // We guarantee that all valid heap addresses below this value + // are allocated and not worth searching. searchAddr offAddr // start and end represent the chunk indices @@ -518,6 +514,30 @@ func (s *pageAlloc) allocRange(base, npages uintptr) uintptr { return uintptr(scav) * pageSize } +// findMappedAddr returns the smallest mapped offAddr that is +// >= addr. That is, if addr refers to mapped memory, then it is +// returned. If addr is higher than any mapped region, then +// it returns maxOffAddr. +// +// s.mheapLock must be held. +func (s *pageAlloc) findMappedAddr(addr offAddr) offAddr { + // If we're not in a test, validate first by checking mheap_.arenas. + // This is a fast path which is only safe to use outside of testing. + ai := arenaIndex(addr.addr()) + if s.test || mheap_.arenas[ai.l1()] == nil || mheap_.arenas[ai.l1()][ai.l2()] == nil { + vAddr, ok := s.inUse.findAddrGreaterEqual(addr.addr()) + if ok { + return offAddr{vAddr} + } else { + // The candidate search address is greater than any + // known address, which means we definitely have no + // free memory left. + return maxOffAddr + } + } + return addr +} + // find searches for the first (address-ordered) contiguous free region of // npages in size and returns a base address for that region. // @@ -526,6 +546,7 @@ func (s *pageAlloc) allocRange(base, npages uintptr) uintptr { // // find also computes and returns a candidate s.searchAddr, which may or // may not prune more of the address space than s.searchAddr already does. +// This candidate is always a valid s.searchAddr. // // find represents the slow path and the full radix tree search. // @@ -695,7 +716,7 @@ nextLevel: // We found a sufficiently large run of free pages straddling // some boundary, so compute the address and return it. addr := levelIndexToOffAddr(l, i).add(uintptr(base) * pageSize).addr() - return addr, firstFree.base + return addr, s.findMappedAddr(firstFree.base) } if l == 0 { // We're at level zero, so that means we've exhausted our search. @@ -741,7 +762,7 @@ nextLevel: // found an even narrower free window. searchAddr := chunkBase(ci) + uintptr(searchIdx)*pageSize foundFree(offAddr{searchAddr}, chunkBase(ci+1)-searchAddr) - return addr, firstFree.base + return addr, s.findMappedAddr(firstFree.base) } // alloc allocates npages worth of memory from the page heap, returning the base diff --git a/src/runtime/mpagealloc_test.go b/src/runtime/mpagealloc_test.go index 89a4a2502c..65ba71d459 100644 --- a/src/runtime/mpagealloc_test.go +++ b/src/runtime/mpagealloc_test.go @@ -612,6 +612,63 @@ func TestPageAllocAlloc(t *testing.T) { baseChunkIdx + chunkIdxBigJump: {{0, PallocChunkPages}}, }, } + + // Test to check for issue #40191. Essentially, the candidate searchAddr + // discovered by find may not point to mapped memory, so we need to handle + // that explicitly. + // + // chunkIdxSmallOffset is an offset intended to be used within chunkIdxBigJump. + // It is far enough within chunkIdxBigJump that the summaries at the beginning + // of an address range the size of chunkIdxBigJump will not be mapped in. + const chunkIdxSmallOffset = 0x503 + tests["DiscontiguousBadSearchAddr"] = test{ + before: map[ChunkIdx][]BitRange{ + // The mechanism for the bug involves three chunks, A, B, and C, which are + // far apart in the address space. In particular, B is chunkIdxBigJump + + // chunkIdxSmalloffset chunks away from B, and C is 2*chunkIdxBigJump chunks + // away from A. A has 1 page free, B has several (NOT at the end of B), and + // C is totally free. + // Note that B's free memory must not be at the end of B because the fast + // path in the page allocator will check if the searchAddr even gives us + // enough space to place the allocation in a chunk before accessing the + // summary. + BaseChunkIdx + chunkIdxBigJump*0: {{0, PallocChunkPages - 1}}, + BaseChunkIdx + chunkIdxBigJump*1 + chunkIdxSmallOffset: { + {0, PallocChunkPages - 10}, + {PallocChunkPages - 1, 1}, + }, + BaseChunkIdx + chunkIdxBigJump*2: {}, + }, + scav: map[ChunkIdx][]BitRange{ + BaseChunkIdx + chunkIdxBigJump*0: {}, + BaseChunkIdx + chunkIdxBigJump*1 + chunkIdxSmallOffset: {}, + BaseChunkIdx + chunkIdxBigJump*2: {}, + }, + hits: []hit{ + // We first allocate into A to set the page allocator's searchAddr to the + // end of that chunk. That is the only purpose A serves. + {1, PageBase(BaseChunkIdx, PallocChunkPages-1), 0}, + // Then, we make a big allocation that doesn't fit into B, and so must be + // fulfilled by C. + // + // On the way to fulfilling the allocation into C, we estimate searchAddr + // using the summary structure, but that will give us a searchAddr of + // B's base address minus chunkIdxSmallOffset chunks. These chunks will + // not be mapped. + {100, PageBase(baseChunkIdx+chunkIdxBigJump*2, 0), 0}, + // Now we try to make a smaller allocation that can be fulfilled by B. + // In an older implementation of the page allocator, this will segfault, + // because this last allocation will first try to access the summary + // for B's base address minus chunkIdxSmallOffset chunks in the fast path, + // and this will not be mapped. + {9, PageBase(baseChunkIdx+chunkIdxBigJump*1+chunkIdxSmallOffset, PallocChunkPages-10), 0}, + }, + after: map[ChunkIdx][]BitRange{ + BaseChunkIdx + chunkIdxBigJump*0: {{0, PallocChunkPages}}, + BaseChunkIdx + chunkIdxBigJump*1 + chunkIdxSmallOffset: {{0, PallocChunkPages}}, + BaseChunkIdx + chunkIdxBigJump*2: {{0, 100}}, + }, + } } for name, v := range tests { v := v diff --git a/src/runtime/mranges.go b/src/runtime/mranges.go index e23d0778eb..2c0eb2c2dd 100644 --- a/src/runtime/mranges.go +++ b/src/runtime/mranges.go @@ -188,6 +188,25 @@ func (a *addrRanges) findSucc(addr uintptr) int { return len(a.ranges) } +// findAddrGreaterEqual returns the smallest address represented by a +// that is >= addr. Thus, if the address is represented by a, +// then it returns addr. The second return value indicates whether +// such an address exists for addr in a. That is, if addr is larger than +// any address known to a, the second return value will be false. +func (a *addrRanges) findAddrGreaterEqual(addr uintptr) (uintptr, bool) { + i := a.findSucc(addr) + if i == 0 { + return a.ranges[0].base.addr(), true + } + if a.ranges[i-1].contains(addr) { + return addr, true + } + if i < len(a.ranges) { + return a.ranges[i].base.addr(), true + } + return 0, false +} + // contains returns true if a covers the address addr. func (a *addrRanges) contains(addr uintptr) bool { i := a.findSucc(addr) diff --git a/src/runtime/os_linux_x86.go b/src/runtime/os_linux_x86.go index d001e6ee59..97f870707d 100644 --- a/src/runtime/os_linux_x86.go +++ b/src/runtime/os_linux_x86.go @@ -7,7 +7,10 @@ package runtime -import "runtime/internal/atomic" +import ( + "runtime/internal/atomic" + "unsafe" +) //go:noescape func uname(utsname *new_utsname) int @@ -55,6 +58,36 @@ func osArchInit() { return } + if major == 5 && minor == 4 && patch < 2 { + // All 5.4 versions of Ubuntu are patched. + procVersion := []byte("/proc/version\000") + f := open(&procVersion[0], _O_RDONLY, 0) + if f >= 0 { + var buf [512]byte + p := noescape(unsafe.Pointer(&buf[0])) + n := read(f, p, int32(len(buf))) + closefd(f) + + needle := []byte("Ubuntu") + contains: + for i, c := range buf[:n] { + if c != needle[0] { + continue + } + if int(n)-i < len(needle) { + break + } + for j, c2 := range needle { + if c2 != buf[i+j] { + continue contains + } + } + // This is an Ubuntu system. + return + } + } + } + if major == 5 && (minor == 2 || minor == 3 && patch < 15 || minor == 4 && patch < 2) { gsignalInitQuirk = mlockGsignal if m0.gsignal != nil { diff --git a/src/runtime/os_openbsd.go b/src/runtime/os_openbsd.go index b486b83688..cd3565df5b 100644 --- a/src/runtime/os_openbsd.go +++ b/src/runtime/os_openbsd.go @@ -340,6 +340,7 @@ func osStackRemap(s *mspan, flags int32) { } } +//go:nosplit func raise(sig uint32) { thrkill(getthrid(), int(sig)) } diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 2399f0a1d3..035822216d 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -132,7 +132,7 @@ func main() { if GOARCH != "wasm" { // no threads on wasm yet, so no sysmon systemstack(func() { - newm(sysmon, nil) + newm(sysmon, nil, -1) }) } @@ -562,7 +562,7 @@ func schedinit() { stackinit() mallocinit() fastrandinit() // must run before mcommoninit - mcommoninit(_g_.m) + mcommoninit(_g_.m, -1) cpuinit() // must run before alginit alginit() // maps must not be used before this call modulesinit() // provides activeModules @@ -623,7 +623,22 @@ func checkmcount() { } } -func mcommoninit(mp *m) { +// mReserveID returns the next ID to use for a new m. This new m is immediately +// considered 'running' by checkdead. +// +// sched.lock must be held. +func mReserveID() int64 { + if sched.mnext+1 < sched.mnext { + throw("runtime: thread ID overflow") + } + id := sched.mnext + sched.mnext++ + checkmcount() + return id +} + +// Pre-allocated ID may be passed as 'id', or omitted by passing -1. +func mcommoninit(mp *m, id int64) { _g_ := getg() // g0 stack won't make sense for user (and is not necessary unwindable). @@ -632,12 +647,12 @@ func mcommoninit(mp *m) { } lock(&sched.lock) - if sched.mnext+1 < sched.mnext { - throw("runtime: thread ID overflow") + + if id >= 0 { + mp.id = id + } else { + mp.id = mReserveID() } - mp.id = sched.mnext - sched.mnext++ - checkmcount() mp.fastrand[0] = uint32(int64Hash(uint64(mp.id), fastrandseed)) mp.fastrand[1] = uint32(int64Hash(uint64(cputicks()), ^fastrandseed)) @@ -1068,7 +1083,7 @@ func startTheWorldWithSema(emitTraceEvent bool) int64 { notewakeup(&mp.park) } else { // Start M to run P. Do not start another M below. - newm(nil, p) + newm(nil, p, -1) } } @@ -1413,12 +1428,13 @@ type cgothreadstart struct { // Allocate a new m unassociated with any thread. // Can use p for allocation context if needed. // fn is recorded as the new m's m.mstartfn. +// id is optional pre-allocated m ID. Omit by passing -1. // // This function is allowed to have write barriers even if the caller // isn't because it borrows _p_. // //go:yeswritebarrierrec -func allocm(_p_ *p, fn func()) *m { +func allocm(_p_ *p, fn func(), id int64) *m { _g_ := getg() acquirem() // disable GC because it can be called from sysmon if _g_.m.p == 0 { @@ -1447,7 +1463,7 @@ func allocm(_p_ *p, fn func()) *m { mp := new(m) mp.mstartfn = fn - mcommoninit(mp) + mcommoninit(mp, id) // In case of cgo or Solaris or illumos or Darwin, pthread_create will make us a stack. // Windows and Plan 9 will layout sched stack on OS stack. @@ -1586,7 +1602,7 @@ func oneNewExtraM() { // The sched.pc will never be returned to, but setting it to // goexit makes clear to the traceback routines where // the goroutine stack ends. - mp := allocm(nil, nil) + mp := allocm(nil, nil, -1) gp := malg(4096) gp.sched.pc = funcPC(goexit) + sys.PCQuantum gp.sched.sp = gp.stack.hi @@ -1757,9 +1773,11 @@ var newmHandoff struct { // Create a new m. It will start off with a call to fn, or else the scheduler. // fn needs to be static and not a heap allocated closure. // May run with m.p==nil, so write barriers are not allowed. +// +// id is optional pre-allocated m ID. Omit by passing -1. //go:nowritebarrierrec -func newm(fn func(), _p_ *p) { - mp := allocm(_p_, fn) +func newm(fn func(), _p_ *p, id int64) { + mp := allocm(_p_, fn, id) mp.nextp.set(_p_) mp.sigmask = initSigmask if gp := getg(); gp != nil && gp.m != nil && (gp.m.lockedExt != 0 || gp.m.incgo) && GOOS != "plan9" { @@ -1828,7 +1846,7 @@ func startTemplateThread() { releasem(mp) return } - newm(templateThread, nil) + newm(templateThread, nil, -1) releasem(mp) } @@ -1923,16 +1941,31 @@ func startm(_p_ *p, spinning bool) { } } mp := mget() - unlock(&sched.lock) if mp == nil { + // No M is available, we must drop sched.lock and call newm. + // However, we already own a P to assign to the M. + // + // Once sched.lock is released, another G (e.g., in a syscall), + // could find no idle P while checkdead finds a runnable G but + // no running M's because this new M hasn't started yet, thus + // throwing in an apparent deadlock. + // + // Avoid this situation by pre-allocating the ID for the new M, + // thus marking it as 'running' before we drop sched.lock. This + // new M will eventually run the scheduler to execute any + // queued G's. + id := mReserveID() + unlock(&sched.lock) + var fn func() if spinning { // The caller incremented nmspinning, so set m.spinning in the new M. fn = mspinning } - newm(fn, _p_) + newm(fn, _p_, id) return } + unlock(&sched.lock) if mp.spinning { throw("startm: m is spinning") } @@ -5192,7 +5225,9 @@ func runqputbatch(pp *p, q *gQueue, qsize int) { atomic.StoreRel(&pp.runqtail, t) if !q.empty() { + lock(&sched.lock) globrunqputbatch(q, int32(qsize)) + unlock(&sched.lock) } } diff --git a/src/runtime/race/README b/src/runtime/race/README index 65378c8ca6..34485f0fb2 100644 --- a/src/runtime/race/README +++ b/src/runtime/race/README @@ -6,8 +6,8 @@ To update the .syso files use golang.org/x/build/cmd/racebuild. race_darwin_amd64.syso built with LLVM 3496d6e4bea9cb99cb382939b7e79a50a3b863a5 and Go 553e003414d3aa90cc39830ee22f08453d9f3408. race_freebsd_amd64.syso built with LLVM 3496d6e4bea9cb99cb382939b7e79a50a3b863a5 and Go 553e003414d3aa90cc39830ee22f08453d9f3408. -race_linux_amd64.syso built with LLVM 3496d6e4bea9cb99cb382939b7e79a50a3b863a5 and Go 553e003414d3aa90cc39830ee22f08453d9f3408. -race_linux_ppc64le.syso built with LLVM 3496d6e4bea9cb99cb382939b7e79a50a3b863a5 and Go 553e003414d3aa90cc39830ee22f08453d9f3408. +race_linux_amd64.syso built with LLVM 6c75db8b4bc59eace18143ce086419d37da24746 and Go 7388956b76ce15a11346cebefcf6193db044caaf. +race_linux_ppc64le.syso built with LLVM 6c75db8b4bc59eace18143ce086419d37da24746 and Go 7388956b76ce15a11346cebefcf6193db044caaf. race_netbsd_amd64.syso built with LLVM 3496d6e4bea9cb99cb382939b7e79a50a3b863a5 and Go 553e003414d3aa90cc39830ee22f08453d9f3408. race_windows_amd64.syso built with LLVM 3496d6e4bea9cb99cb382939b7e79a50a3b863a5 and Go 553e003414d3aa90cc39830ee22f08453d9f3408. -race_linux_arm64.syso built with LLVM 3496d6e4bea9cb99cb382939b7e79a50a3b863a5 and Go 553e003414d3aa90cc39830ee22f08453d9f3408. +race_linux_arm64.syso built with LLVM 6c75db8b4bc59eace18143ce086419d37da24746 and Go 7388956b76ce15a11346cebefcf6193db044caaf. diff --git a/src/runtime/race/race_linux_amd64.syso b/src/runtime/race/race_linux_amd64.syso Binary files differindex 255b2e5c08..d31f85df56 100644 --- a/src/runtime/race/race_linux_amd64.syso +++ b/src/runtime/race/race_linux_amd64.syso diff --git a/src/runtime/race/race_linux_arm64.syso b/src/runtime/race/race_linux_arm64.syso Binary files differindex f15c5995e6..7c74171b0f 100644 --- a/src/runtime/race/race_linux_arm64.syso +++ b/src/runtime/race/race_linux_arm64.syso diff --git a/src/runtime/race/race_linux_ppc64le.syso b/src/runtime/race/race_linux_ppc64le.syso Binary files differindex 2bf5029659..a3c72bec55 100644 --- a/src/runtime/race/race_linux_ppc64le.syso +++ b/src/runtime/race/race_linux_ppc64le.syso diff --git a/src/runtime/race/syso_test.go b/src/runtime/race/syso_test.go new file mode 100644 index 0000000000..db846c5d2a --- /dev/null +++ b/src/runtime/race/syso_test.go @@ -0,0 +1,39 @@ +// Copyright 2020 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. + +// +build !android,!js,!ppc64le + +// Note: we don't run on Android or ppc64 because if there is any non-race test +// file in this package, the OS tries to link the .syso file into the +// test (even when we're not in race mode), which fails. I'm not sure +// why, but easiest to just punt - as long as a single builder runs +// this test, we're good. + +package race + +import ( + "bytes" + "os/exec" + "path/filepath" + "runtime" + "testing" +) + +func TestIssue37485(t *testing.T) { + files, err := filepath.Glob("./*.syso") + if err != nil { + t.Fatalf("can't find syso files: %s", err) + } + for _, f := range files { + cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), "tool", "nm", f) + res, err := cmd.CombinedOutput() + if err != nil { + t.Errorf("nm of %s failed: %s", f, err) + continue + } + if bytes.Contains(res, []byte("getauxval")) { + t.Errorf("%s contains getauxval", f) + } + } +} diff --git a/src/runtime/sys_darwin.go b/src/runtime/sys_darwin.go index 28c500a710..06474434c9 100644 --- a/src/runtime/sys_darwin.go +++ b/src/runtime/sys_darwin.go @@ -129,7 +129,7 @@ func syscall_rawSyscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintpt // syscallNoErr is used in crypto/x509 to call into Security.framework and CF. -//go:linkname crypto_x509_syscall crypto/x509/internal/macOS.syscall +//go:linkname crypto_x509_syscall crypto/x509/internal/macos.syscall //go:nosplit //go:cgo_unsafe_args func crypto_x509_syscall(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1 uintptr) { diff --git a/src/runtime/sys_linux_386.s b/src/runtime/sys_linux_386.s index 1b28098ad9..5b9b638ad7 100644 --- a/src/runtime/sys_linux_386.s +++ b/src/runtime/sys_linux_386.s @@ -222,7 +222,7 @@ TEXT runtime·mincore(SB),NOSPLIT,$0-16 RET // func walltime1() (sec int64, nsec int32) -TEXT runtime·walltime1(SB), NOSPLIT, $0-12 +TEXT runtime·walltime1(SB), NOSPLIT, $8-12 // We don't know how much stack space the VDSO code will need, // so switch to g0. @@ -233,6 +233,13 @@ TEXT runtime·walltime1(SB), NOSPLIT, $0-12 MOVL g_m(AX), SI // SI unchanged by C code. // Set vdsoPC and vdsoSP for SIGPROF traceback. + // Save the old values on stack and restore them on exit, + // so this function is reentrant. + MOVL m_vdsoPC(SI), CX + MOVL m_vdsoSP(SI), DX + MOVL CX, 0(SP) + MOVL DX, 4(SP) + LEAL sec+0(FP), DX MOVL -4(DX), CX MOVL CX, m_vdsoPC(SI) @@ -276,7 +283,15 @@ finish: MOVL 12(SP), BX // nsec MOVL BP, SP // Restore real SP - MOVL $0, m_vdsoSP(SI) + // Restore vdsoPC, vdsoSP + // We don't worry about being signaled between the two stores. + // If we are not in a signal handler, we'll restore vdsoSP to 0, + // and no one will care about vdsoPC. If we are in a signal handler, + // we cannot receive another signal. + MOVL 4(SP), CX + MOVL CX, m_vdsoSP(SI) + MOVL 0(SP), CX + MOVL CX, m_vdsoPC(SI) // sec is in AX, nsec in BX MOVL AX, sec_lo+0(FP) @@ -286,7 +301,7 @@ finish: // int64 nanotime(void) so really // void nanotime(int64 *nsec) -TEXT runtime·nanotime1(SB), NOSPLIT, $0-8 +TEXT runtime·nanotime1(SB), NOSPLIT, $8-8 // Switch to g0 stack. See comment above in runtime·walltime. MOVL SP, BP // Save old SP; BP unchanged by C code. @@ -296,6 +311,13 @@ TEXT runtime·nanotime1(SB), NOSPLIT, $0-8 MOVL g_m(AX), SI // SI unchanged by C code. // Set vdsoPC and vdsoSP for SIGPROF traceback. + // Save the old values on stack and restore them on exit, + // so this function is reentrant. + MOVL m_vdsoPC(SI), CX + MOVL m_vdsoSP(SI), DX + MOVL CX, 0(SP) + MOVL DX, 4(SP) + LEAL ret+0(FP), DX MOVL -4(DX), CX MOVL CX, m_vdsoPC(SI) @@ -332,7 +354,15 @@ finish: MOVL 12(SP), BX // nsec MOVL BP, SP // Restore real SP - MOVL $0, m_vdsoSP(SI) + // Restore vdsoPC, vdsoSP + // We don't worry about being signaled between the two stores. + // If we are not in a signal handler, we'll restore vdsoSP to 0, + // and no one will care about vdsoPC. If we are in a signal handler, + // we cannot receive another signal. + MOVL 4(SP), CX + MOVL CX, m_vdsoSP(SI) + MOVL 0(SP), CX + MOVL CX, m_vdsoPC(SI) // sec is in AX, nsec in BX // convert to DX:AX nsec diff --git a/src/runtime/sys_linux_amd64.s b/src/runtime/sys_linux_amd64.s index 58d3bc54b4..fe9c6bce85 100644 --- a/src/runtime/sys_linux_amd64.s +++ b/src/runtime/sys_linux_amd64.s @@ -206,7 +206,7 @@ TEXT runtime·mincore(SB),NOSPLIT,$0-28 // func walltime1() (sec int64, nsec int32) // non-zero frame-size means bp is saved and restored -TEXT runtime·walltime1(SB),NOSPLIT,$8-12 +TEXT runtime·walltime1(SB),NOSPLIT,$16-12 // We don't know how much stack space the VDSO code will need, // so switch to g0. // In particular, a kernel configured with CONFIG_OPTIMIZE_INLINING=n @@ -221,6 +221,13 @@ TEXT runtime·walltime1(SB),NOSPLIT,$8-12 MOVQ g_m(AX), BX // BX unchanged by C code. // Set vdsoPC and vdsoSP for SIGPROF traceback. + // Save the old values on stack and restore them on exit, + // so this function is reentrant. + MOVQ m_vdsoPC(BX), CX + MOVQ m_vdsoSP(BX), DX + MOVQ CX, 0(SP) + MOVQ DX, 8(SP) + LEAQ sec+0(FP), DX MOVQ -8(DX), CX MOVQ CX, m_vdsoPC(BX) @@ -244,8 +251,17 @@ noswitch: CALL AX MOVQ 0(SP), AX // sec MOVQ 8(SP), DX // nsec +ret: MOVQ BP, SP // Restore real SP - MOVQ $0, m_vdsoSP(BX) + // Restore vdsoPC, vdsoSP + // We don't worry about being signaled between the two stores. + // If we are not in a signal handler, we'll restore vdsoSP to 0, + // and no one will care about vdsoPC. If we are in a signal handler, + // we cannot receive another signal. + MOVQ 8(SP), CX + MOVQ CX, m_vdsoSP(BX) + MOVQ 0(SP), CX + MOVQ CX, m_vdsoPC(BX) MOVQ AX, sec+0(FP) MOVL DX, nsec+8(FP) RET @@ -257,15 +273,10 @@ fallback: MOVQ 0(SP), AX // sec MOVL 8(SP), DX // usec IMULQ $1000, DX - MOVQ BP, SP // Restore real SP - MOVQ $0, m_vdsoSP(BX) - MOVQ AX, sec+0(FP) - MOVL DX, nsec+8(FP) - RET + JMP ret // func nanotime1() int64 -// non-zero frame-size means bp is saved and restored -TEXT runtime·nanotime1(SB),NOSPLIT,$8-8 +TEXT runtime·nanotime1(SB),NOSPLIT,$16-8 // Switch to g0 stack. See comment above in runtime·walltime. MOVQ SP, BP // Save old SP; BP unchanged by C code. @@ -275,6 +286,13 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$8-8 MOVQ g_m(AX), BX // BX unchanged by C code. // Set vdsoPC and vdsoSP for SIGPROF traceback. + // Save the old values on stack and restore them on exit, + // so this function is reentrant. + MOVQ m_vdsoPC(BX), CX + MOVQ m_vdsoSP(BX), DX + MOVQ CX, 0(SP) + MOVQ DX, 8(SP) + LEAQ ret+0(FP), DX MOVQ -8(DX), CX MOVQ CX, m_vdsoPC(BX) @@ -298,8 +316,17 @@ noswitch: CALL AX MOVQ 0(SP), AX // sec MOVQ 8(SP), DX // nsec +ret: MOVQ BP, SP // Restore real SP - MOVQ $0, m_vdsoSP(BX) + // Restore vdsoPC, vdsoSP + // We don't worry about being signaled between the two stores. + // If we are not in a signal handler, we'll restore vdsoSP to 0, + // and no one will care about vdsoPC. If we are in a signal handler, + // we cannot receive another signal. + MOVQ 8(SP), CX + MOVQ CX, m_vdsoSP(BX) + MOVQ 0(SP), CX + MOVQ CX, m_vdsoPC(BX) // sec is in AX, nsec in DX // return nsec in AX IMULQ $1000000000, AX @@ -313,15 +340,8 @@ fallback: CALL AX MOVQ 0(SP), AX // sec MOVL 8(SP), DX // usec - MOVQ BP, SP // Restore real SP - MOVQ $0, m_vdsoSP(BX) IMULQ $1000, DX - // sec is in AX, nsec in DX - // return nsec in AX - IMULQ $1000000000, AX - ADDQ DX, AX - MOVQ AX, ret+0(FP) - RET + JMP ret TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0-28 MOVL how+0(FP), DI diff --git a/src/runtime/sys_linux_arm.s b/src/runtime/sys_linux_arm.s index e103da56dc..475f52344c 100644 --- a/src/runtime/sys_linux_arm.s +++ b/src/runtime/sys_linux_arm.s @@ -242,7 +242,7 @@ TEXT runtime·mincore(SB),NOSPLIT,$0 MOVW R0, ret+12(FP) RET -TEXT runtime·walltime1(SB),NOSPLIT,$0-12 +TEXT runtime·walltime1(SB),NOSPLIT,$8-12 // We don't know how much stack space the VDSO code will need, // so switch to g0. @@ -252,6 +252,13 @@ TEXT runtime·walltime1(SB),NOSPLIT,$0-12 MOVW g_m(g), R5 // R5 is unchanged by C code. // Set vdsoPC and vdsoSP for SIGPROF traceback. + // Save the old values on stack and restore them on exit, + // so this function is reentrant. + MOVW m_vdsoPC(R5), R1 + MOVW m_vdsoSP(R5), R2 + MOVW R1, 4(R13) + MOVW R2, 8(R13) + MOVW LR, m_vdsoPC(R5) MOVW R13, m_vdsoSP(R5) @@ -312,8 +319,15 @@ finish: MOVW 12(R13), R2 // nsec MOVW R4, R13 // Restore real SP - MOVW $0, R1 + // Restore vdsoPC, vdsoSP + // We don't worry about being signaled between the two stores. + // If we are not in a signal handler, we'll restore vdsoSP to 0, + // and no one will care about vdsoPC. If we are in a signal handler, + // we cannot receive another signal. + MOVW 8(R13), R1 MOVW R1, m_vdsoSP(R5) + MOVW 4(R13), R1 + MOVW R1, m_vdsoPC(R5) MOVW R0, sec_lo+0(FP) MOVW R1, sec_hi+4(FP) @@ -321,7 +335,7 @@ finish: RET // int64 nanotime1(void) -TEXT runtime·nanotime1(SB),NOSPLIT,$0-8 +TEXT runtime·nanotime1(SB),NOSPLIT,$8-8 // Switch to g0 stack. See comment above in runtime·walltime. // Save old SP. Use R13 instead of SP to avoid linker rewriting the offsets. @@ -330,6 +344,13 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$0-8 MOVW g_m(g), R5 // R5 is unchanged by C code. // Set vdsoPC and vdsoSP for SIGPROF traceback. + // Save the old values on stack and restore them on exit, + // so this function is reentrant. + MOVW m_vdsoPC(R5), R1 + MOVW m_vdsoSP(R5), R2 + MOVW R1, 4(R13) + MOVW R2, 8(R13) + MOVW LR, m_vdsoPC(R5) MOVW R13, m_vdsoSP(R5) @@ -390,8 +411,15 @@ finish: MOVW 12(R13), R2 // nsec MOVW R4, R13 // Restore real SP - MOVW $0, R4 + // Restore vdsoPC, vdsoSP + // We don't worry about being signaled between the two stores. + // If we are not in a signal handler, we'll restore vdsoSP to 0, + // and no one will care about vdsoPC. If we are in a signal handler, + // we cannot receive another signal. + MOVW 8(R13), R4 MOVW R4, m_vdsoSP(R5) + MOVW 4(R13), R4 + MOVW R4, m_vdsoPC(R5) MOVW $1000000000, R3 MULLU R0, R3, (R1, R0) diff --git a/src/runtime/sys_linux_arm64.s b/src/runtime/sys_linux_arm64.s index b23e3b9a11..198a5bacef 100644 --- a/src/runtime/sys_linux_arm64.s +++ b/src/runtime/sys_linux_arm64.s @@ -214,6 +214,13 @@ TEXT runtime·walltime1(SB),NOSPLIT,$24-12 MOVD g_m(g), R21 // R21 = m // Set vdsoPC and vdsoSP for SIGPROF traceback. + // Save the old values on stack and restore them on exit, + // so this function is reentrant. + MOVD m_vdsoPC(R21), R2 + MOVD m_vdsoSP(R21), R3 + MOVD R2, 8(RSP) + MOVD R3, 16(RSP) + MOVD LR, m_vdsoPC(R21) MOVD R20, m_vdsoSP(R21) @@ -269,7 +276,15 @@ finish: MOVD 8(RSP), R5 // nsec MOVD R20, RSP // restore SP - MOVD $0, m_vdsoSP(R21) // clear vdsoSP + // Restore vdsoPC, vdsoSP + // We don't worry about being signaled between the two stores. + // If we are not in a signal handler, we'll restore vdsoSP to 0, + // and no one will care about vdsoPC. If we are in a signal handler, + // we cannot receive another signal. + MOVD 16(RSP), R1 + MOVD R1, m_vdsoSP(R21) + MOVD 8(RSP), R1 + MOVD R1, m_vdsoPC(R21) MOVD R3, sec+0(FP) MOVW R5, nsec+8(FP) @@ -282,6 +297,13 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$24-8 MOVD g_m(g), R21 // R21 = m // Set vdsoPC and vdsoSP for SIGPROF traceback. + // Save the old values on stack and restore them on exit, + // so this function is reentrant. + MOVD m_vdsoPC(R21), R2 + MOVD m_vdsoSP(R21), R3 + MOVD R2, 8(RSP) + MOVD R3, 16(RSP) + MOVD LR, m_vdsoPC(R21) MOVD R20, m_vdsoSP(R21) @@ -337,7 +359,15 @@ finish: MOVD 8(RSP), R5 // nsec MOVD R20, RSP // restore SP - MOVD $0, m_vdsoSP(R21) // clear vdsoSP + // Restore vdsoPC, vdsoSP + // We don't worry about being signaled between the two stores. + // If we are not in a signal handler, we'll restore vdsoSP to 0, + // and no one will care about vdsoPC. If we are in a signal handler, + // we cannot receive another signal. + MOVD 16(RSP), R1 + MOVD R1, m_vdsoSP(R21) + MOVD 8(RSP), R1 + MOVD R1, m_vdsoPC(R21) // sec is in R3, nsec in R5 // return nsec in R3 diff --git a/src/runtime/sys_linux_mips64x.s b/src/runtime/sys_linux_mips64x.s index 6668a0fd86..afad056d06 100644 --- a/src/runtime/sys_linux_mips64x.s +++ b/src/runtime/sys_linux_mips64x.s @@ -214,13 +214,20 @@ TEXT runtime·mincore(SB),NOSPLIT|NOFRAME,$0-28 RET // func walltime1() (sec int64, nsec int32) -TEXT runtime·walltime1(SB),NOSPLIT,$16 +TEXT runtime·walltime1(SB),NOSPLIT,$16-12 MOVV R29, R16 // R16 is unchanged by C code MOVV R29, R1 MOVV g_m(g), R17 // R17 = m // Set vdsoPC and vdsoSP for SIGPROF traceback. + // Save the old values on stack and restore them on exit, + // so this function is reentrant. + MOVV m_vdsoPC(R17), R2 + MOVV m_vdsoSP(R17), R3 + MOVV R2, 8(R29) + MOVV R3, 16(R29) + MOVV R31, m_vdsoPC(R17) MOVV R29, m_vdsoSP(R17) @@ -249,7 +256,15 @@ finish: MOVV 8(R29), R5 // nsec MOVV R16, R29 // restore SP - MOVV R0, m_vdsoSP(R17) // clear vdsoSP + // Restore vdsoPC, vdsoSP + // We don't worry about being signaled between the two stores. + // If we are not in a signal handler, we'll restore vdsoSP to 0, + // and no one will care about vdsoPC. If we are in a signal handler, + // we cannot receive another signal. + MOVV 16(R29), R1 + MOVV R1, m_vdsoSP(R17) + MOVV 8(R29), R1 + MOVV R1, m_vdsoPC(R17) MOVV R3, sec+0(FP) MOVW R5, nsec+8(FP) @@ -260,13 +275,20 @@ fallback: SYSCALL JMP finish -TEXT runtime·nanotime1(SB),NOSPLIT,$16 +TEXT runtime·nanotime1(SB),NOSPLIT,$16-8 MOVV R29, R16 // R16 is unchanged by C code MOVV R29, R1 MOVV g_m(g), R17 // R17 = m // Set vdsoPC and vdsoSP for SIGPROF traceback. + // Save the old values on stack and restore them on exit, + // so this function is reentrant. + MOVV m_vdsoPC(R17), R2 + MOVV m_vdsoSP(R17), R3 + MOVV R2, 8(R29) + MOVV R3, 16(R29) + MOVV R31, m_vdsoPC(R17) MOVV R29, m_vdsoSP(R17) @@ -295,7 +317,15 @@ finish: MOVV 8(R29), R5 // nsec MOVV R16, R29 // restore SP - MOVV R0, m_vdsoSP(R17) // clear vdsoSP + // Restore vdsoPC, vdsoSP + // We don't worry about being signaled between the two stores. + // If we are not in a signal handler, we'll restore vdsoSP to 0, + // and no one will care about vdsoPC. If we are in a signal handler, + // we cannot receive another signal. + MOVV 16(R29), R1 + MOVV R1, m_vdsoSP(R17) + MOVV 8(R29), R1 + MOVV R1, m_vdsoPC(R17) // sec is in R3, nsec in R5 // return nsec in R3 diff --git a/src/runtime/sys_linux_ppc64x.s b/src/runtime/sys_linux_ppc64x.s index 8629fe3233..fd69ee70a5 100644 --- a/src/runtime/sys_linux_ppc64x.s +++ b/src/runtime/sys_linux_ppc64x.s @@ -185,7 +185,7 @@ TEXT runtime·mincore(SB),NOSPLIT|NOFRAME,$0-28 RET // func walltime1() (sec int64, nsec int32) -TEXT runtime·walltime1(SB),NOSPLIT,$16 +TEXT runtime·walltime1(SB),NOSPLIT,$16-12 MOVD R1, R15 // R15 is unchanged by C code MOVD g_m(g), R21 // R21 = m @@ -196,6 +196,13 @@ TEXT runtime·walltime1(SB),NOSPLIT,$16 BEQ fallback // Set vdsoPC and vdsoSP for SIGPROF traceback. + // Save the old values on stack and restore them on exit, + // so this function is reentrant. + MOVD m_vdsoPC(R21), R4 + MOVD m_vdsoSP(R21), R5 + MOVD R4, 32(R1) + MOVD R5, 40(R1) + MOVD LR, R14 MOVD R14, m_vdsoPC(R21) MOVD R15, m_vdsoSP(R21) @@ -214,11 +221,20 @@ noswitch: MOVD R1, R4 BL (CTR) // Call from VDSO MOVD $0, R0 // Restore R0 - MOVD R0, m_vdsoSP(R21) // Clear vdsoSP MOVD 0(R1), R3 // sec MOVD 8(R1), R5 // nsec MOVD R15, R1 // Restore SP + // Restore vdsoPC, vdsoSP + // We don't worry about being signaled between the two stores. + // If we are not in a signal handler, we'll restore vdsoSP to 0, + // and no one will care about vdsoPC. If we are in a signal handler, + // we cannot receive another signal. + MOVD 40(R1), R6 + MOVD R6, m_vdsoSP(R21) + MOVD 32(R1), R6 + MOVD R6, m_vdsoPC(R21) + finish: MOVD R3, sec+0(FP) MOVW R5, nsec+8(FP) @@ -232,7 +248,7 @@ fallback: MOVD 40(R1), R5 JMP finish -TEXT runtime·nanotime1(SB),NOSPLIT,$16 +TEXT runtime·nanotime1(SB),NOSPLIT,$16-8 MOVD $1, R3 // CLOCK_MONOTONIC MOVD R1, R15 // R15 is unchanged by C code @@ -243,6 +259,13 @@ TEXT runtime·nanotime1(SB),NOSPLIT,$16 BEQ fallback // Set vdsoPC and vdsoSP for SIGPROF traceback. + // Save the old values on stack and restore them on exit, + // so this function is reentrant. + MOVD m_vdsoPC(R21), R4 + MOVD m_vdsoSP(R21), R5 + MOVD R4, 32(R1) + MOVD R5, 40(R1) + MOVD LR, R14 // R14 is unchanged by C code MOVD R14, m_vdsoPC(R21) MOVD R15, m_vdsoSP(R21) @@ -261,11 +284,20 @@ noswitch: MOVD R1, R4 BL (CTR) // Call from VDSO MOVD $0, R0 // Restore R0 - MOVD $0, m_vdsoSP(R21) // Clear vdsoSP MOVD 0(R1), R3 // sec MOVD 8(R1), R5 // nsec MOVD R15, R1 // Restore SP + // Restore vdsoPC, vdsoSP + // We don't worry about being signaled between the two stores. + // If we are not in a signal handler, we'll restore vdsoSP to 0, + // and no one will care about vdsoPC. If we are in a signal handler, + // we cannot receive another signal. + MOVD 40(R1), R6 + MOVD R6, m_vdsoSP(R21) + MOVD 32(R1), R6 + MOVD R6, m_vdsoPC(R21) + finish: // sec is in R3, nsec in R5 // return nsec in R3 diff --git a/src/sync/runtime2.go b/src/sync/runtime2.go index 931edad9f1..f10c4e8e0e 100644 --- a/src/sync/runtime2.go +++ b/src/sync/runtime2.go @@ -1,3 +1,7 @@ +// Copyright 2020 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. + // +build !goexperiment.staticlockranking package sync diff --git a/src/sync/runtime2_lockrank.go b/src/sync/runtime2_lockrank.go index 5a68e901fa..aaa1c27626 100644 --- a/src/sync/runtime2_lockrank.go +++ b/src/sync/runtime2_lockrank.go @@ -1,3 +1,7 @@ +// Copyright 2020 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. + // +build goexperiment.staticlockranking package sync diff --git a/src/syscall/exec_linux.go b/src/syscall/exec_linux.go index 23d7343d3a..b7351cda82 100644 --- a/src/syscall/exec_linux.go +++ b/src/syscall/exec_linux.go @@ -465,7 +465,7 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att } _, _, err1 = RawSyscall(SYS_DUP3, uintptr(fd[i]), uintptr(nextfd), O_CLOEXEC) if _SYS_dup != SYS_DUP3 && err1 == ENOSYS { - _, _, err1 = RawSyscall(_SYS_dup, uintptr(pipe), uintptr(nextfd), 0) + _, _, err1 = RawSyscall(_SYS_dup, uintptr(fd[i]), uintptr(nextfd), 0) if err1 != 0 { goto childerror } diff --git a/src/testing/testing.go b/src/testing/testing.go index 85da6bb02a..061142b9ab 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Package testing provides support for automated testing of Go packages. -// It is intended to be used in concert with the ``go test'' command, which automates +// It is intended to be used in concert with the "go test" command, which automates // execution of any function of the form // func TestXxx(*testing.T) // where Xxx does not start with a lowercase letter. The function name @@ -14,8 +14,8 @@ // To write a new test suite, create a file whose name ends _test.go that // contains the TestXxx functions as described here. Put the file in the same // package as the one being tested. The file will be excluded from regular -// package builds but will be included when the ``go test'' command is run. -// For more detail, run ``go help test'' and ``go help testflag''. +// package builds but will be included when the "go test" command is run. +// For more detail, run "go help test" and "go help testflag". // // A simple test function looks like this: // diff --git a/src/text/template/multi_test.go b/src/text/template/multi_test.go index bf1f1b2701..34d2378e38 100644 --- a/src/text/template/multi_test.go +++ b/src/text/template/multi_test.go @@ -359,6 +359,7 @@ func TestEmptyTemplate(t *testing.T) { in string want string }{ + {[]string{"x", "y"}, "", "y"}, {[]string{""}, "once", ""}, {[]string{"", ""}, "twice", ""}, {[]string{"{{.}}", "{{.}}"}, "twice", "twice"}, diff --git a/test/codegen/memops.go b/test/codegen/memops.go index cd35910c12..a234283146 100644 --- a/test/codegen/memops.go +++ b/test/codegen/memops.go @@ -354,3 +354,26 @@ func idxCompare(i int) int { } return 1 } + +func idxFloatOps(a []float64, b []float32, i int) (float64, float32) { + c := float64(7) + // amd64: `ADDSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+` + c += a[i+1] + // amd64: `SUBSD\t16\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+` + c -= a[i+2] + // amd64: `MULSD\t24\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+` + c *= a[i+3] + // amd64: `DIVSD\t32\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+` + c /= a[i+4] + + d := float32(8) + // amd64: `ADDSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+` + d += b[i+1] + // amd64: `SUBSS\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+` + d -= b[i+2] + // amd64: `MULSS\t12\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+` + d *= b[i+3] + // amd64: `DIVSS\t16\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+` + d /= b[i+4] + return c, d +} diff --git a/test/fixedbugs/issue33308.go b/test/fixedbugs/issue33308.go new file mode 100644 index 0000000000..b0fd6a450c --- /dev/null +++ b/test/fixedbugs/issue33308.go @@ -0,0 +1,12 @@ +// errorcheck + +// Copyright 2020 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. + +// Test that the compiler does not crash on a []byte conversion of an +// untyped expression. +package p + +var v uint +var x = []byte((1 << v) + 1) // ERROR "cannot convert" diff --git a/test/fixedbugs/issue40367.go b/test/fixedbugs/issue40367.go new file mode 100644 index 0000000000..0dc5ad7120 --- /dev/null +++ b/test/fixedbugs/issue40367.go @@ -0,0 +1,41 @@ +// run + +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func case1() { + rates := []int32{1,2,3,4,5,6} + var sink [6]int + j := len(sink) + for star, _ := range rates { + if star+1 < 1 { + panic("") + } + j-- + sink[j] = j + } +} + +func case2() { + i := 0 + var sink [3]int + j := len(sink) +top: + j-- + sink[j] = j + if i < 2 { + i++ + if i < 1 { + return + } + goto top + } +} + +func main() { + case1() + case2() +}
\ No newline at end of file diff --git a/test/prove.go b/test/prove.go index d37021d283..3c19c513b6 100644 --- a/test/prove.go +++ b/test/prove.go @@ -670,7 +670,8 @@ func oforuntil(b []int) { i := 0 if len(b) > i { top: - println(b[i]) // ERROR "Induction variable: limits \[0,\?\), increment 1$" "Proved IsInBounds$" + // TODO: remove the todo of next line once we complete the following optimization of CL 244579 + // println(b[i]) // todo: ERROR "Induction variable: limits \[0,\?\), increment 1$" "Proved IsInBounds$" i++ if i < len(b) { goto top @@ -720,7 +721,8 @@ func range1(b []int) { // range2 elements are larger, so they use the general form of a range loop. func range2(b [][32]int) { for i, v := range b { - b[i][0] = v[0] + 1 // ERROR "Induction variable: limits \[0,\?\), increment 1$" "Proved IsInBounds$" + // TODO: remove the todo of next line once we complete the following optimization of CL 244579 + b[i][0] = v[0] + 1 // todo: ERROR "Induction variable: limits \[0,\?\), increment 1$" "Proved IsInBounds$" if i < len(b) { // ERROR "Proved Less64$" println("x") } |