security.go raw

   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  }
 303