techie
6d | Jan 20, 2021, 12:20:10 AM
Command PATH security in Go
/ng/golang

Today’s Go security release
fixes an issue involving PATH lookups in untrusted directories
that can lead to remote execution during the go get command.
We expect people to have questions about what exactly this means
and whether they might have issues in their own programs.
This post details the bug, the fixes we have applied,
how to decide whether your own programs are vulnerable to similar problems,
and what you can do if they are.




Go command & remote execution
One of the design goals for the go command is that most commands – including
go build, go doc, go get, go install, and go list – do not run
arbitrary code downloaded from the internet.
There are a few obvious exceptions:
clearly go run, go test, and go generate do run arbitrary code – that's their job.
But the others must not, for a variety of reasons including reproducible builds and security.
So when go get can be tricked into executing arbitrary code, we consider that a security bug.
If go get must not run arbitrary code, then unfortunately that means
all the programs it invokes, such as compilers and version control systems, are also inside the security perimeter.
For example, we've had issues in the past in which clever use of obscure compiler features
or remote execution bugs in version control systems became remote execution bugs in Go.
(On that note, Go 1.16 aims to improve the situation by introducing a GOVCS setting
that allows configuration of exactly which version control systems are allowed and when.)
Today's bug, however, was entirely our fault, not a bug or obscure feature of gcc or git.
The bug involves how Go and other programs find other executables,
so we need to spend a little time looking at that before we can get to the details.




