github.com/corona10/go@v0.0.0-20180224231303-7a218942be57/src/cmd/go/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 "cmd/go/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(`-I([^@\-].*)`), 45 re(`-O`), 46 re(`-O([^@\-].*)`), 47 re(`-W`), 48 re(`-W([^@,]+)`), // -Wall but not -Wa,-foo. 49 re(`-f(no-)?blocks`), 50 re(`-f(no-)?common`), 51 re(`-f(no-)?constant-cfstrings`), 52 re(`-f(no-)?exceptions`), 53 re(`-finput-charset=([^@\-].*)`), 54 re(`-f(no-)?lto`), 55 re(`-f(no-)?modules`), 56 re(`-f(no-)?objc-arc`), 57 re(`-f(no-)?omit-frame-pointer`), 58 re(`-f(no-)?openmp(-simd)?`), 59 re(`-f(no-)?permissive`), 60 re(`-f(no-)?(pic|PIC|pie|PIE)`), 61 re(`-f(no-)?rtti`), 62 re(`-f(no-)?split-stack`), 63 re(`-f(no-)?stack-(.+)`), 64 re(`-f(no-)?strict-aliasing`), 65 re(`-fsanitize=(.+)`), 66 re(`-g([^@\-].*)?`), 67 re(`-m(arch|cpu|fpu|tune)=([^@\-].*)`), 68 re(`-m(no-)?avx[0-9a-z.]*`), 69 re(`-m(no-)?ms-bitfields`), 70 re(`-m(no-)?stack-(.+)`), 71 re(`-mmacosx-(.+)`), 72 re(`-mnop-fun-dllimport`), 73 re(`-m(no-)?sse[0-9.]*`), 74 re(`-pedantic(-errors)?`), 75 re(`-pipe`), 76 re(`-pthread`), 77 re(`-?-std=([^@\-].*)`), 78 re(`-x([^@\-].*)`), 79 } 80 81 var validCompilerFlagsWithNextArg = []string{ 82 "-D", 83 "-I", 84 "-isystem", 85 "-framework", 86 "-x", 87 } 88 89 var validLinkerFlags = []*regexp.Regexp{ 90 re(`-F([^@\-].*)`), 91 re(`-l([^@\-].*)`), 92 re(`-L([^@\-].*)`), 93 re(`-f(no-)?(pic|PIC|pie|PIE)`), 94 re(`-fsanitize=([^@\-].*)`), 95 re(`-g([^@\-].*)?`), 96 re(`-m(arch|cpu|fpu|tune)=([^@\-].*)`), 97 re(`-(pic|PIC|pie|PIE)`), 98 re(`-pthread`), 99 re(`-?-static([-a-z0-9+]*)`), 100 101 // Note that any wildcards in -Wl need to exclude comma, 102 // since -Wl splits its argument at commas and passes 103 // them all to the linker uninterpreted. Allowing comma 104 // in a wildcard would allow tunnelling arbitrary additional 105 // linker arguments through one of these. 106 re(`-Wl,--(no-)?as-needed`), 107 re(`-Wl,-Bdynamic`), 108 re(`-Wl,-Bstatic`), 109 re(`-Wl,--disable-new-dtags`), 110 re(`-Wl,--enable-new-dtags`), 111 re(`-Wl,--end-group`), 112 re(`-Wl,-framework,[^,@\-][^,]+`), 113 re(`-Wl,-headerpad_max_install_names`), 114 re(`-Wl,--no-undefined`), 115 re(`-Wl,-rpath,([^,@\-][^,]+)`), 116 re(`-Wl,-search_paths_first`), 117 re(`-Wl,--start-group`), 118 re(`-Wl,-?-unresolved-symbols=[^,]+`), 119 re(`-Wl,--(no-)?warn-([^,]+)`), 120 121 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) 122 } 123 124 var validLinkerFlagsWithNextArg = []string{ 125 "-F", 126 "-l", 127 "-L", 128 "-framework", 129 "-Wl,-framework", 130 } 131 132 func checkCompilerFlags(name, source string, list []string) error { 133 return checkFlags(name, source, list, validCompilerFlags, validCompilerFlagsWithNextArg) 134 } 135 136 func checkLinkerFlags(name, source string, list []string) error { 137 return checkFlags(name, source, list, validLinkerFlags, validLinkerFlagsWithNextArg) 138 } 139 140 func checkFlags(name, source string, list []string, valid []*regexp.Regexp, validNext []string) error { 141 // Let users override rules with $CGO_CFLAGS_ALLOW, $CGO_CFLAGS_DISALLOW, etc. 142 var ( 143 allow *regexp.Regexp 144 disallow *regexp.Regexp 145 ) 146 if env := os.Getenv("CGO_" + name + "_ALLOW"); env != "" { 147 r, err := regexp.Compile(env) 148 if err != nil { 149 return fmt.Errorf("parsing $CGO_%s_ALLOW: %v", name, err) 150 } 151 allow = r 152 } 153 if env := os.Getenv("CGO_" + name + "_DISALLOW"); env != "" { 154 r, err := regexp.Compile(env) 155 if err != nil { 156 return fmt.Errorf("parsing $CGO_%s_DISALLOW: %v", name, err) 157 } 158 disallow = r 159 } 160 161 Args: 162 for i := 0; i < len(list); i++ { 163 arg := list[i] 164 if disallow != nil && disallow.FindString(arg) == arg { 165 goto Bad 166 } 167 if allow != nil && allow.FindString(arg) == arg { 168 continue Args 169 } 170 for _, re := range valid { 171 if re.FindString(arg) == arg { // must be complete match 172 continue Args 173 } 174 } 175 for _, x := range validNext { 176 if arg == x { 177 if i+1 < len(list) && load.SafeArg(list[i+1]) { 178 i++ 179 continue Args 180 } 181 182 // Permit -Wl,-framework -Wl,name. 183 if i+1 < len(list) && 184 strings.HasPrefix(arg, "-Wl,") && 185 strings.HasPrefix(list[i+1], "-Wl,") && 186 load.SafeArg(list[i+1][4:]) && 187 !strings.Contains(list[i+1][4:], ",") { 188 i++ 189 continue Args 190 } 191 192 if i+1 < len(list) { 193 return fmt.Errorf("invalid flag in %s: %s %s (see https://golang.org/s/invalidflag)", source, arg, list[i+1]) 194 } 195 return fmt.Errorf("invalid flag in %s: %s without argument (see https://golang.org/s/invalidflag)", source, arg) 196 } 197 } 198 Bad: 199 return fmt.Errorf("invalid flag in %s: %s", source, arg) 200 } 201 return nil 202 }