...

Source file src/pkg/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_].*)`),
    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 = []*lazyregexp.Regexp{
   140		re(`-F([^@\-].*)`),
   141		re(`-l([^@\-].*)`),
   142		re(`-L([^@\-].*)`),
   143		re(`-O`),
   144		re(`-O([^@\-].*)`),
   145		re(`-f(no-)?(pic|PIC|pie|PIE)`),
   146		re(`-f(no-)?openmp(-simd)?`),
   147		re(`-fsanitize=([^@\-].*)`),
   148		re(`-flat_namespace`),
   149		re(`-g([^@\-].*)?`),
   150		re(`-headerpad_max_install_names`),
   151		re(`-m(abi|arch|cpu|fpu|tune)=([^@\-].*)`),
   152		re(`-mfloat-abi=([^@\-].*)`),
   153		re(`-mmacosx-(.+)`),
   154		re(`-mios-simulator-version-min=(.+)`),
   155		re(`-miphoneos-version-min=(.+)`),
   156		re(`-mthreads`),
   157		re(`-mwindows`),
   158		re(`-(pic|PIC|pie|PIE)`),
   159		re(`-pthread`),
   160		re(`-rdynamic`),
   161		re(`-shared`),
   162		re(`-?-static([-a-z0-9+]*)`),
   163		re(`-?-stdlib=([^@\-].*)`),
   164		re(`-v`),
   165	
   166		// Note that any wildcards in -Wl need to exclude comma,
   167		// since -Wl splits its argument at commas and passes
   168		// them all to the linker uninterpreted. Allowing comma
   169		// in a wildcard would allow tunnelling arbitrary additional
   170		// linker arguments through one of these.
   171		re(`-Wl,--(no-)?allow-multiple-definition`),
   172		re(`-Wl,--(no-)?allow-shlib-undefined`),
   173		re(`-Wl,--(no-)?as-needed`),
   174		re(`-Wl,-Bdynamic`),
   175		re(`-Wl,-berok`),
   176		re(`-Wl,-Bstatic`),
   177		re(`-WL,-O([^@,\-][^,]*)?`),
   178		re(`-Wl,-d[ny]`),
   179		re(`-Wl,--disable-new-dtags`),
   180		re(`-Wl,-e[=,][a-zA-Z0-9]*`),
   181		re(`-Wl,--enable-new-dtags`),
   182		re(`-Wl,--end-group`),
   183		re(`-Wl,--(no-)?export-dynamic`),
   184		re(`-Wl,-framework,[^,@\-][^,]+`),
   185		re(`-Wl,-headerpad_max_install_names`),
   186		re(`-Wl,--no-undefined`),
   187		re(`-Wl,-R([^@\-][^,@]*$)`),
   188		re(`-Wl,--just-symbols[=,]([^,@\-][^,@]+)`),
   189		re(`-Wl,-rpath(-link)?[=,]([^,@\-][^,]+)`),
   190		re(`-Wl,-s`),
   191		re(`-Wl,-search_paths_first`),
   192		re(`-Wl,-sectcreate,([^,@\-][^,]+),([^,@\-][^,]+),([^,@\-][^,]+)`),
   193		re(`-Wl,--start-group`),
   194		re(`-Wl,-?-static`),
   195		re(`-Wl,-?-subsystem,(native|windows|console|posix|xbox)`),
   196		re(`-Wl,-syslibroot[=,]([^,@\-][^,]+)`),
   197		re(`-Wl,-undefined[=,]([^,@\-][^,]+)`),
   198		re(`-Wl,-?-unresolved-symbols=[^,]+`),
   199		re(`-Wl,--(no-)?warn-([^,]+)`),
   200		re(`-Wl,-z,(no)?execstack`),
   201		re(`-Wl,-z,relro`),
   202	
   203		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)
   204		re(`\./.*\.(a|o|obj|dll|dylib|so)`),
   205	}
   206	
   207	var validLinkerFlagsWithNextArg = []string{
   208		"-arch",
   209		"-F",
   210		"-l",
   211		"-L",
   212		"-framework",
   213		"-isysroot",
   214		"--sysroot",
   215		"-target",
   216		"-Wl,-framework",
   217		"-Wl,-rpath",
   218		"-Wl,-R",
   219		"-Wl,--just-symbols",
   220		"-Wl,-undefined",
   221	}
   222	
   223	func checkCompilerFlags(name, source string, list []string) error {
   224		return checkFlags(name, source, list, validCompilerFlags, validCompilerFlagsWithNextArg)
   225	}
   226	
   227	func checkLinkerFlags(name, source string, list []string) error {
   228		return checkFlags(name, source, list, validLinkerFlags, validLinkerFlagsWithNextArg)
   229	}
   230	
   231	func checkFlags(name, source string, list []string, valid []*lazyregexp.Regexp, validNext []string) error {
   232		// Let users override rules with $CGO_CFLAGS_ALLOW, $CGO_CFLAGS_DISALLOW, etc.
   233		var (
   234			allow    *regexp.Regexp
   235			disallow *regexp.Regexp
   236		)
   237		if env := cfg.Getenv("CGO_" + name + "_ALLOW"); env != "" {
   238			r, err := regexp.Compile(env)
   239			if err != nil {
   240				return fmt.Errorf("parsing $CGO_%s_ALLOW: %v", name, err)
   241			}
   242			allow = r
   243		}
   244		if env := cfg.Getenv("CGO_" + name + "_DISALLOW"); env != "" {
   245			r, err := regexp.Compile(env)
   246			if err != nil {
   247				return fmt.Errorf("parsing $CGO_%s_DISALLOW: %v", name, err)
   248			}
   249			disallow = r
   250		}
   251	
   252	Args:
   253		for i := 0; i < len(list); i++ {
   254			arg := list[i]
   255			if disallow != nil && disallow.FindString(arg) == arg {
   256				goto Bad
   257			}
   258			if allow != nil && allow.FindString(arg) == arg {
   259				continue Args
   260			}
   261			for _, re := range valid {
   262				if re.FindString(arg) == arg { // must be complete match
   263					continue Args
   264				}
   265			}
   266			for _, x := range validNext {
   267				if arg == x {
   268					if i+1 < len(list) && load.SafeArg(list[i+1]) {
   269						i++
   270						continue Args
   271					}
   272	
   273					// Permit -Wl,-framework -Wl,name.
   274					if i+1 < len(list) &&
   275						strings.HasPrefix(arg, "-Wl,") &&
   276						strings.HasPrefix(list[i+1], "-Wl,") &&
   277						load.SafeArg(list[i+1][4:]) &&
   278						!strings.Contains(list[i+1][4:], ",") {
   279						i++
   280						continue Args
   281					}
   282	
   283					if i+1 < len(list) {
   284						return fmt.Errorf("invalid flag in %s: %s %s (see https://golang.org/s/invalidflag)", source, arg, list[i+1])
   285					}
   286					return fmt.Errorf("invalid flag in %s: %s without argument (see https://golang.org/s/invalidflag)", source, arg)
   287				}
   288			}
   289		Bad:
   290			return fmt.Errorf("invalid flag in %s: %s", source, arg)
   291		}
   292		return nil
   293	}
   294	

View as plain text