Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: Strict-Mode: Memory guarantees in Go. #63171

Closed
snadrus opened this issue Sep 22, 2023 · 1 comment
Closed

proposal: Strict-Mode: Memory guarantees in Go. #63171

snadrus opened this issue Sep 22, 2023 · 1 comment

Comments

@snadrus
Copy link

snadrus commented Sep 22, 2023

Abstract
Single-threaded Go code (without C or Unsafe) cannot describe memory-clobbering errors like stack smashing, map simultaneous-write, or unmutexed half-writes. Rust is similar here, but Go has better readability. During multi-threading, GoRoutines and CSP provide better ergonomics again. But multi-threaded Go brings risks of dangerous, hard-to-find memory problems. How can we change Go to alleviate these risks while retaining readability?

I propose a backard-compatible compilation switch (--strict) to bring a tough definition to complex codebases that need it.
Changes from regular Go when compiling with --strict (StrictMode-v1):

  1. No passing pointers (or anything with references) to the creation of GoRoutines Ex: go func(copied1, copied2 any)

v2: Unless it's definitely a move: parent is owner & does not use it afterward

  1. No modifying globals (or anything they reference) outside of init’s 1 goroutine

Any goroutines made from init must follow “runtime rules”, ex: not modify globals either.
This includes references to globals, so these will all need notation as readonly.

  1. No passing pointers (or anything with references) over channels or panics.

Channels can be passed
v2: unless it’s a move

  1. No sync & atomic packages(because they’re useless given the above rules) except atomic.Value is allowed for types without references.

v2: identify which variables can only be accessed when their mutex is locked

  1. WARN on all unsafe/cgo calls unless commented as known evaluated for this main binary's package.
  2. Obscure for -buildmode=plugin: late-init packages can not modify any globals but their own

This could be generalized to “all init cannot modify other packages globals” or strict mode could simply not allow Go plugins for v1 (neither as a build target nor allowing opening a plug-in).

How compatible is this with Today’s Go?

  • ALL single-thread libraries without editable globals already follow this.
  • Code with dependency injection is friendly to this
  • Item 2 (above) will need considerable clean-up in most codebases.
  • Items 3-5 (above) limit multithreaded communication to copies over CSP.

What’s StrictMode-v2:
The same guarantees with increased flexibility in exchange for more code analysis time.
V2 can also benefit from a runtime constant (read only) stamp. Anything constant (including constant containers full of constants) could be shared even if pointed to. This would create the same “freedom” that globals-from-init get.

Integration tests are likely the only place --strict will be used as the program should operate the same in either context. So why not put it in a linter? Because it's likely that future iterations will need new syntax for proving.

Partial Example: A thread-shared (ex) string-to-int Cache in V1:
In some cache.New: go func(ch chan<- cacheReq ) will initialize a map[string]int for the data

type cacheReq struct { Query string, retVal <-chan }
func (c *Cache)Read(key string) int { 
  out := make(chan string)
  c.ch <- cacheReq{ Query: key, out }
  return <-out
}

Cache writer not shown here for simplicity, but it will be an addition to cacheReq.

V1 will not be friendly enough for high-performance code. It is not even compatible with the http server, but it could be by V2.

Why this fix?
Readability: Go would not lose its readable nature, so it would enable more junior high performance developers while keeping a stable codebase.
Incremental: Just like how Escape Analysis improved, over time the restrictions will lighten and rich error messages can describe exactly why something cannot be proven safe. In the future, new optional syntax may arrive to signal intent and keep the proving work minimal.
Easy First Step: Restricting syntax is fairly straightforward. Types are known at compile-time. Passing-through a readonly type-modifier (for globals) will be the hardest part of v1. Where it's too difficult to do statically, it's reasonable in v1 to throw a compile error.

@gopherbot gopherbot added this to the Proposal milestone Sep 22, 2023
@ianlancetaylor
Copy link
Member

This is an interesting idea but it's not an actionable proposal. And it describes a language that is significant different from Go as it exists today. We don't use the issue tracker for discussions, so I'm going to close this issue. I encourage you to take it to a forum like golang-nuts. See https://go.dev/wiki/Questions.

@golang golang locked and limited conversation to collaborators Sep 21, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants