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// 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.) 11// 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 16// disallow commas. 17// 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) 23// to reject these. 24// 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. 29 30package work 31 32import ( 33 "fmt" 34 "internal/lazyregexp" 35 "regexp" 36 "strings" 37 38 "cmd/go/internal/cfg" 39 "cmd/go/internal/load" 40) 41 42var re = lazyregexp.New 43 44var validCompilerFlags = []*lazyregexp.Regexp{ 45 re(`-D([A-Za-z_][A-Za-z0-9_]*)(=[^@\-]*)?`), 46 re(`-U([A-Za-z_][A-Za-z0-9_]*)`), 47 re(`-F([^@\-].*)`), 48 re(`-I([^@\-].*)`), 49 re(`-O`), 50 re(`-O([^@\-].*)`), 51 re(`-W`), 52 re(`-W([^@,]+)`), // -Wall but not -Wa,-foo. 53 re(`-Wa,-mbig-obj`), 54 re(`-Wp,-D([A-Za-z_][A-Za-z0-9_]*)(=[^@,\-]*)?`), 55 re(`-Wp,-U([A-Za-z_][A-Za-z0-9_]*)`), 56 re(`-ansi`), 57 re(`-f(no-)?asynchronous-unwind-tables`), 58 re(`-f(no-)?blocks`), 59 re(`-f(no-)builtin-[a-zA-Z0-9_]*`), 60 re(`-f(no-)?common`), 61 re(`-f(no-)?constant-cfstrings`), 62 re(`-fdebug-prefix-map=([^@]+)=([^@]+)`), 63 re(`-fdiagnostics-show-note-include-stack`), 64 re(`-ffile-prefix-map=([^@]+)=([^@]+)`), 65 re(`-fno-canonical-system-headers`), 66 re(`-f(no-)?eliminate-unused-debug-types`), 67 re(`-f(no-)?exceptions`), 68 re(`-f(no-)?fast-math`), 69 re(`-f(no-)?inline-functions`), 70 re(`-finput-charset=([^@\-].*)`), 71 re(`-f(no-)?fat-lto-objects`), 72 re(`-f(no-)?keep-inline-dllexport`), 73 re(`-f(no-)?lto`), 74 re(`-fmacro-backtrace-limit=(.+)`), 75 re(`-fmessage-length=(.+)`), 76 re(`-f(no-)?modules`), 77 re(`-f(no-)?objc-arc`), 78 re(`-f(no-)?objc-nonfragile-abi`), 79 re(`-f(no-)?objc-legacy-dispatch`), 80 re(`-f(no-)?omit-frame-pointer`), 81 re(`-f(no-)?openmp(-simd)?`), 82 re(`-f(no-)?permissive`), 83 re(`-f(no-)?(pic|PIC|pie|PIE)`), 84 re(`-f(no-)?plt`), 85 re(`-f(no-)?rtti`), 86 re(`-f(no-)?split-stack`), 87 re(`-f(no-)?stack-(.+)`), 88 re(`-f(no-)?strict-aliasing`), 89 re(`-f(un)signed-char`), 90 re(`-f(no-)?use-linker-plugin`), // safe if -B is not used; we don't permit -B 91 re(`-f(no-)?visibility-inlines-hidden`), 92 re(`-fsanitize=(.+)`), 93 re(`-ftemplate-depth-(.+)`), 94 re(`-fvisibility=(.+)`), 95 re(`-g([^@\-].*)?`), 96 re(`-m32`), 97 re(`-m64`), 98 re(`-m(abi|arch|cpu|fpu|tune)=([^@\-].*)`), 99 re(`-m(no-)?v?aes`), 100 re(`-marm`), 101 re(`-m(no-)?avx[0-9a-z]*`), 102 re(`-mcmodel=[0-9a-z-]+`), 103 re(`-mfloat-abi=([^@\-].*)`), 104 re(`-mfpmath=[0-9a-z,+]*`), 105 re(`-m(no-)?avx[0-9a-z.]*`), 106 re(`-m(no-)?ms-bitfields`), 107 re(`-m(no-)?stack-(.+)`), 108 re(`-mmacosx-(.+)`), 109 re(`-mios-simulator-version-min=(.+)`), 110 re(`-miphoneos-version-min=(.+)`), 111 re(`-mlarge-data-threshold=[0-9]+`), 112 re(`-mtvos-simulator-version-min=(.+)`), 113 re(`-mtvos-version-min=(.+)`), 114 re(`-mwatchos-simulator-version-min=(.+)`), 115 re(`-mwatchos-version-min=(.+)`), 116 re(`-mnop-fun-dllimport`), 117 re(`-m(no-)?sse[0-9.]*`), 118 re(`-m(no-)?ssse3`), 119 re(`-mthumb(-interwork)?`), 120 re(`-mthreads`), 121 re(`-mwindows`), 122 re(`-no-canonical-prefixes`), 123 re(`--param=ssp-buffer-size=[0-9]*`), 124 re(`-pedantic(-errors)?`), 125 re(`-pipe`), 126 re(`-pthread`), 127 re(`-?-std=([^@\-].*)`), 128 re(`-?-stdlib=([^@\-].*)`), 129 re(`--sysroot=([^@\-].*)`), 130 re(`-w`), 131 re(`-x([^@\-].*)`), 132 re(`-v`), 133} 134 135var validCompilerFlagsWithNextArg = []string{ 136 "-arch", 137 "-D", 138 "-U", 139 "-I", 140 "-F", 141 "-framework", 142 "-include", 143 "-isysroot", 144 "-isystem", 145 "--sysroot", 146 "-target", 147 "-x", 148} 149 150var invalidLinkerFlags = []*lazyregexp.Regexp{ 151 // On macOS this means the linker loads and executes the next argument. 152 // Have to exclude separately because -lfoo is allowed in general. 153 re(`-lto_library`), 154} 155 156var validLinkerFlags = []*lazyregexp.Regexp{ 157 re(`-F([^@\-].*)`), 158 re(`-l([^@\-].*)`), 159 re(`-L([^@\-].*)`), 160 re(`-O`), 161 re(`-O([^@\-].*)`), 162 re(`-f(no-)?(pic|PIC|pie|PIE)`), 163 re(`-f(no-)?openmp(-simd)?`), 164 re(`-fsanitize=([^@\-].*)`), 165 re(`-flat_namespace`), 166 re(`-g([^@\-].*)?`), 167 re(`-headerpad_max_install_names`), 168 re(`-m(abi|arch|cpu|fpu|tune)=([^@\-].*)`), 169 re(`-mfloat-abi=([^@\-].*)`), 170 re(`-mmacosx-(.+)`), 171 re(`-mios-simulator-version-min=(.+)`), 172 re(`-miphoneos-version-min=(.+)`), 173 re(`-mthreads`), 174 re(`-mwindows`), 175 re(`-(pic|PIC|pie|PIE)`), 176 re(`-pthread`), 177 re(`-rdynamic`), 178 re(`-shared`), 179 re(`-?-static([-a-z0-9+]*)`), 180 re(`-?-stdlib=([^@\-].*)`), 181 re(`-v`), 182 183 // Note that any wildcards in -Wl need to exclude comma, 184 // since -Wl splits its argument at commas and passes 185 // them all to the linker uninterpreted. Allowing comma 186 // in a wildcard would allow tunneling arbitrary additional 187 // linker arguments through one of these. 188 re(`-Wl,--(no-)?allow-multiple-definition`), 189 re(`-Wl,--(no-)?allow-shlib-undefined`), 190 re(`-Wl,--(no-)?as-needed`), 191 re(`-Wl,-Bdynamic`), 192 re(`-Wl,-berok`), 193 re(`-Wl,-Bstatic`), 194 re(`-Wl,-Bsymbolic-functions`), 195 re(`-Wl,-O[0-9]+`), 196 re(`-Wl,-d[ny]`), 197 re(`-Wl,--disable-new-dtags`), 198 re(`-Wl,-e[=,][a-zA-Z0-9]+`), 199 re(`-Wl,--enable-new-dtags`), 200 re(`-Wl,--end-group`), 201 re(`-Wl,--(no-)?export-dynamic`), 202 re(`-Wl,-E`), 203 re(`-Wl,-framework,[^,@\-][^,]+`), 204 re(`-Wl,--hash-style=(sysv|gnu|both)`), 205 re(`-Wl,-headerpad_max_install_names`), 206 re(`-Wl,--no-undefined`), 207 re(`-Wl,-R,?([^@\-,][^,@]*$)`), 208 re(`-Wl,--just-symbols[=,]([^,@\-][^,@]+)`), 209 re(`-Wl,-rpath(-link)?[=,]([^,@\-][^,]+)`), 210 re(`-Wl,-s`), 211 re(`-Wl,-search_paths_first`), 212 re(`-Wl,-sectcreate,([^,@\-][^,]+),([^,@\-][^,]+),([^,@\-][^,]+)`), 213 re(`-Wl,--start-group`), 214 re(`-Wl,-?-static`), 215 re(`-Wl,-?-subsystem,(native|windows|console|posix|xbox)`), 216 re(`-Wl,-syslibroot[=,]([^,@\-][^,]+)`), 217 re(`-Wl,-undefined[=,]([^,@\-][^,]+)`), 218 re(`-Wl,-?-unresolved-symbols=[^,]+`), 219 re(`-Wl,--(no-)?warn-([^,]+)`), 220 re(`-Wl,-?-wrap[=,][^,@\-][^,]*`), 221 re(`-Wl(,-z,(relro|now|(no)?execstack))+`), 222 223 re(`[a-zA-Z0-9_/].*\.(a|o|obj|dll|dylib|so|tbd)`), // direct linker inputs: x.o or libfoo.so (but not -foo.o or @foo.o) 224 re(`\./.*\.(a|o|obj|dll|dylib|so|tbd)`), 225} 226 227var validLinkerFlagsWithNextArg = []string{ 228 "-arch", 229 "-F", 230 "-l", 231 "-L", 232 "-framework", 233 "-isysroot", 234 "--sysroot", 235 "-target", 236 "-Wl,-framework", 237 "-Wl,-rpath", 238 "-Wl,-R", 239 "-Wl,--just-symbols", 240 "-Wl,-undefined", 241} 242 243func checkCompilerFlags(name, source string, list []string) error { 244 checkOverrides := true 245 return checkFlags(name, source, list, nil, validCompilerFlags, validCompilerFlagsWithNextArg, checkOverrides) 246} 247 248func checkLinkerFlags(name, source string, list []string) error { 249 checkOverrides := true 250 return checkFlags(name, source, list, invalidLinkerFlags, validLinkerFlags, validLinkerFlagsWithNextArg, checkOverrides) 251} 252 253// checkCompilerFlagsForInternalLink returns an error if 'list' 254// contains a flag or flags that may not be fully supported by 255// internal linking (meaning that we should punt the link to the 256// external linker). 257func checkCompilerFlagsForInternalLink(name, source string, list []string) error { 258 checkOverrides := false 259 if err := checkFlags(name, source, list, nil, validCompilerFlags, validCompilerFlagsWithNextArg, checkOverrides); err != nil { 260 return err 261 } 262 // Currently the only flag on the allow list that causes problems 263 // for the linker is "-flto"; check for it manually here. 264 for _, fl := range list { 265 if strings.HasPrefix(fl, "-flto") { 266 return fmt.Errorf("flag %q triggers external linking", fl) 267 } 268 } 269 return nil 270} 271 272func checkFlags(name, source string, list []string, invalid, valid []*lazyregexp.Regexp, validNext []string, checkOverrides bool) error { 273 // Let users override rules with $CGO_CFLAGS_ALLOW, $CGO_CFLAGS_DISALLOW, etc. 274 var ( 275 allow *regexp.Regexp 276 disallow *regexp.Regexp 277 ) 278 if checkOverrides { 279 if env := cfg.Getenv("CGO_" + name + "_ALLOW"); env != "" { 280 r, err := regexp.Compile(env) 281 if err != nil { 282 return fmt.Errorf("parsing $CGO_%s_ALLOW: %v", name, err) 283 } 284 allow = r 285 } 286 if env := cfg.Getenv("CGO_" + name + "_DISALLOW"); env != "" { 287 r, err := regexp.Compile(env) 288 if err != nil { 289 return fmt.Errorf("parsing $CGO_%s_DISALLOW: %v", name, err) 290 } 291 disallow = r 292 } 293 } 294 295Args: 296 for i := 0; i < len(list); i++ { 297 arg := list[i] 298 if disallow != nil && disallow.FindString(arg) == arg { 299 goto Bad 300 } 301 if allow != nil && allow.FindString(arg) == arg { 302 continue Args 303 } 304 for _, re := range invalid { 305 if re.FindString(arg) == arg { // must be complete match 306 goto Bad 307 } 308 } 309 for _, re := range valid { 310 if re.FindString(arg) == arg { // must be complete match 311 continue Args 312 } 313 } 314 for _, x := range validNext { 315 if arg == x { 316 if i+1 < len(list) && load.SafeArg(list[i+1]) { 317 i++ 318 continue Args 319 } 320 321 // Permit -Wl,-framework -Wl,name. 322 if i+1 < len(list) && 323 strings.HasPrefix(arg, "-Wl,") && 324 strings.HasPrefix(list[i+1], "-Wl,") && 325 load.SafeArg(list[i+1][4:]) && 326 !strings.Contains(list[i+1][4:], ",") { 327 i++ 328 continue Args 329 } 330 331 // Permit -I= /path, -I $SYSROOT. 332 if i+1 < len(list) && arg == "-I" { 333 if (strings.HasPrefix(list[i+1], "=") || strings.HasPrefix(list[i+1], "$SYSROOT")) && 334 load.SafeArg(list[i+1][1:]) { 335 i++ 336 continue Args 337 } 338 } 339 340 if i+1 < len(list) { 341 return fmt.Errorf("invalid flag in %s: %s %s (see https://golang.org/s/invalidflag)", source, arg, list[i+1]) 342 } 343 return fmt.Errorf("invalid flag in %s: %s without argument (see https://golang.org/s/invalidflag)", source, arg) 344 } 345 } 346 Bad: 347 return fmt.Errorf("invalid flag in %s: %s", source, arg) 348 } 349 return nil 350} 351