github.com/zebozhuang/go@v0.0.0-20200207033046-f8a98f6f5c5d/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 ) 38 39 var re = regexp.MustCompile 40 41 var validCompilerFlags = []*regexp.Regexp{ 42 re(`-D([A-Za-z_].*)`), 43 re(`-I([^@\-].*)`), 44 re(`-O`), 45 re(`-O([^@\-].*)`), 46 re(`-W`), 47 re(`-W([^@,]+)`), // -Wall but not -Wa,-foo. 48 re(`-f(no-)?objc-arc`), 49 re(`-f(no-)?omit-frame-pointer`), 50 re(`-f(no-)?(pic|PIC|pie|PIE)`), 51 re(`-f(no-)?split-stack`), 52 re(`-f(no-)?stack-(.+)`), 53 re(`-f(no-)?strict-aliasing`), 54 re(`-fsanitize=(.+)`), 55 re(`-g([^@\-].*)?`), 56 re(`-m(arch|cpu|fpu|tune)=([^@\-].*)`), 57 re(`-m(no-)?stack-(.+)`), 58 re(`-mmacosx-(.+)`), 59 re(`-mnop-fun-dllimport`), 60 re(`-pthread`), 61 re(`-std=([^@\-].*)`), 62 re(`-x([^@\-].*)`), 63 } 64 65 var validCompilerFlagsWithNextArg = []string{ 66 "-D", 67 "-I", 68 "-framework", 69 "-x", 70 } 71 72 var validLinkerFlags = []*regexp.Regexp{ 73 re(`-F([^@\-].*)`), 74 re(`-l([^@\-].*)`), 75 re(`-L([^@\-].*)`), 76 re(`-f(no-)?(pic|PIC|pie|PIE)`), 77 re(`-fsanitize=([^@\-].*)`), 78 re(`-g([^@\-].*)?`), 79 re(`-m(arch|cpu|fpu|tune)=([^@\-].*)`), 80 re(`-(pic|PIC|pie|PIE)`), 81 re(`-pthread`), 82 83 // Note that any wildcards in -Wl need to exclude comma, 84 // since -Wl splits its argument at commas and passes 85 // them all to the linker uninterpreted. Allowing comma 86 // in a wildcard would allow tunnelling arbitrary additional 87 // linker arguments through one of these. 88 re(`-Wl,-rpath,([^,@\-][^,]+)`), 89 re(`-Wl,--(no-)?warn-([^,]+)`), 90 91 re(`[a-zA-Z0-9_].*\.(o|obj|dll|dylib|so)`), // direct linker inputs: x.o or libfoo.so (but not -foo.o or @foo.o) 92 } 93 94 var validLinkerFlagsWithNextArg = []string{ 95 "-F", 96 "-l", 97 "-L", 98 "-framework", 99 } 100 101 func checkCompilerFlags(name, source string, list []string) error { 102 return checkFlags(name, source, list, validCompilerFlags, validCompilerFlagsWithNextArg) 103 } 104 105 func checkLinkerFlags(name, source string, list []string) error { 106 return checkFlags(name, source, list, validLinkerFlags, validLinkerFlagsWithNextArg) 107 } 108 109 func checkFlags(name, source string, list []string, valid []*regexp.Regexp, validNext []string) error { 110 // Let users override rules with $CGO_CFLAGS_ALLOW, $CGO_CFLAGS_DISALLOW, etc. 111 var ( 112 allow *regexp.Regexp 113 disallow *regexp.Regexp 114 ) 115 if env := os.Getenv("CGO_" + name + "_ALLOW"); env != "" { 116 r, err := regexp.Compile(env) 117 if err != nil { 118 return fmt.Errorf("parsing $CGO_%s_ALLOW: %v", name, err) 119 } 120 allow = r 121 } 122 if env := os.Getenv("CGO_" + name + "_DISALLOW"); env != "" { 123 r, err := regexp.Compile(env) 124 if err != nil { 125 return fmt.Errorf("parsing $CGO_%s_DISALLOW: %v", name, err) 126 } 127 disallow = r 128 } 129 130 Args: 131 for i := 0; i < len(list); i++ { 132 arg := list[i] 133 if disallow != nil && disallow.FindString(arg) == arg { 134 goto Bad 135 } 136 if allow != nil && allow.FindString(arg) == arg { 137 continue Args 138 } 139 for _, re := range valid { 140 if re.FindString(arg) == arg { // must be complete match 141 continue Args 142 } 143 } 144 for _, x := range validNext { 145 if arg == x { 146 if i+1 < len(list) && load.SafeArg(list[i+1]) { 147 i++ 148 continue Args 149 } 150 if i+1 < len(list) { 151 return fmt.Errorf("invalid flag in %s: %s %s", source, arg, list[i+1]) 152 } 153 return fmt.Errorf("invalid flag in %s: %s without argument", source, arg) 154 } 155 } 156 Bad: 157 return fmt.Errorf("invalid flag in %s: %s", source, arg) 158 } 159 return nil 160 }