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  }