github.com/sirkon/goproxy@v1.4.8/internal/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 "github.com/sirkon/goproxy/internal/load" 34 "fmt" 35 "os" 36 "regexp" 37 "strings" 38 ) 39 40 var re = regexp.MustCompile 41 42 var validCompilerFlags = []*regexp.Regexp{ 43 re(`-D([A-Za-z_].*)`), 44 re(`-F([^@\-].*)`), 45 re(`-I([^@\-].*)`), 46 re(`-O`), 47 re(`-O([^@\-].*)`), 48 re(`-W`), 49 re(`-W([^@,]+)`), // -Wall but not -Wa,-foo. 50 re(`-Wa,-mbig-obj`), 51 re(`-Wp,-D([A-Za-z_].*)`), 52 re(`-ansi`), 53 re(`-f(no-)?asynchronous-unwind-tables`), 54 re(`-f(no-)?blocks`), 55 re(`-f(no-)builtin-[a-zA-Z0-9_]*`), 56 re(`-f(no-)?common`), 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`), 66 re(`-f(no-)?lto`), 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)`), 77 re(`-f(no-)?plt`), 78 re(`-f(no-)?rtti`), 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=(.+)`), 88 re(`-g([^@\-].*)?`), 89 re(`-m32`), 90 re(`-m64`), 91 re(`-m(abi|arch|cpu|fpu|tune)=([^@\-].*)`), 92 re(`-marm`), 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-(.+)`), 98 re(`-mmacosx-(.+)`), 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)?`), 104 re(`-mthreads`), 105 re(`-mwindows`), 106 re(`--param=ssp-buffer-size=[0-9]*`), 107 re(`-pedantic(-errors)?`), 108 re(`-pipe`), 109 re(`-pthread`), 110 re(`-?-std=([^@\-].*)`), 111 re(`-?-stdlib=([^@\-].*)`), 112 re(`--sysroot=([^@\-].*)`), 113 re(`-w`), 114 re(`-x([^@\-].*)`), 115 re(`-v`), 116 } 117 118 var validCompilerFlagsWithNextArg = []string{ 119 "-arch", 120 "-D", 121 "-I", 122 "-framework", 123 "-isysroot", 124 "-isystem", 125 "--sysroot", 126 "-target", 127 "-x", 128 } 129 130 var validLinkerFlags = []*regexp.Regexp{ 131 re(`-F([^@\-].*)`), 132 re(`-l([^@\-].*)`), 133 re(`-L([^@\-].*)`), 134 re(`-O`), 135 re(`-O([^@\-].*)`), 136 re(`-f(no-)?(pic|PIC|pie|PIE)`), 137 re(`-f(no-)?openmp(-simd)?`), 138 re(`-fsanitize=([^@\-].*)`), 139 re(`-flat_namespace`), 140 re(`-g([^@\-].*)?`), 141 re(`-headerpad_max_install_names`), 142 re(`-m(abi|arch|cpu|fpu|tune)=([^@\-].*)`), 143 re(`-mfloat-abi=([^@\-].*)`), 144 re(`-mmacosx-(.+)`), 145 re(`-mios-simulator-version-min=(.+)`), 146 re(`-miphoneos-version-min=(.+)`), 147 re(`-mthreads`), 148 re(`-mwindows`), 149 re(`-(pic|PIC|pie|PIE)`), 150 re(`-pthread`), 151 re(`-rdynamic`), 152 re(`-shared`), 153 re(`-?-static([-a-z0-9+]*)`), 154 re(`-?-stdlib=([^@\-].*)`), 155 re(`-v`), 156 157 // Note that any wildcards in -Wl need to exclude comma, 158 // since -Wl splits its argument at commas and passes 159 // them all to the linker uninterpreted. Allowing comma 160 // in a wildcard would allow tunnelling arbitrary additional 161 // linker arguments through one of these. 162 re(`-Wl,--(no-)?allow-multiple-definition`), 163 re(`-Wl,--(no-)?allow-shlib-undefined`), 164 re(`-Wl,--(no-)?as-needed`), 165 re(`-Wl,-Bdynamic`), 166 re(`-Wl,-Bstatic`), 167 re(`-WL,-O([^@,\-][^,]*)?`), 168 re(`-Wl,-d[ny]`), 169 re(`-Wl,--disable-new-dtags`), 170 re(`-Wl,-e[=,][a-zA-Z0-9]*`), 171 re(`-Wl,--enable-new-dtags`), 172 re(`-Wl,--end-group`), 173 re(`-Wl,-framework,[^,@\-][^,]+`), 174 re(`-Wl,-headerpad_max_install_names`), 175 re(`-Wl,--no-undefined`), 176 re(`-Wl,-rpath(-link)?[=,]([^,@\-][^,]+)`), 177 re(`-Wl,-s`), 178 re(`-Wl,-search_paths_first`), 179 re(`-Wl,-sectcreate,([^,@\-][^,]+),([^,@\-][^,]+),([^,@\-][^,]+)`), 180 re(`-Wl,--start-group`), 181 re(`-Wl,-?-static`), 182 re(`-Wl,-?-subsystem,(native|windows|console|posix|xbox)`), 183 re(`-Wl,-syslibroot[=,]([^,@\-][^,]+)`), 184 re(`-Wl,-undefined[=,]([^,@\-][^,]+)`), 185 re(`-Wl,-?-unresolved-symbols=[^,]+`), 186 re(`-Wl,--(no-)?warn-([^,]+)`), 187 re(`-Wl,-z,(no)?execstack`), 188 re(`-Wl,-z,relro`), 189 190 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) 191 re(`\./.*\.(a|o|obj|dll|dylib|so)`), 192 } 193 194 var validLinkerFlagsWithNextArg = []string{ 195 "-arch", 196 "-F", 197 "-l", 198 "-L", 199 "-framework", 200 "-isysroot", 201 "--sysroot", 202 "-target", 203 "-Wl,-framework", 204 "-Wl,-rpath", 205 "-Wl,-undefined", 206 } 207 208 func checkCompilerFlags(name, source string, list []string) error { 209 return checkFlags(name, source, list, validCompilerFlags, validCompilerFlagsWithNextArg) 210 } 211 212 func checkLinkerFlags(name, source string, list []string) error { 213 return checkFlags(name, source, list, validLinkerFlags, validLinkerFlagsWithNextArg) 214 } 215 216 func checkFlags(name, source string, list []string, valid []*regexp.Regexp, validNext []string) error { 217 // Let users override rules with $CGO_CFLAGS_ALLOW, $CGO_CFLAGS_DISALLOW, etc. 218 var ( 219 allow *regexp.Regexp 220 disallow *regexp.Regexp 221 ) 222 if env := os.Getenv("CGO_" + name + "_ALLOW"); env != "" { 223 r, err := regexp.Compile(env) 224 if err != nil { 225 return fmt.Errorf("parsing $CGO_%s_ALLOW: %v", name, err) 226 } 227 allow = r 228 } 229 if env := os.Getenv("CGO_" + name + "_DISALLOW"); env != "" { 230 r, err := regexp.Compile(env) 231 if err != nil { 232 return fmt.Errorf("parsing $CGO_%s_DISALLOW: %v", name, err) 233 } 234 disallow = r 235 } 236 237 Args: 238 for i := 0; i < len(list); i++ { 239 arg := list[i] 240 if disallow != nil && disallow.FindString(arg) == arg { 241 goto Bad 242 } 243 if allow != nil && allow.FindString(arg) == arg { 244 continue Args 245 } 246 for _, re := range valid { 247 if re.FindString(arg) == arg { // must be complete match 248 continue Args 249 } 250 } 251 for _, x := range validNext { 252 if arg == x { 253 if i+1 < len(list) && load.SafeArg(list[i+1]) { 254 i++ 255 continue Args 256 } 257 258 // Permit -Wl,-framework -Wl,name. 259 if i+1 < len(list) && 260 strings.HasPrefix(arg, "-Wl,") && 261 strings.HasPrefix(list[i+1], "-Wl,") && 262 load.SafeArg(list[i+1][4:]) && 263 !strings.Contains(list[i+1][4:], ",") { 264 i++ 265 continue Args 266 } 267 268 if i+1 < len(list) { 269 return fmt.Errorf("invalid flag in %s: %s %s (see https://golang.org/s/invalidflag)", source, arg, list[i+1]) 270 } 271 return fmt.Errorf("invalid flag in %s: %s without argument (see https://golang.org/s/invalidflag)", source, arg) 272 } 273 } 274 Bad: 275 return fmt.Errorf("invalid flag in %s: %s", source, arg) 276 } 277 return nil 278 }