Source file src/cmd/go/internal/work/security.go

     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  	"fmt"
    34  	"internal/lazyregexp"
    35  	"regexp"
    36  	"strings"
    37  
    38  	"cmd/go/internal/cfg"
    39  	"cmd/go/internal/load"
    40  )
    41  
    42  var re = lazyregexp.New
    43  
    44  var validCompilerFlags = []*lazyregexp.Regexp{
    45  	re(`-D([A-Za-z_][A-Za-z0-9_]*)(=[^@\-]*)?`),
    46  	re(`-U([A-Za-z_][A-Za-z0-9_]*)`),
    47  	re(`-F([^@\-].*)`),
    48  	re(`-I([^@\-].*)`),
    49  	re(`-O`),
    50  	re(`-O([^@\-].*)`),
    51  	re(`-W`),
    52  	re(`-W([^@,]+)`), // -Wall but not -Wa,-foo.
    53  	re(`-Wa,-mbig-obj`),
    54  	re(`-Wp,-D([A-Za-z_][A-Za-z0-9_]*)(=[^@,\-]*)?`),
    55  	re(`-Wp,-U([A-Za-z_][A-Za-z0-9_]*)`),
    56  	re(`-ansi`),
    57  	re(`-f(no-)?asynchronous-unwind-tables`),
    58  	re(`-f(no-)?blocks`),
    59  	re(`-f(no-)builtin-[a-zA-Z0-9_]*`),
    60  	re(`-f(no-)?common`),
    61  	re(`-f(no-)?constant-cfstrings`),
    62  	re(`-fdebug-prefix-map=([^@]+)=([^@]+)`),
    63  	re(`-fdiagnostics-show-note-include-stack`),
    64  	re(`-ffile-prefix-map=([^@]+)=([^@]+)`),
    65  	re(`-fno-canonical-system-headers`),
    66  	re(`-f(no-)?eliminate-unused-debug-types`),
    67  	re(`-f(no-)?exceptions`),
    68  	re(`-f(no-)?fast-math`),
    69  	re(`-f(no-)?inline-functions`),
    70  	re(`-finput-charset=([^@\-].*)`),
    71  	re(`-f(no-)?fat-lto-objects`),
    72  	re(`-f(no-)?keep-inline-dllexport`),
    73  	re(`-f(no-)?lto`),
    74  	re(`-fmacro-backtrace-limit=(.+)`),
    75  	re(`-fmessage-length=(.+)`),
    76  	re(`-f(no-)?modules`),
    77  	re(`-f(no-)?objc-arc`),
    78  	re(`-f(no-)?objc-nonfragile-abi`),
    79  	re(`-f(no-)?objc-legacy-dispatch`),
    80  	re(`-f(no-)?omit-frame-pointer`),
    81  	re(`-f(no-)?openmp(-simd)?`),
    82  	re(`-f(no-)?permissive`),
    83  	re(`-f(no-)?(pic|PIC|pie|PIE)`),
    84  	re(`-f(no-)?plt`),
    85  	re(`-f(no-)?rtti`),
    86  	re(`-f(no-)?split-stack`),
    87  	re(`-f(no-)?stack-(.+)`),
    88  	re(`-f(no-)?strict-aliasing`),
    89  	re(`-f(un)signed-char`),
    90  	re(`-f(no-)?use-linker-plugin`), // safe if -B is not used; we don't permit -B
    91  	re(`-f(no-)?visibility-inlines-hidden`),
    92  	re(`-fsanitize=(.+)`),
    93  	re(`-fsanitize-undefined-strip-path-components=(-)?[0-9]+`),
    94  	re(`-ftemplate-depth-(.+)`),
    95  	re(`-ftls-model=(global-dynamic|local-dynamic|initial-exec|local-exec)`),
    96  	re(`-fvisibility=(.+)`),
    97  	re(`-g([^@\-].*)?`),
    98  	re(`-m32`),
    99  	re(`-m64`),
   100  	re(`-m(abi|arch|cpu|fpu|simd|tls-dialect|tune)=([^@\-].*)`),
   101  	re(`-m(no-)?v?aes`),
   102  	re(`-marm`),
   103  	re(`-m(no-)?avx[0-9a-z]*`),
   104  	re(`-mcmodel=[0-9a-z-]+`),
   105  	re(`-mfloat-abi=([^@\-].*)`),
   106  	re(`-m(soft|single|double)-float`),
   107  	re(`-mfpmath=[0-9a-z,+]*`),
   108  	re(`-m(no-)?avx[0-9a-z.]*`),
   109  	re(`-m(no-)?ms-bitfields`),
   110  	re(`-m(no-)?stack-(.+)`),
   111  	re(`-mmacosx-(.+)`),
   112  	re(`-m(no-)?relax`),
   113  	re(`-m(no-)?strict-align`),
   114  	re(`-m(no-)?(lsx|lasx|frecipe|div32|lam-bh|lamcas|ld-seq-sa)`),
   115  	re(`-mios-simulator-version-min=(.+)`),
   116  	re(`-miphoneos-version-min=(.+)`),
   117  	re(`-mlarge-data-threshold=[0-9]+`),
   118  	re(`-mtvos-simulator-version-min=(.+)`),
   119  	re(`-mtvos-version-min=(.+)`),
   120  	re(`-mwatchos-simulator-version-min=(.+)`),
   121  	re(`-mwatchos-version-min=(.+)`),
   122  	re(`-mnop-fun-dllimport`),
   123  	re(`-m(no-)?sse[0-9.]*`),
   124  	re(`-m(no-)?ssse3`),
   125  	re(`-mthumb(-interwork)?`),
   126  	re(`-mthreads`),
   127  	re(`-mwindows`),
   128  	re(`-no-canonical-prefixes`),
   129  	re(`--param=ssp-buffer-size=[0-9]*`),
   130  	re(`-pedantic(-errors)?`),
   131  	re(`-pipe`),
   132  	re(`-pthread`),
   133  	re(`-?-std=([^@\-].*)`),
   134  	re(`-?-stdlib=([^@\-].*)`),
   135  	re(`--sysroot=([^@\-].*)`),
   136  	re(`-w`),
   137  	re(`-x([^@\-].*)`),
   138  	re(`-v`),
   139  }
   140  
   141  var validCompilerFlagsWithNextArg = []string{
   142  	"-arch",
   143  	"-D",
   144  	"-U",
   145  	"-I",
   146  	"-F",
   147  	"-framework",
   148  	"-include",
   149  	"-isysroot",
   150  	"-isystem",
   151  	"--sysroot",
   152  	"-target",
   153  	"-x",
   154  }
   155  
   156  var invalidLinkerFlags = []*lazyregexp.Regexp{
   157  	// On macOS this means the linker loads and executes the next argument.
   158  	// Have to exclude separately because -lfoo is allowed in general.
   159  	re(`-lto_library`),
   160  }
   161  
   162  var validLinkerFlags = []*lazyregexp.Regexp{
   163  	re(`-F([^@\-].*)`),
   164  	re(`-l([^@\-].*)`),
   165  	re(`-L([^@\-].*)`),
   166  	re(`-O`),
   167  	re(`-O([^@\-].*)`),
   168  	re(`-f(no-)?(pic|PIC|pie|PIE)`),
   169  	re(`-f(no-)?openmp(-simd)?`),
   170  	re(`-fsanitize=([^@\-].*)`),
   171  	re(`-flat_namespace`),
   172  	re(`-g([^@\-].*)?`),
   173  	re(`-headerpad_max_install_names`),
   174  	re(`-m(abi|arch|cpu|fpu|simd|tls-dialect|tune)=([^@\-].*)`),
   175  	re(`-mcmodel=[0-9a-z-]+`),
   176  	re(`-mfloat-abi=([^@\-].*)`),
   177  	re(`-m(soft|single|double)-float`),
   178  	re(`-m(no-)?relax`),
   179  	re(`-m(no-)?strict-align`),
   180  	re(`-m(no-)?(lsx|lasx|frecipe|div32|lam-bh|lamcas|ld-seq-sa)`),
   181  	re(`-mmacosx-(.+)`),
   182  	re(`-mios-simulator-version-min=(.+)`),
   183  	re(`-miphoneos-version-min=(.+)`),
   184  	re(`-mthreads`),
   185  	re(`-mwindows`),
   186  	re(`-(pic|PIC|pie|PIE)`),
   187  	re(`-pthread`),
   188  	re(`-rdynamic`),
   189  	re(`-shared`),
   190  	re(`-?-static([-a-z0-9+]*)`),
   191  	re(`-?-stdlib=([^@\-].*)`),
   192  	re(`-v`),
   193  
   194  	// Note that any wildcards in -Wl need to exclude comma,
   195  	// since -Wl splits its argument at commas and passes
   196  	// them all to the linker uninterpreted. Allowing comma
   197  	// in a wildcard would allow tunneling arbitrary additional
   198  	// linker arguments through one of these.
   199  	re(`-Wl,--(no-)?allow-multiple-definition`),
   200  	re(`-Wl,--(no-)?allow-shlib-undefined`),
   201  	re(`-Wl,--(no-)?as-needed`),
   202  	re(`-Wl,-Bdynamic`),
   203  	re(`-Wl,-berok`),
   204  	re(`-Wl,-Bstatic`),
   205  	re(`-Wl,-Bsymbolic-functions`),
   206  	re(`-Wl,-O[0-9]+`),
   207  	re(`-Wl,-d[ny]`),
   208  	re(`-Wl,--disable-new-dtags`),
   209  	re(`-Wl,-e[=,][a-zA-Z0-9]+`),
   210  	re(`-Wl,--enable-new-dtags`),
   211  	re(`-Wl,--end-group`),
   212  	re(`-Wl,--(no-)?export-dynamic`),
   213  	re(`-Wl,-E`),
   214  	re(`-Wl,-framework,[^,@\-][^,]*`),
   215  	re(`-Wl,--hash-style=(sysv|gnu|both)`),
   216  	re(`-Wl,-headerpad_max_install_names`),
   217  	re(`-Wl,--no-undefined`),
   218  	re(`-Wl,--pop-state`),
   219  	re(`-Wl,--push-state`),
   220  	re(`-Wl,-R,?([^@\-,][^,@]*$)`),
   221  	re(`-Wl,--just-symbols[=,]([^,@\-][^,@]*)`),
   222  	re(`-Wl,-rpath(-link)?[=,]([^,@\-][^,]*)`),
   223  	re(`-Wl,-s`),
   224  	re(`-Wl,-search_paths_first`),
   225  	re(`-Wl,-sectcreate,([^,@\-][^,]*),([^,@\-][^,]*),([^,@\-][^,]*)`),
   226  	re(`-Wl,--start-group`),
   227  	re(`-Wl,-?-static`),
   228  	re(`-Wl,-?-subsystem,(native|windows|console|posix|xbox)`),
   229  	re(`-Wl,-syslibroot[=,]([^,@\-][^,]*)`),
   230  	re(`-Wl,-undefined[=,]([^,@\-][^,]*)`),
   231  	re(`-Wl,-?-unresolved-symbols=[^,]+`),
   232  	re(`-Wl,--(no-)?warn-([^,]+)`),
   233  	re(`-Wl,-?-wrap[=,][^,@\-][^,]*`),
   234  	re(`-Wl(,-z,(relro|now|(no)?execstack))+`),
   235  
   236  	re(`[a-zA-Z0-9_/].*\.(a|o|obj|dll|dylib|so|tbd)`), // direct linker inputs: x.o or libfoo.so (but not -foo.o or @foo.o)
   237  	re(`\./.*\.(a|o|obj|dll|dylib|so|tbd)`),
   238  }
   239  
   240  var validLinkerFlagsWithNextArg = []string{
   241  	"-arch",
   242  	"-F",
   243  	"-l",
   244  	"-L",
   245  	"-framework",
   246  	"-isysroot",
   247  	"--sysroot",
   248  	"-target",
   249  	"-Wl,-framework",
   250  	"-Wl,-rpath",
   251  	"-Wl,-R",
   252  	"-Wl,--just-symbols",
   253  	"-Wl,-undefined",
   254  }
   255  
   256  func checkCompilerFlags(name, source string, list []string) error {
   257  	checkOverrides := true
   258  	return checkFlags(name, source, list, nil, validCompilerFlags, validCompilerFlagsWithNextArg, checkOverrides)
   259  }
   260  
   261  func checkLinkerFlags(name, source string, list []string) error {
   262  	checkOverrides := true
   263  	return checkFlags(name, source, list, invalidLinkerFlags, validLinkerFlags, validLinkerFlagsWithNextArg, checkOverrides)
   264  }
   265  
   266  // checkCompilerFlagsForInternalLink returns an error if 'list'
   267  // contains a flag or flags that may not be fully supported by
   268  // internal linking (meaning that we should punt the link to the
   269  // external linker).
   270  func checkCompilerFlagsForInternalLink(name, source string, list []string) error {
   271  	checkOverrides := false
   272  	if err := checkFlags(name, source, list, nil, validCompilerFlags, validCompilerFlagsWithNextArg, checkOverrides); err != nil {
   273  		return err
   274  	}
   275  	// Currently the only flag on the allow list that causes problems
   276  	// for the linker is "-flto"; check for it manually here.
   277  	for _, fl := range list {
   278  		if strings.HasPrefix(fl, "-flto") {
   279  			return fmt.Errorf("flag %q triggers external linking", fl)
   280  		}
   281  	}
   282  	return nil
   283  }
   284  
   285  func checkFlags(name, source string, list []string, invalid, valid []*lazyregexp.Regexp, validNext []string, checkOverrides bool) error {
   286  	// Let users override rules with $CGO_CFLAGS_ALLOW, $CGO_CFLAGS_DISALLOW, etc.
   287  	var (
   288  		allow    *regexp.Regexp
   289  		disallow *regexp.Regexp
   290  	)
   291  	if checkOverrides {
   292  		if env := cfg.Getenv("CGO_" + name + "_ALLOW"); env != "" {
   293  			r, err := regexp.Compile(env)
   294  			if err != nil {
   295  				return fmt.Errorf("parsing $CGO_%s_ALLOW: %v", name, err)
   296  			}
   297  			allow = r
   298  		}
   299  		if env := cfg.Getenv("CGO_" + name + "_DISALLOW"); env != "" {
   300  			r, err := regexp.Compile(env)
   301  			if err != nil {
   302  				return fmt.Errorf("parsing $CGO_%s_DISALLOW: %v", name, err)
   303  			}
   304  			disallow = r
   305  		}
   306  	}
   307  
   308  Args:
   309  	for i := 0; i < len(list); i++ {
   310  		arg := list[i]
   311  		if disallow != nil && disallow.FindString(arg) == arg {
   312  			goto Bad
   313  		}
   314  		if allow != nil && allow.FindString(arg) == arg {
   315  			continue Args
   316  		}
   317  		for _, re := range invalid {
   318  			if re.FindString(arg) == arg { // must be complete match
   319  				goto Bad
   320  			}
   321  		}
   322  		for _, re := range valid {
   323  			if match := re.FindString(arg); match == arg { // must be complete match
   324  				continue Args
   325  			} else if strings.HasPrefix(arg, "-Wl,--push-state,") {
   326  				// Examples for --push-state are written
   327  				//     -Wl,--push-state,--as-needed
   328  				// Support other commands in the same -Wl arg.
   329  				args := strings.Split(arg, ",")
   330  				for _, a := range args[1:] {
   331  					a = "-Wl," + a
   332  					var found bool
   333  					for _, re := range valid {
   334  						if re.FindString(a) == a {
   335  							found = true
   336  							break
   337  						}
   338  					}
   339  					if !found {
   340  						goto Bad
   341  					}
   342  					for _, re := range invalid {
   343  						if re.FindString(a) == a {
   344  							goto Bad
   345  						}
   346  					}
   347  				}
   348  				continue Args
   349  			}
   350  		}
   351  		for _, x := range validNext {
   352  			if arg == x {
   353  				if i+1 < len(list) && load.SafeArg(list[i+1]) {
   354  					i++
   355  					continue Args
   356  				}
   357  
   358  				// Permit -Wl,-framework -Wl,name.
   359  				if i+1 < len(list) &&
   360  					strings.HasPrefix(arg, "-Wl,") &&
   361  					strings.HasPrefix(list[i+1], "-Wl,") &&
   362  					load.SafeArg(list[i+1][4:]) &&
   363  					!strings.Contains(list[i+1][4:], ",") {
   364  					i++
   365  					continue Args
   366  				}
   367  
   368  				// Permit -I= /path, -I $SYSROOT.
   369  				if i+1 < len(list) && arg == "-I" {
   370  					if (strings.HasPrefix(list[i+1], "=") || strings.HasPrefix(list[i+1], "$SYSROOT")) &&
   371  						load.SafeArg(list[i+1][1:]) {
   372  						i++
   373  						continue Args
   374  					}
   375  				}
   376  
   377  				if i+1 < len(list) {
   378  					return fmt.Errorf("invalid flag in %s: %s %s (see https://golang.org/s/invalidflag)", source, arg, list[i+1])
   379  				}
   380  				return fmt.Errorf("invalid flag in %s: %s without argument (see https://golang.org/s/invalidflag)", source, arg)
   381  			}
   382  		}
   383  	Bad:
   384  		return fmt.Errorf("invalid flag in %s: %s", source, arg)
   385  	}
   386  	return nil
   387  }
   388  

View as plain text