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  }