Commands and PATHs and Go
All operating systems have a concept of an executable path
($PATH on Unix, %PATH% on Windows; for simplicity, we'll just use the term PATH),
which is a list of directories.
When you type a command into a shell prompt,
the shell looks in each of the listed directories,
in turn, for an executable with the name you typed.
It runs the first one it finds, or it prints a message like “command not found.”
On Unix, this idea first appeared in Seventh Edition Unix's Bourne shell (1979). The manual explained:

The shell parameter $PATH defines the search path for the directory containing the command.
Each alternative directory name is separated by a colon (:).
The default path is :/bin:/usr/bin.
If the command name contains a / then the search path is not used.
Otherwise, each directory in the path is searched for an executable file.

Note the default: the current directory (denoted here by an empty string,
but let's call it “dot”)
is listed ahead of /bin and /usr/bin.
MS-DOS and then Windows chose to hard-code that behavior:
on those systems, dot is always searched first,
automatically, before considering any directories listed in %PATH%.
As Grampp and Morris pointed out in their
classic paper “UNIX Operating System Security” (1984),
placing dot ahead of system directories in the PATH
means that if you cd into a directory and run ls,
you might get a malicious copy from that directory
instead of the system utility.
And if you can trick a system administrator to run ls in your home directory
while logged in as root, then you can run any code you want.
Because of this problem and others like it,
essentially all modern Unix distributions set a new user's default PATH
to exclude dot.
But Windows systems continue to search dot first, no matter what PATH says.
For example, when you type the command
go version

on a typically-configured Unix,
the shell runs a go executable from a system directory in your PATH.
But when you type that command on Windows,
cmd.exe checks dot first.
If .\go.exe (or .\go.bat or many other choices) exists,
cmd.exe runs that executable, not one from your PATH.
For Go, PATH searches are handled by exec.LookPath,
called automatically by
exec.Command.
And to fit well into the host system, Go's exec.LookPath
implements the Unix rules on Unix and the Windows rules on Windows.
For example, this command
out, err := exec.Command("go", "version").CombinedOutput()

behaves the same as typing go version into the operating system shell.
On Windows, it runs .\go.exe when that exists.
(It is worth noting that Windows PowerShell changed this behavior,
dropping the implicit search of dot, but cmd.exe and the
Windows C library SearchPath function
continue to behave as they always have.
Go continues to match cmd.exe.)




The Bug
When go get downloads and builds a package that contains
import "C", it runs a program called cgo to prepare the Go
equivalent of the relevant C code.
The go command runs cgo in the directory containing the package sources.
Once cgo has generated its Go output files,
the go command itself invokes the Go compiler
on the generated Go files
and the host C compiler (gcc or clang)
to build any C sources included with the package.
All this works well.
But where does the go command find the host C compiler?
It looks in the PATH, of course. Luckily, while it runs the C compiler
in the package source directory, it does the PATH lookup
from the original directory where the go command was invoked:
cmd := exec.Command("gcc", "file.c")
cmd.Dir = "badpkg"
cmd.Run()

So even if badpkg\gcc.exe exists on a Windows system,
this code snippet will not find it.
The lookup that happens in exec.Command does not know
about the badpkg directory.
The go command uses similar code to invoke cgo,
and in that case there's not even a path lookup,
because cgo always comes from GOROOT:
cmd := exec.Command(GOROOT+"/pkg/tool/"+GOOS_GOARCH+"/cgo", "file.go")
cmd.Dir = "badpkg"
cmd.Run()

This is even safer than the previous snippet:
there's no chance of running any bad cgo.exe that may exist.
But it turns out that cgo itself also invokes the host C compiler,
on some temporary files it creates, meaning it executes this code itself:
// running in cgo in badpkg dir
cmd := exec.Command("gcc", "tmpfile.c")
cmd.Run()

Now, because cgo itself is running in badpkg,
not in the directory where the go command was run,
it will run badpkg\gcc.exe if that file exists,
instead of finding the system gcc.
So an attacker can create a malicious package that uses cgo and
includes a gcc.exe, and then any Windows user
that runs go get to download and build the attacker's package
will run the attacker-supplied gcc.exe in preference to any
gcc in the system path.
Unix systems avoid the problem first because dot is typically not
in the PATH and second because module unpacking does not
set execute bits on the files it writes.
But Unix users who have dot ahead of system directories
in their PATH and are using GOPATH mode would be as susceptible
as Windows users.
(If that describes you, today is a good day to remove dot from your path
and to start using Go modules.)
(Thanks to RyotaK for reporting this issue to us.)




The Fixes
It's obviously unacceptable for the go get command to download
and run a malicious gcc.exe.
But what's the actual mistake that allows that?
And then what's the fix?
One possible answer is that the mistake is that cgo does the search for the host C compiler
in the untrusted source directory instead of in the directory where the go command
was invoked.
If that's the mistake,
then the fix is to change the go command to pass cgo the full path to the
host C compiler, so that cgo need not do a PATH lookup in
to the untrusted directory.
Another possible answer is that the mistake is to look in dot
during PATH lookups, whether happens automatically on Windows
or because of an explicit PATH entry on a Unix system.
A user may want to look in dot to find a command they typed
in a console or shell window,
but it's unlikely they also want to look there to find a subprocess of a subprocess
of a typed command.
If that's the mistake,
then the fix is to change the cgo command not to look in dot during a PATH lookup.
We decided both were mistakes, so we applied both fixes.
The go command now passes the full host C compiler path to cgo.
On top of that, cgo, go, and every other command in the Go distribution
now use a variant of the os/exec package that reports an error if it would
have previously used an executable from dot.
The packages go/build and go/import use the same policy for
their invocation of the go command and other tools.
This should shut the door on any similar security problems that may be lurking.
Out of an abundance of caution, we also made a similar fix in
commands like goimports and gopls,
as well as the libraries
golang.org/x/tools/go/analysis
and
golang.org/x/tools/go/packages,
which invoke the go command as a subprocess.
If you run these programs in untrusted directories –
for example, if you git checkout untrusted repositories
and cd into them and then run programs like these,
and you use Windows or use Unix with dot in your PATH –
then you should update your copies of these commands too.
If the only untrusted directories on your computer
are the ones in the module cache managed by go get,
then you only need the new Go release.
After updating to the new Go release, you can update to the latest gopls by using:
GO111MODULE=on \
go get golang.org/x/tools/gopls@v0.6.4

and you can update to the latest goimports or other tools by using:
GO111MODULE=on \
go get golang.org/x/tools/cmd/goimports@v0.1.0

You can update programs that depend on golang.org/x/tools/go/packages,
even before their authors do,
by adding an explicit upgrade of the dependency during go get:
GO111MODULE=on \
go get example.com/cmd/thecmd golang.org/x/tools@v0.1.0

For programs that use go/build, it is sufficient for you to recompile them
using the updated Go release.
Again, you only need to update these other programs if you
are a Windows user or a Unix user with dot in the PATH
and you run these programs in source directories you do not trust
that may contain malicious programs.




Are your own programs affected?
If you use exec.LookPath or exec.Command in your own programs,
you only need to be concerned if you (or your users) run your program
in a directory with untrusted contents.
If so, then a subprocess could be started using an executable
from dot instead of from a system directory.
(Again, using an executable from dot happens always on Windows
and only with uncommon PATH settings on Unix.)
If you are concerned, then we've published the more restricted variant
of os/exec as golang.org/x/sys/execabs.
You can use it in your program by simply replacing
import "os/exec"

with
import exec "golang.org/x/sys/execabs"

and recompiling.




Securing os/exec by default
We have been discussing on
golang.org/issue/38736
whether the Windows behavior of always preferring the current directory
in PATH lookups (during exec.Command and exec.LookPath)
should be changed.
The argument in favor of the change is that it closes the kinds of
security problems discussed in this blog post.
A supporting argument is that although the Windows SearchPath API
and cmd.exe still always search the current directory,
PowerShell, the successor to cmd.exe, does not,
an apparent recognition that the original behavior was a mistake.
The argument against the change is that it could break existing Windows
programs that intend to find programs in the current directory.
We don’t know how many such programs exist,
but they would get unexplained failures if the PATH lookups
started skipping the current directory entirely.
The approach we have taken in golang.org/x/sys/execabs may
be a reasonable middle ground.
It finds the result of the old PATH lookup and then returns a
clear error rather than use a result from the current directory.
The error returned from exec.Command("prog") when prog.exe exists looks like:
prog resolves to executable in current directory (.\prog.exe)

For programs that do change behavior, this error should make very clear what has happened.
Programs that intend to run a program from the current directory can use
exec.Command("./prog") instead (that syntax works on all systems, even Windows).
We have filed this idea as a new proposal, golang.org/issue/43724.





blog.golang.org/path-security ...

0x | 0x | 0x | 0x
techie
1mo | Dec 17, 2020, 7:20:22 PM
Go on ARM and Beyond
/ng/golang

The industry is abuzz about non-x86 processors recently,
so we thought it would be worth a brief post about Go’s support for them.
It has always been important to us for Go to be portable,
not overfitting to any particular operating system or architecture.
The initial open source release of Go
included support for two operating systems (Linux and Mac OS X) and three
architectures (64-bit x86,
32-bit x86, and 32-bit ARM).
Over the years, we’ve added support for many more operating systems and architecture combinations:

Go 1 (March 2012) supported the original systems as well as FreeBSD,
NetBSD, and OpenBSD on 64-bit and 32-bit x86,
and Plan 9 on 32-bit x86.
Go 1.3 (June 2014) added support for Solaris on 64-bit x86.
Go 1.4 (December 2014) added support for Android on 32-bit ARM and Plan 9 on 64-bit x86.
Go 1.5 (August 2015) added support for Linux on 64-bit ARM and 64-bit PowerPC,
as well as iOS on 32-bit and 64-bit ARM.
Go 1.6 (February 2016) added support for Linux on 64-bit MIPS,
as well as Android on 32-bit x86.
It also added an official binary download for Linux on 32-bit ARM,
primarily for Raspberry Pi systems.
Go 1.7 (August 2016) added support for Linux on z Systems (S390x) and Plan 9 on 32-bit ARM.
Go 1.8 (February 2017) added support for Linux on 32-bit MIPS,
and it added official binary downloads for Linux on 64-bit PowerPC and z Systems.
Go 1.9 (August 2017) added official binary downloads for Linux on 64-bit ARM.
Go 1.12 (February 2018) added support for Windows 10 IoT Core on 32-bit ARM,
such as the Raspberry Pi 3.
It also added support for AIX on 64-bit PowerPC.
Go 1.14 (February 2019) added support for Linux on 64-bit RISC-V.

Although the x86-64 port got most of the attention in the early days of Go,
today all our target architectures are well supported by our SSA-based compiler back end
and produce excellent code.
We’ve been helped along the way by many contributors,
including engineers from Amazon, ARM, Atos,
IBM, Intel, and MIPS.
Go supports cross-compiling for all these systems out of the box with minimal effort.
For example, to build an app for 32-bit x86-based Windows from a 64-bit Linux system:
GOARCH=386 GOOS=windows go build myapp # writes myapp.exe

In the past year, several major vendors have made announcements of new ARM64
hardware for servers,
laptops and developer machines.
Go was well-positioned for this. For years now,
Go has been powering Docker, Kubernetes, and the rest of the Go ecosystem
on ARM64 Linux servers,
as well as mobile apps on ARM64 Android and iOS devices.
Since Apple’s announcement of the Mac transitioning to Apple silicon this summer,
Apple and Google have been working together to ensure that Go and the broader
Go ecosystem work well on them,
both running Go x86 binaries under Rosetta 2 and running native Go ARM64 binaries.
Earlier this week, we released the first Go 1.16 beta,
which includes native support for Macs using the M1 chip.
You can download and try the Go 1.16 beta for M1 Macs and all your other
systems on the Go download page.
(Of course, this is a beta release and, like all betas,
it certainly has bugs we don’t know about.
If you run into any problems, please report them at golang.org/issue/new.)
It’s always nice to use the same CPU architecture for local development as in production,
to remove one variation between the two environments.
If you deploy to ARM64 production servers,
Go makes it easy to develop on ARM64 Linux and Mac systems too.
But of course, it’s still as easy as ever to work on one system and cross-compile
for deployment to another,
whether you’re working on an x86 system and deploying to ARM,
working on Windows and deploying to Linux,
or some other combination.
The next target we’d like to add support for is ARM64 Windows 10 systems.
If you have expertise and would like to help,
we’re coordinating work on golang.org/issue/36439.





blog.golang.org/ports ...

0x | 0x | 0x | 0x
techie
1mo | Dec 15, 2020, 5:20:33 PM
Redirecting godoc.org requests to pkg.go.dev
/ng/golang

With the introduction of Go modules and the growth of the Go ecosystem,
pkg.go.dev was
launched in 2019 to provide a central place
where developers can discover and evaluate Go packages and modules. Like
godoc.org, pkg.go.dev serves Go documentation, but it also supports modules,
better search functionality, and signals to help Go users to find the right
packages.
As we shared in January 2020, our
goal is to eventually redirect traffic from godoc.org to the corresponding page
on pkg.go.dev. We’ve also made it possible for users to opt in to redirecting
their own requests from godoc.org to pkg.go.dev.
We’ve received a lot of great feedback this year, which has been tracked and
resolved through the
pkgsite/godoc.org-redirect
and pkgsite/design-2020
milestones on the Go issue tracker. Your feedback resulted in support for
popular feature requests on pkg.go.dev,
open sourcing pkgsite, and most recently, a
redesign of pkg.go.dev.




Next Steps
The next step in this migration is to redirect all requests from godoc.org to
the corresponding page on pkg.go.dev.
This will happen in early 2021, once the work tracked at the
pkgsite/godoc.org-redirect milestone
is complete.
During this migration, updates will be posted to
Go issue 43178.
We encourage everyone to begin using pkg.go.dev today. You can do so by
visiting godoc.org?redirect=on, or clicking
“Always use pkg.go.dev” in the top right corner of any godoc.org page.




FAQs
Will godoc.org URLs continue to work?
Yes! We will redirect all requests arriving at godoc.org to the equivalent page
on pkg.go.dev, so all your bookmarks and links will continue to take you to the
documentation you need.
What will happen to the golang/gddo repository?
The gddo repository will remain available
for anyone who wants to keep running it themselves, or even fork and improve
it. We will mark it archived to make clear that we will no longer accept
contributions. However, you will be able to continue forking the repository.
Will api.godoc.org continue to work?
This transition will have no impact on api.godoc.org. Until an API is available
for pkg.go.dev, api.godoc.org will continue to serve traffic. See
Go issue 36785 for updates on an API for
pkg.go.dev.
Will my godoc.org badges keep working?
Yes! Badge URLs will redirect to the equivalent URL on pkg.go.dev too. Your
page will automatically get a new pkg.go.dev badge. You can also generate a new
badge at pkg.go.dev/badge if you would like to
update your badge link.




Feedback
As always, feel free to file an issue
on the Go issue tracker for any feedback.




Contributing
Pkg.go.dev is an open source project. If
you’re interested in contributing to the pkgsite project, check out the
contribution guidelines
or join the

pkgsite channel on Gophers Slack

to learn more.





blog.golang.org/godoc.org-redirect ...

0x | 0x | 0x | 0x
techie
3mo | Nov 10, 2020, 4:42:06 PM
Pkg.go.dev has a new look!
/ng/golang

Since launching pkg.go.dev, we’ve received a lot of great feedback on
design and usability.
In particular, it was clear that the way information was organized confused
users when navigating the site.
Today we’re excited to share a redesigned pkg.go.dev,
which we hope will be clearer and more helpful.
This blog post presents the highlights. For details,
see Go issue 41585.




Consistent landing page for all paths
The main change is that the pkg.go.dev/<path> page has been reorganized
around the idea of a path.
A path represents a directory in a particular version of a module.
Now, regardless of what’s in that directory,
every path page will have the same layout,
with the goal of making the experience consistently useful and predictable.



The path page will display the README at that path if there is one.
Previously, the overview tab only showed the README if present at the module root.
This is one of many changes we’re making to place the most important information up front.




Documentation navigation
The documentation section now displays an index along with a sidenav.
This gives the ability to see the full package API,
while having context as they are navigating the documentation section.
There is also a new Jump To input box in the left sidenav,
to search for identifiers.



See Go issue 41587 for details on changes in the documentation section.




Metadata on main page
The top bar on each page now shows additional metadata,
such as each package’s “imports” and “imported by” counts.
Banners also show information about the latest minor and major versions of a module.
See Go issue 41588 for details.






Video Walkthrough
Last week at Google Open Source Live,
we presented a walkthrough of the new site experience in our talk,
Level Up: Go Package Discovery and Editor Tooling.








Feedback
We’re excited to share this updated design with you.
As always, please let us know what you think via the “Share Feedback”
and “Report an Issue” links at the bottom of every page of the site.
And if you’re interested in contributing to this project, pkg.go.dev is open source! Check out the
contribution guidelines
to find out more.

0x | 0x | 0x | 0x
techie
3mo | Nov 10, 2020, 4:42:06 PM
Eleven Years of Go
/ng/golang

Today we celebrate the eleventh birthday of the Go open source release.
The parties we had for
Go turning 10
seem like a distant memory.
It’s been a tough year, but
we’ve kept Go development moving forward
and accumulated quite a few highlights.
In November, we launched go.dev and pkg.go.dev
shortly after Go’s 10th birthday.
In February, the Go 1.14 release
delivered the first officially “production-ready” implementation of Go modules,
along with many performance improvements,
including
faster defers
and
non-cooperative goroutine preemption
to reduce scheduling
and garbage collection latency.
In early March, we launched a
new API for protocol buffers,
google.golang.org/protobuf,
with much-improved support for protocol buffer reflection and custom messages.

When the pandemic hit, we decided to pause any public announcements
or launches in the spring,
recognizing that everyone’s attention rightly belonged elsewhere.
But we kept working, and one of our team members joined the
Apple/Google collaboration on
privacy-preserving exposure notifications
to support contact tracing efforts all over the world.
In May, that group launched the
reference backend server,
written in Go.
We continued to improve gopls,
which enables advanced
Go-aware support
in many editors.
In June, the
VSCode Go extension officially joined the Go project
and is now maintained by the same developers who work on gopls.
Also in June, thanks to your feedback, we open-sourced
the code behind pkg.go.dev
as part of the Go project as well.
Later in June, we
released the latest design draft for generics,
along with a prototype tool and generics playground.
In July, we published and discussed three new design drafts for future changes:
new //go:build lines for file selection,
file system interfaces,
and
build-time file embedding.
(We’ll see all of those in 2021, as noted below.)
In August, the Go 1.15 release
delivered mainly optimizations and bug fixes rather than new features.
The most significant was the start of a rewrite of the linker,
making it run 20% faster and use 30% less memory
on average for large builds.
Last month, we ran our annual Go user survey.
We will post results on the blog once we’ve analyzed them.
The Go community has adapted to “virtual-first” along with everyone else,
and we saw many virtual meetups and over a dozen virtual Go conferences this year.
Last week, the Go team hosted
Go day at Google Open Source Live
(videos at the link).




Going Forward
We’re also incredibly excited about what’s in store for Go’s 12th year.
Most immediately, this week Go team members will
be presenting eight events at
GopherCon 2020.
Mark your calendars!

“Typing [Generic] Go”,
a talk by Robert Griesemer,
Nov 11, 10:00 AM (US Eastern);
Q&A at 10:30 AM.
“What to Expect When You’re NOT Expecting”,
a live taping of the Go time podcast with a panel of expert debuggers,
including Hana Kim,
Nov 11 12:00 PM.
“Evolving the Go Memory Manager's RAM and CPU Efficiency”,
a talk by Michael Knyszek,
Nov 11 1:00 PM;
Q&A at 1:50 PM.
“Implementing Faster Defers”,
a talk by Dan Scales,
Nov 11 5:10 PM;
Q&A at 5:40 PM.
“Go Team - Ask Me Anything”,
a live Q&A with Julie Qiu, Rebecca Stambler, Russ Cox, Sameer Ajmani, and Van Riper,
Nov 12 3:00 PM.
“Pardon the Interruption: Loop Preemption in Go 1.14”,
a talk by Austin Clements,
Nov 12 4:45 PM;
Q&A at 5:15 PM.
“Working with Errors”,
a talk by Jonathan Amsterdam,
Nov 13 1:00 PM;
Q&A at 1:50 PM.
“Crossing the Chasm for Go: Two Million Users and Growing”,
a talk by Carmen Andoh,
Nov 13 5:55 PM.





Go Releases
In February, the Go 1.16 release will include the new
file system interfaces
and
build-time file embedding.
It will complete the linker rewrite, bringing additional performance improvements.
And it will include support for the new Apple Silicon (GOARCH=arm64) Macs.
In August, the Go 1.17 release will no doubt bring more features and improvements,
although it’s far enough out that the exact details remain up in the air.
It will include a new register-based calling convention for x86-64
(without breaking existing assembly!),
which will make programs faster across the board.
(Other architectures will follow in later releases.)
One nice feature that will definitely be included is the
new //go:build lines,
which are far less error-prone than the
current // +build lines.
Another highly anticipated feature we hope will be ready for beta testing next year
is
support for fuzzing in the go test command.




Go Modules
Over the next year, we will continue to work on developing support for Go modules
and integrating them well into the entire Go ecosystem.
Go 1.16 will include our smoothest Go modules experience yet.
One preliminary result from our recent survey is that 96% of users
have now adopted Go modules (up from 90% a year ago).
We will also finally wind down support for GOPATH-based development:
any programs using dependencies other than the standard library will need a go.mod.
(If you haven’t switched to modules yet, see the
GOPATH wiki page
for details about this final step in the journey from GOPATH to modules.)
From the start, the goal for Go modules
has been “to add the concept of package versions to the working vocabulary
of both Go developers and our tools,”
to enable deep support for modules and versions throughout the Go ecosystem.
The Go module mirror, checksum database, and index
were made possible by this ecosystem-wide understanding of what a package version is.
Over the next year, we will see rich module support added to more tools and systems.
For example, we plan to investigate new tooling to help module authors publish new versions
(go release)
as well as to help module consumers update their code to migrate away from
deprecated APIs (a new go fix).
As a larger example,
we created gopls
to reduce many tools used by editors for Go support,
none of which supported modules, down to a single one that did.
Over the next year,
we’ll be ready to make the VSCode Go extension use gopls by default,
for an excellent module experience out of the box,
and we’ll release gopls 1.0.
Of course, one of the best things about gopls is that it is editor-neutral:
any editor that understands the
language server protocol
can use it.
Another important use of version information is tracking whether
any package in a build has a known vulnerability.
Over the next year, we plan to develop a database of known vulnerabilities
as well as tools to check your programs against that database.
The Go package discovery site
pkg.go.dev
is another example of a version-aware system enabled by Go modules.
We’ve been focused on getting the core functionality and user experience right,
including a
redesign launching today.
Over the next year,
we will be unifying godoc.org into pkg.go.dev.
We will also be expanding the version timeline for each package,
showing important changes in each version,
known vulnerabilities, and more,
following the overall goal of surfacing what you need to make
informed decisions about adding dependencies.
We’re excited to see this journey from GOPATH to Go modules
nearing completion and all the excellent dependency-aware tools
that Go modules are enabling.




Generics
The next feature on everyone’s minds is of course generics.
As we mentioned above, we published the
latest design draft for generics
back in June.
Since then, we’ve continued to refine rough edges and have turned our
attention to the details of implementing a production-ready version.
We will be working on that throughout 2021, with a goal of having
something for people to try out by the end of the year,
perhaps a part of the Go 1.18 betas.




Thank You!
Go is far more than just us on the Go team at Google.
We are indebted to the contributors who work with us with the Go releases and tools.
Beyond that, Go only succeeds because of all of you who work in
and contribute to Go’s thriving ecosystem.
It has been a difficult year in the world outside Go.
More than ever, we appreciate you taking the time
to join us and help make Go such a success.
Thank you.
We hope you are all staying safe and wish you all the best.

0x | 0x | 0x | 0x
techie
6mo | Aug 11, 2020, 10:12:16 PM
0x | 0x | 0x | 0x
mmm7777
7mo | Jun 17, 2020, 9:42:08 PM
0x | 0x | 0x | 0x