-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathoptions.go
More file actions
80 lines (71 loc) · 2.81 KB
/
Copy pathoptions.go
File metadata and controls
80 lines (71 loc) · 2.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// options.go implements the constructor Option mechanism and the LOUD
// degradation gate. The only option so far is WithInsecureFallback —
// deliberately ugly to type: a silent fallback to the heap is the exact
// footgun this library exists to prevent, so accepting one must be visible
// at the call site.
package secmem
import (
"log/slog"
"runtime"
"sync"
)
// config collects the effects of constructor Options.
type config struct {
insecureFallback bool
}
// Option configures a constructor ([NewBuffer], [NewEmptyBuffer],
// [NewSyscallSafeBuffer], [NewBufferFromReader], [NewArena]).
type Option func(*config)
// WithInsecureFallback permits a constructor to fall back to plain Go heap
// memory on platforms with no lockable off-heap memory (everything except
// linux, darwin, and windows).
//
// It is a permission, not a demand: on supported platforms it changes
// nothing. On unsupported platforms it converts the constructor's
// [ErrNoSecureMemory] failure into a heap allocation with NO protection —
// not locked, swappable, GC-visible, included in core dumps. The resulting
// [Capabilities] report Insecure=true, Warnings() leads with the exposure,
// and a one-time slog warning fires on first use.
func WithInsecureFallback() Option {
return func(c *config) { c.insecureFallback = true }
}
// applyOptions folds opts into a config. Nil options are ignored (no-panic).
func applyOptions(opts []Option) config {
var cfg config
for _, o := range opts {
if o != nil {
o(&cfg)
}
}
return cfg
}
// gateInsecure is the LOUD degradation policy: allocation on a platform with
// no secure memory fails with [ErrNoSecureMemory] unless the caller opted in.
// The platform fact is a parameter (rather than read from the build-tagged
// const directly) so the policy is testable on every platform.
func gateInsecure(platformSecure bool, cfg config) error {
if platformSecure {
return nil
}
if !cfg.insecureFallback {
return ErrNoSecureMemory
}
warnInsecureFallback()
return nil
}
// insecureWarnOnce backs the one-time LOUD warning on first use of the
// fallback.
var insecureWarnOnce sync.Once //nolint:gochecknoglobals // once-only process-wide warning.
// warnInsecureFallback fires the one-time warning that a constructor has
// accepted the plain-heap fallback. It lives on the gate, not in the stub
// allocator: Probe allocates through the same path to REPORT what the
// platform provides, and used to trip the warning on a platform where no
// caller had opted into anything — a false alarm on the one call meant to
// tell the truth quietly.
func warnInsecureFallback() {
insecureWarnOnce.Do(func() {
slog.Warn("secmem: INSECURE fallback in use — secrets are on the unprotected Go heap "+
"(not locked, swappable, GC-visible, included in core dumps)",
"GOOS", runtime.GOOS, "GOARCH", runtime.GOARCH)
})
}