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