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_].*)`),
49 re(`-W([^@,]+)`), // -Wall but not -Wa,-foo.
51 re(`-Wp,-D([A-Za-z_].*)`),
53 re(`-f(no-)?asynchronous-unwind-tables`),
55 re(`-f(no-)builtin-[a-zA-Z0-9_]*`),
57 re(`-f(no-)?constant-cfstrings`),
58 re(`-fdiagnostics-show-note-include-stack`),
59 re(`-f(no-)?eliminate-unused-debug-types`),
60 re(`-f(no-)?exceptions`),
61 re(`-f(no-)?fast-math`),
62 re(`-f(no-)?inline-functions`),
63 re(`-finput-charset=([^@\-].*)`),
64 re(`-f(no-)?fat-lto-objects`),
65 re(`-f(no-)?keep-inline-dllexport`),
67 re(`-fmacro-backtrace-limit=(.+)`),
68 re(`-fmessage-length=(.+)`),
69 re(`-f(no-)?modules`),
70 re(`-f(no-)?objc-arc`),
71 re(`-f(no-)?objc-nonfragile-abi`),
72 re(`-f(no-)?objc-legacy-dispatch`),
73 re(`-f(no-)?omit-frame-pointer`),
74 re(`-f(no-)?openmp(-simd)?`),
75 re(`-f(no-)?permissive`),
76 re(`-f(no-)?(pic|PIC|pie|PIE)`),
79 re(`-f(no-)?split-stack`),
80 re(`-f(no-)?stack-(.+)`),
81 re(`-f(no-)?strict-aliasing`),
82 re(`-f(un)signed-char`),
83 re(`-f(no-)?use-linker-plugin`), // safe if -B is not used; we don't permit -B
84 re(`-f(no-)?visibility-inlines-hidden`),
85 re(`-fsanitize=(.+)`),
86 re(`-ftemplate-depth-(.+)`),
87 re(`-fvisibility=(.+)`),
91 re(`-m(abi|arch|cpu|fpu|tune)=([^@\-].*)`),
93 re(`-mfloat-abi=([^@\-].*)`),
94 re(`-mfpmath=[0-9a-z,+]*`),
95 re(`-m(no-)?avx[0-9a-z.]*`),
96 re(`-m(no-)?ms-bitfields`),
97 re(`-m(no-)?stack-(.+)`),
99 re(`-mios-simulator-version-min=(.+)`),
100 re(`-miphoneos-version-min=(.+)`),
101 re(`-mnop-fun-dllimport`),
102 re(`-m(no-)?sse[0-9.]*`),
103 re(`-mthumb(-interwork)?`),
106 re(`--param=ssp-buffer-size=[0-9]*`),
107 re(`-pedantic(-errors)?`),
110 re(`-?-std=([^@\-].*)`),
111 re(`-?-stdlib=([^@\-].*)`),
112 re(`--sysroot=([^@\-].*)`),
117 var validCompilerFlagsWithNextArg
= []string{
129 var validLinkerFlags
= []*regexp
.Regexp
{
135 re(`-f(no-)?(pic|PIC|pie|PIE)`),
136 re(`-f(no-)?openmp(-simd)?`),
137 re(`-fsanitize=([^@\-].*)`),
139 re(`-headerpad_max_install_names`),
140 re(`-m(abi|arch|cpu|fpu|tune)=([^@\-].*)`),
141 re(`-mfloat-abi=([^@\-].*)`),
143 re(`-mios-simulator-version-min=(.+)`),
144 re(`-miphoneos-version-min=(.+)`),
147 re(`-(pic|PIC|pie|PIE)`),
151 re(`-?-static([-a-z0-9+]*)`),
152 re(`-?-stdlib=([^@\-].*)`),
154 // Note that any wildcards in -Wl need to exclude comma,
155 // since -Wl splits its argument at commas and passes
156 // them all to the linker uninterpreted. Allowing comma
157 // in a wildcard would allow tunnelling arbitrary additional
158 // linker arguments through one of these.
159 re(`-Wl,--(no-)?allow-multiple-definition`),
160 re(`-Wl,--(no-)?allow-shlib-undefined`),
161 re(`-Wl,--(no-)?as-needed`),
164 re(`-WL,-O([^@,\-][^,]*)?`),
166 re(`-Wl,--disable-new-dtags`),
167 re(`-Wl,-e[=,][a-zA-Z0-9]*`),
168 re(`-Wl,--enable-new-dtags`),
169 re(`-Wl,--end-group`),
170 re(`-Wl,-framework,[^,@\-][^,]+`),
171 re(`-Wl,-headerpad_max_install_names`),
172 re(`-Wl,--no-undefined`),
173 re(`-Wl,-rpath(-link)?[=,]([^,@\-][^,]+)`),
175 re(`-Wl,-search_paths_first`),
176 re(`-Wl,-sectcreate,([^,@\-][^,]+),([^,@\-][^,]+),([^,@\-][^,]+)`),
177 re(`-Wl,--start-group`),
179 re(`-Wl,-?-subsystem,(native|windows|console|posix|xbox)`),
180 re(`-Wl,-syslibroot[=,]([^,@\-][^,]+)`),
181 re(`-Wl,-undefined[=,]([^,@\-][^,]+)`),
182 re(`-Wl,-?-unresolved-symbols=[^,]+`),
183 re(`-Wl,--(no-)?warn-([^,]+)`),
184 re(`-Wl,-z,(no)?execstack`),
187 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)
188 re(`\./.*\.(a|o|obj|dll|dylib|so)`),
191 var validLinkerFlagsWithNextArg
= []string{
205 func checkCompilerFlags(name
, source
string, list
[]string) error
{
206 return checkFlags(name
, source
, list
, validCompilerFlags
, validCompilerFlagsWithNextArg
)
209 func checkLinkerFlags(name
, source
string, list
[]string) error
{
210 return checkFlags(name
, source
, list
, validLinkerFlags
, validLinkerFlagsWithNextArg
)
213 func checkFlags(name
, source
string, list
[]string, valid
[]*regexp
.Regexp
, validNext
[]string) error
{
214 // Let users override rules with $CGO_CFLAGS_ALLOW, $CGO_CFLAGS_DISALLOW, etc.
217 disallow
*regexp
.Regexp
219 if env
:= os
.Getenv("CGO_" + name
+ "_ALLOW"); env
!= "" {
220 r
, err
:= regexp
.Compile(env
)
222 return fmt
.Errorf("parsing $CGO_%s_ALLOW: %v", name
, err
)
226 if env
:= os
.Getenv("CGO_" + name
+ "_DISALLOW"); env
!= "" {
227 r
, err
:= regexp
.Compile(env
)
229 return fmt
.Errorf("parsing $CGO_%s_DISALLOW: %v", name
, err
)
235 for i
:= 0; i
< len(list
); i
++ {
237 if disallow
!= nil && disallow
.FindString(arg
) == arg
{
240 if allow
!= nil && allow
.FindString(arg
) == arg
{
243 for _
, re
:= range valid
{
244 if re
.FindString(arg
) == arg
{ // must be complete match
248 for _
, x
:= range validNext
{
250 if i
+1 < len(list
) && load
.SafeArg(list
[i
+1]) {
255 // Permit -Wl,-framework -Wl,name.
256 if i
+1 < len(list
) &&
257 strings
.HasPrefix(arg
, "-Wl,") &&
258 strings
.HasPrefix(list
[i
+1], "-Wl,") &&
259 load
.SafeArg(list
[i
+1][4:]) &&
260 !strings
.Contains(list
[i
+1][4:], ",") {
266 return fmt
.Errorf("invalid flag in %s: %s %s (see https://golang.org/s/invalidflag)", source
, arg
, list
[i
+1])
268 return fmt
.Errorf("invalid flag in %s: %s without argument (see https://golang.org/s/invalidflag)", source
, arg
)
272 return fmt
.Errorf("invalid flag in %s: %s", source
, arg
)