1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Checking of compiler and linker flags.
6 // We must avoid flags like -fplugin=, which can allow
7 // arbitrary code execution during the build.
8 // Do not make changes here without carefully
9 // considering the implications.
10 // (That's why the code is isolated in a file named security.go.)
12 // Note that -Wl,foo means split foo on commas and pass to
13 // the linker, so that -Wl,-foo,bar means pass -foo bar to
14 // the linker. Similarly -Wa,foo for the assembler and so on.
15 // If any of these are permitted, the wildcard portion must
18 // Note also that GNU binutils accept any argument @foo
19 // as meaning "read more flags from the file foo", so we must
20 // guard against any command-line argument beginning with @,
21 // even things like "-I @foo".
22 // We use load.SafeArg (which is even more conservative)
25 // Even worse, gcc -I@foo (one arg) turns into cc1 -I @foo (two args),
26 // so although gcc doesn't expand the @foo, cc1 will.
27 // So out of paranoia, we reject @ at the beginning of every
28 // flag argument that might be split into its own argument.
33 "cmd/go/internal/load"
40 var re
= regexp
.MustCompile
42 var validCompilerFlags
= []*regexp
.Regexp
{
43 re(`-D([A-Za-z_].*)`),
48 re(`-W([^@,]+)`), // -Wall but not -Wa,-foo.
53 re(`-f(no-)?constant-cfstrings`),
54 re(`-fdiagnostics-show-note-include-stack`),
55 re(`-f(no-)?exceptions`),
56 re(`-f(no-)?inline-functions`),
57 re(`-finput-charset=([^@\-].*)`),
58 re(`-f(no-)?fat-lto-objects`),
60 re(`-fmacro-backtrace-limit=(.+)`),
61 re(`-fmessage-length=(.+)`),
62 re(`-f(no-)?modules`),
63 re(`-f(no-)?objc-arc`),
64 re(`-f(no-)?omit-frame-pointer`),
65 re(`-f(no-)?openmp(-simd)?`),
66 re(`-f(no-)?permissive`),
67 re(`-f(no-)?(pic|PIC|pie|PIE)`),
69 re(`-f(no-)?split-stack`),
70 re(`-f(no-)?stack-(.+)`),
71 re(`-f(no-)?strict-aliasing`),
72 re(`-f(un)signed-char`),
73 re(`-f(no-)?use-linker-plugin`), // safe if -B is not used; we don't permit -B
74 re(`-fsanitize=(.+)`),
75 re(`-ftemplate-depth-(.+)`),
76 re(`-fvisibility=(.+)`),
80 re(`-m(arch|cpu|fpu|tune)=([^@\-].*)`),
81 re(`-m(no-)?avx[0-9a-z.]*`),
82 re(`-m(no-)?ms-bitfields`),
83 re(`-m(no-)?stack-(.+)`),
85 re(`-mios-simulator-version-min=(.+)`),
86 re(`-miphoneos-version-min=(.+)`),
87 re(`-mnop-fun-dllimport`),
88 re(`-m(no-)?sse[0-9.]*`),
90 re(`-pedantic(-errors)?`),
93 re(`-?-std=([^@\-].*)`),
94 re(`-?-stdlib=([^@\-].*)`),
99 var validCompilerFlagsWithNextArg
= []string{
111 var validLinkerFlags
= []*regexp
.Regexp
{
117 re(`-f(no-)?(pic|PIC|pie|PIE)`),
118 re(`-fsanitize=([^@\-].*)`),
120 re(`-m(arch|cpu|fpu|tune)=([^@\-].*)`),
122 re(`-mios-simulator-version-min=(.+)`),
123 re(`-miphoneos-version-min=(.+)`),
125 re(`-(pic|PIC|pie|PIE)`),
128 re(`-?-static([-a-z0-9+]*)`),
129 re(`-?-stdlib=([^@\-].*)`),
131 // Note that any wildcards in -Wl need to exclude comma,
132 // since -Wl splits its argument at commas and passes
133 // them all to the linker uninterpreted. Allowing comma
134 // in a wildcard would allow tunnelling arbitrary additional
135 // linker arguments through one of these.
136 re(`-Wl,--(no-)?allow-multiple-definition`),
137 re(`-Wl,--(no-)?as-needed`),
141 re(`-Wl,--disable-new-dtags`),
142 re(`-Wl,--enable-new-dtags`),
143 re(`-Wl,--end-group`),
144 re(`-Wl,-framework,[^,@\-][^,]+`),
145 re(`-Wl,-headerpad_max_install_names`),
146 re(`-Wl,--no-undefined`),
147 re(`-Wl,-rpath[=,]([^,@\-][^,]+)`),
148 re(`-Wl,-search_paths_first`),
149 re(`-Wl,-sectcreate,([^,@\-][^,]+),([^,@\-][^,]+),([^,@\-][^,]+)`),
150 re(`-Wl,--start-group`),
152 re(`-Wl,--subsystem,(native|windows|console|posix|xbox)`),
153 re(`-Wl,-undefined[=,]([^,@\-][^,]+)`),
154 re(`-Wl,-?-unresolved-symbols=[^,]+`),
155 re(`-Wl,--(no-)?warn-([^,]+)`),
156 re(`-Wl,-z,(no)?execstack`),
159 re(`[a-zA-Z0-9_/].*\.(a|o|obj|dll|dylib|so)`), // direct linker inputs: x.o or libfoo.so (but not -foo.o or @foo.o)
162 var validLinkerFlagsWithNextArg
= []string{
176 func checkCompilerFlags(name
, source
string, list
[]string) error
{
177 return checkFlags(name
, source
, list
, validCompilerFlags
, validCompilerFlagsWithNextArg
)
180 func checkLinkerFlags(name
, source
string, list
[]string) error
{
181 return checkFlags(name
, source
, list
, validLinkerFlags
, validLinkerFlagsWithNextArg
)
184 func checkFlags(name
, source
string, list
[]string, valid
[]*regexp
.Regexp
, validNext
[]string) error
{
185 // Let users override rules with $CGO_CFLAGS_ALLOW, $CGO_CFLAGS_DISALLOW, etc.
188 disallow
*regexp
.Regexp
190 if env
:= os
.Getenv("CGO_" + name
+ "_ALLOW"); env
!= "" {
191 r
, err
:= regexp
.Compile(env
)
193 return fmt
.Errorf("parsing $CGO_%s_ALLOW: %v", name
, err
)
197 if env
:= os
.Getenv("CGO_" + name
+ "_DISALLOW"); env
!= "" {
198 r
, err
:= regexp
.Compile(env
)
200 return fmt
.Errorf("parsing $CGO_%s_DISALLOW: %v", name
, err
)
206 for i
:= 0; i
< len(list
); i
++ {
208 if disallow
!= nil && disallow
.FindString(arg
) == arg
{
211 if allow
!= nil && allow
.FindString(arg
) == arg
{
214 for _
, re
:= range valid
{
215 if re
.FindString(arg
) == arg
{ // must be complete match
219 for _
, x
:= range validNext
{
221 if i
+1 < len(list
) && load
.SafeArg(list
[i
+1]) {
226 // Permit -Wl,-framework -Wl,name.
227 if i
+1 < len(list
) &&
228 strings
.HasPrefix(arg
, "-Wl,") &&
229 strings
.HasPrefix(list
[i
+1], "-Wl,") &&
230 load
.SafeArg(list
[i
+1][4:]) &&
231 !strings
.Contains(list
[i
+1][4:], ",") {
237 return fmt
.Errorf("invalid flag in %s: %s %s (see https://golang.org/s/invalidflag)", source
, arg
, list
[i
+1])
239 return fmt
.Errorf("invalid flag in %s: %s without argument (see https://golang.org/s/invalidflag)", source
, arg
)
243 return fmt
.Errorf("invalid flag in %s: %s", source
, arg
)