github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/cgo/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 // 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 }