security.go raw
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.
4
5 // This file has been copied from the Go 1.13 release tree.
6
7 // Checking of compiler and linker flags.
8 // We must avoid flags like -fplugin=, which can allow
9 // arbitrary code execution during the build.
10 // Do not make changes here without carefully
11 // considering the implications.
12 // (That's why the code is isolated in a file named security.go.)
13 //
14 // Note that -Wl,foo means split foo on commas and pass to
15 // the linker, so that -Wl,-foo,bar means pass -foo bar to
16 // the linker. Similarly -Wa,foo for the assembler and so on.
17 // If any of these are permitted, the wildcard portion must
18 // disallow commas.
19 //
20 // Note also that GNU binutils accept any argument @foo
21 // as meaning "read more flags from the file foo", so we must
22 // guard against any command-line argument beginning with @,
23 // even things like "-I @foo".
24 // We use safeArg (which is even more conservative)
25 // to reject these.
26 //
27 // Even worse, gcc -I@foo (one arg) turns into cc1 -I @foo (two args),
28 // so although gcc doesn't expand the @foo, cc1 will.
29 // So out of paranoia, we reject @ at the beginning of every
30 // flag argument that might be split into its own argument.
31
32 package cgo
33
34 import (
35 "fmt"
36 "os"
37 "regexp"
38 "strings"
39 "unicode/utf8"
40 )
41
42 var re = regexp.MustCompile
43
44 var validCompilerFlags = []*regexp.Regexp{
45 re(`-D([A-Za-z_].*)`),
46 re(`-F([^@\-].*)`),
47 re(`-I([^@\-].*)`),
48 re(`-O`),
49 re(`-O([^@\-].*)`),
50 re(`-W`),
51 re(`-W([^@,]+)`), // -Wall but not -Wa,-foo.
52 re(`-Wa,-mbig-obj`),
53 re(`-Wp,-D([A-Za-z_].*)`),
54 re(`-ansi`),
55 re(`-f(no-)?asynchronous-unwind-tables`),
56 re(`-f(no-)?blocks`),
57 re(`-f(no-)builtin-[a-zA-Z0-9_]*`),
58 re(`-f(no-)?common`),
59 re(`-f(no-)?constant-cfstrings`),
60 re(`-fdiagnostics-show-note-include-stack`),
61 re(`-f(no-)?eliminate-unused-debug-types`),
62 re(`-f(no-)?exceptions`),
63 re(`-f(no-)?fast-math`),
64 re(`-f(no-)?inline-functions`),
65 re(`-finput-charset=([^@\-].*)`),
66 re(`-f(no-)?fat-lto-objects`),
67 re(`-f(no-)?keep-inline-dllexport`),
68 re(`-f(no-)?lto`),
69 re(`-fmacro-backtrace-limit=(.+)`),
70 re(`-fmessage-length=(.+)`),
71 re(`-f(no-)?modules`),
72 re(`-f(no-)?objc-arc`),
73 re(`-f(no-)?objc-nonfragile-abi`),
74 re(`-f(no-)?objc-legacy-dispatch`),
75 re(`-f(no-)?omit-frame-pointer`),
76 re(`-f(no-)?openmp(-simd)?`),
77 re(`-f(no-)?permissive`),
78 re(`-f(no-)?(pic|PIC|pie|PIE)`),
79 re(`-f(no-)?plt`),
80 re(`-f(no-)?rtti`),
81 re(`-f(no-)?split-stack`),
82 re(`-f(no-)?stack-(.+)`),
83 re(`-f(no-)?strict-aliasing`),
84 re(`-f(un)signed-char`),
85 re(`-f(no-)?use-linker-plugin`), // safe if -B is not used; we don't permit -B
86 re(`-f(no-)?visibility-inlines-hidden`),
87 re(`-fsanitize=(.+)`),
88 re(`-ftemplate-depth-(.+)`),
89 re(`-fvisibility=(.+)`),
90 re(`-g([^@\-].*)?`),
91 re(`-m32`),
92 re(`-m64`),
93 re(`-m(abi|arch|cpu|fpu|tune)=([^@\-].*)`),
94 re(`-m(no-)?v?aes`),
95 re(`-marm`),
96 re(`-m(no-)?avx[0-9a-z]*`),
97 re(`-mfloat-abi=([^@\-].*)`),
98 re(`-mfpmath=[0-9a-z,+]*`),
99 re(`-m(no-)?avx[0-9a-z.]*`),
100 re(`-m(no-)?ms-bitfields`),
101 re(`-m(no-)?stack-(.+)`),
102 re(`-mmacosx-(.+)`),
103 re(`-mios-simulator-version-min=(.+)`),
104 re(`-miphoneos-version-min=(.+)`),
105 re(`-mtvos-simulator-version-min=(.+)`),
106 re(`-mtvos-version-min=(.+)`),
107 re(`-mwatchos-simulator-version-min=(.+)`),
108 re(`-mwatchos-version-min=(.+)`),
109 re(`-mnop-fun-dllimport`),
110 re(`-m(no-)?sse[0-9.]*`),
111 re(`-m(no-)?ssse3`),
112 re(`-mthumb(-interwork)?`),
113 re(`-mthreads`),
114 re(`-mwindows`),
115 re(`--param=ssp-buffer-size=[0-9]*`),
116 re(`-pedantic(-errors)?`),
117 re(`-pipe`),
118 re(`-pthread`),
119 re(`-?-std=([^@\-].*)`),
120 re(`-?-stdlib=([^@\-].*)`),
121 re(`--sysroot=([^@\-].*)`),
122 re(`-w`),
123 re(`-x([^@\-].*)`),
124 re(`-v`),
125 }
126
127 var validCompilerFlagsWithNextArg = []string{
128 "-arch",
129 "-D",
130 "-I",
131 "-framework",
132 "-isysroot",
133 "-isystem",
134 "--sysroot",
135 "-target",
136 "-x",
137 }
138
139 var validLinkerFlags = []*regexp.Regexp{
140 re(`-F([^@\-].*)`),
141 re(`-l([^@\-].*)`),
142 re(`-L([^@\-].*)`),
143 re(`-O`),
144 re(`-O([^@\-].*)`),
145 re(`--export=([^@\-].*)`),
146 re(`-f(no-)?(pic|PIC|pie|PIE)`),
147 re(`-f(no-)?openmp(-simd)?`),
148 re(`-fsanitize=([^@\-].*)`),
149 re(`-flat_namespace`),
150 re(`-g([^@\-].*)?`),
151 re(`-headerpad_max_install_names`),
152 re(`-m(abi|arch|cpu|fpu|tune)=([^@\-].*)`),
153 re(`-mfloat-abi=([^@\-].*)`),
154 re(`-mmacosx-(.+)`),
155 re(`-mios-simulator-version-min=(.+)`),
156 re(`-miphoneos-version-min=(.+)`),
157 re(`-mthreads`),
158 re(`-mwindows`),
159 re(`-(pic|PIC|pie|PIE)`),
160 re(`-pthread`),
161 re(`-rdynamic`),
162 re(`-shared`),
163 re(`-?-static([-a-z0-9+]*)`),
164 re(`-?-stdlib=([^@\-].*)`),
165 re(`-v`),
166
167 // Note that any wildcards in -Wl need to exclude comma,
168 // since -Wl splits its argument at commas and passes
169 // them all to the linker uninterpreted. Allowing comma
170 // in a wildcard would allow tunnelling arbitrary additional
171 // linker arguments through one of these.
172 re(`-Wl,--(no-)?allow-multiple-definition`),
173 re(`-Wl,--(no-)?allow-shlib-undefined`),
174 re(`-Wl,--(no-)?as-needed`),
175 re(`-Wl,-Bdynamic`),
176 re(`-Wl,-berok`),
177 re(`-Wl,-Bstatic`),
178 re(`-WL,-O([^@,\-][^,]*)?`),
179 re(`-Wl,-d[ny]`),
180 re(`-Wl,--disable-new-dtags`),
181 re(`-Wl,-e[=,][a-zA-Z0-9]*`),
182 re(`-Wl,--enable-new-dtags`),
183 re(`-Wl,--end-group`),
184 re(`-Wl,--(no-)?export-dynamic`),
185 re(`-Wl,-framework,[^,@\-][^,]+`),
186 re(`-Wl,-headerpad_max_install_names`),
187 re(`-Wl,--no-undefined`),
188 re(`-Wl,-R([^@\-][^,@]*$)`),
189 re(`-Wl,--just-symbols[=,]([^,@\-][^,@]+)`),
190 re(`-Wl,-rpath(-link)?[=,]([^,@\-][^,]+)`),
191 re(`-Wl,-s`),
192 re(`-Wl,-search_paths_first`),
193 re(`-Wl,-sectcreate,([^,@\-][^,]+),([^,@\-][^,]+),([^,@\-][^,]+)`),
194 re(`-Wl,--start-group`),
195 re(`-Wl,-?-static`),
196 re(`-Wl,-?-subsystem,(native|windows|console|posix|xbox)`),
197 re(`-Wl,-syslibroot[=,]([^,@\-][^,]+)`),
198 re(`-Wl,-undefined[=,]([^,@\-][^,]+)`),
199 re(`-Wl,-?-unresolved-symbols=[^,]+`),
200 re(`-Wl,--(no-)?warn-([^,]+)`),
201 re(`-Wl,-z,(no)?execstack`),
202 re(`-Wl,-z,relro`),
203
204 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)
205 re(`\./.*\.(a|o|obj|dll|dylib|so)`),
206 }
207
208 var validLinkerFlagsWithNextArg = []string{
209 "-arch",
210 "-F",
211 "-l",
212 "-L",
213 "-framework",
214 "-isysroot",
215 "--sysroot",
216 "-target",
217 "-Wl,-framework",
218 "-Wl,-rpath",
219 "-Wl,-R",
220 "-Wl,--just-symbols",
221 "-Wl,-undefined",
222 }
223
224 func checkCompilerFlags(name string, list []string) error {
225 return checkFlags(name, list, validCompilerFlags, validCompilerFlagsWithNextArg)
226 }
227
228 func checkLinkerFlags(name string, list []string) error {
229 return checkFlags(name, list, validLinkerFlags, validLinkerFlagsWithNextArg)
230 }
231
232 func checkFlags(name string, list []string, valid []*regexp.Regexp, validNext []string) error {
233 // Let users override rules with $CGO_CFLAGS_ALLOW, $CGO_CFLAGS_DISALLOW, etc.
234 var (
235 allow *regexp.Regexp
236 disallow *regexp.Regexp
237 )
238 if env := os.Getenv("CGO_" + name + "_ALLOW"); env != "" {
239 r, err := regexp.Compile(env)
240 if err != nil {
241 return fmt.Errorf("parsing $CGO_%s_ALLOW: %v", name, err)
242 }
243 allow = r
244 }
245 if env := os.Getenv("CGO_" + name + "_DISALLOW"); env != "" {
246 r, err := regexp.Compile(env)
247 if err != nil {
248 return fmt.Errorf("parsing $CGO_%s_DISALLOW: %v", name, err)
249 }
250 disallow = r
251 }
252
253 Args:
254 for i := 0; i < len(list); i++ {
255 arg := list[i]
256 if disallow != nil && disallow.FindString(arg) == arg {
257 goto Bad
258 }
259 if allow != nil && allow.FindString(arg) == arg {
260 continue Args
261 }
262 for _, re := range valid {
263 if re.FindString(arg) == arg { // must be complete match
264 continue Args
265 }
266 }
267 for _, x := range validNext {
268 if arg == x {
269 if i+1 < len(list) && safeArg(list[i+1]) {
270 i++
271 continue Args
272 }
273
274 // Permit -Wl,-framework -Wl,name.
275 if i+1 < len(list) &&
276 strings.HasPrefix(arg, "-Wl,") &&
277 strings.HasPrefix(list[i+1], "-Wl,") &&
278 safeArg(list[i+1][4:]) &&
279 !strings.Contains(list[i+1][4:], ",") {
280 i++
281 continue Args
282 }
283
284 if i+1 < len(list) {
285 return fmt.Errorf("invalid flag: %s %s (see https://golang.org/s/invalidflag)", arg, list[i+1])
286 }
287 return fmt.Errorf("invalid flag: %s without argument (see https://golang.org/s/invalidflag)", arg)
288 }
289 }
290 Bad:
291 return fmt.Errorf("invalid flag: %s", arg)
292 }
293 return nil
294 }
295
296 func safeArg(name string) bool {
297 if name == "" {
298 return false
299 }
300 c := name[0]
301 return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '.' || c == '_' || c == '/' || c >= utf8.RuneSelf
302 }
303