github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/seccomp/seccomp_rules.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package seccomp
    16  
    17  import "fmt"
    18  
    19  // The offsets are based on the following struct in include/linux/seccomp.h.
    20  // struct seccomp_data {
    21  //	int nr;
    22  //	__u32 arch;
    23  //	__u64 instruction_pointer;
    24  //	__u64 args[6];
    25  // };
    26  const (
    27  	seccompDataOffsetNR     = 0
    28  	seccompDataOffsetArch   = 4
    29  	seccompDataOffsetIPLow  = 8
    30  	seccompDataOffsetIPHigh = 12
    31  	seccompDataOffsetArgs   = 16
    32  )
    33  
    34  func seccompDataOffsetArgLow(i int) uint32 {
    35  	return uint32(seccompDataOffsetArgs + i*8)
    36  }
    37  
    38  func seccompDataOffsetArgHigh(i int) uint32 {
    39  	return seccompDataOffsetArgLow(i) + 4
    40  }
    41  
    42  // MatchAny is marker to indicate any value will be accepted.
    43  type MatchAny struct{}
    44  
    45  func (a MatchAny) String() (s string) {
    46  	return "*"
    47  }
    48  
    49  // EqualTo specifies a value that needs to be strictly matched.
    50  type EqualTo uintptr
    51  
    52  func (a EqualTo) String() (s string) {
    53  	return fmt.Sprintf("== %#x", uintptr(a))
    54  }
    55  
    56  // NotEqual specifies a value that is strictly not equal.
    57  type NotEqual uintptr
    58  
    59  func (a NotEqual) String() (s string) {
    60  	return fmt.Sprintf("!= %#x", uintptr(a))
    61  }
    62  
    63  // GreaterThan specifies a value that needs to be strictly smaller.
    64  type GreaterThan uintptr
    65  
    66  func (a GreaterThan) String() (s string) {
    67  	return fmt.Sprintf("> %#x", uintptr(a))
    68  }
    69  
    70  // GreaterThanOrEqual specifies a value that needs to be smaller or equal.
    71  type GreaterThanOrEqual uintptr
    72  
    73  func (a GreaterThanOrEqual) String() (s string) {
    74  	return fmt.Sprintf(">= %#x", uintptr(a))
    75  }
    76  
    77  // LessThan specifies a value that needs to be strictly greater.
    78  type LessThan uintptr
    79  
    80  func (a LessThan) String() (s string) {
    81  	return fmt.Sprintf("< %#x", uintptr(a))
    82  }
    83  
    84  // LessThanOrEqual specifies a value that needs to be greater or equal.
    85  type LessThanOrEqual uintptr
    86  
    87  func (a LessThanOrEqual) String() (s string) {
    88  	return fmt.Sprintf("<= %#x", uintptr(a))
    89  }
    90  
    91  type maskedEqual struct {
    92  	mask  uintptr
    93  	value uintptr
    94  }
    95  
    96  func (a maskedEqual) String() (s string) {
    97  	return fmt.Sprintf("& %#x == %#x", a.mask, a.value)
    98  }
    99  
   100  // MaskedEqual specifies a value that matches the input after the input is
   101  // masked (bitwise &) against the given mask. Can be used to verify that input
   102  // only includes certain approved flags.
   103  func MaskedEqual(mask, value uintptr) interface{} {
   104  	return maskedEqual{
   105  		mask:  mask,
   106  		value: value,
   107  	}
   108  }
   109  
   110  // Rule stores the allowed syscall arguments.
   111  //
   112  // For example:
   113  // rule := Rule {
   114  //       EqualTo(linux.ARCH_GET_FS | linux.ARCH_SET_FS), // arg0
   115  // }
   116  type Rule [7]interface{} // 6 arguments + RIP
   117  
   118  // RuleIP indicates what rules in the Rule array have to be applied to
   119  // instruction pointer.
   120  const RuleIP = 6
   121  
   122  func (r Rule) String() (s string) {
   123  	if len(r) == 0 {
   124  		return
   125  	}
   126  	s += "( "
   127  	for _, arg := range r {
   128  		if arg != nil {
   129  			s += fmt.Sprintf("%v ", arg)
   130  		}
   131  	}
   132  	s += ")"
   133  	return
   134  }
   135  
   136  // SyscallRules stores a map of OR'ed argument rules indexed by the syscall number.
   137  // If the 'Rules' is empty, we treat it as any argument is allowed.
   138  //
   139  // For example:
   140  //  rules := SyscallRules{
   141  //         syscall.SYS_FUTEX: []Rule{
   142  //                 {
   143  //                         MatchAny{},
   144  //                         EqualTo(linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG),
   145  //                 }, // OR
   146  //                 {
   147  //                         MatchAny{},
   148  //                         EqualTo(linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG),
   149  //                 },
   150  //         },
   151  //         syscall.SYS_GETPID: []Rule{},
   152  // }
   153  type SyscallRules map[uintptr][]Rule
   154  
   155  // NewSyscallRules returns a new SyscallRules.
   156  func NewSyscallRules() SyscallRules {
   157  	return make(map[uintptr][]Rule)
   158  }
   159  
   160  // AddRule adds the given rule. It will create a new entry for a new syscall, otherwise
   161  // it will append to the existing rules.
   162  func (sr SyscallRules) AddRule(sysno uintptr, r Rule) {
   163  	if cur, ok := sr[sysno]; ok {
   164  		// An empty rules means allow all. Honor it when more rules are added.
   165  		if len(cur) == 0 {
   166  			sr[sysno] = append(sr[sysno], Rule{})
   167  		}
   168  		sr[sysno] = append(sr[sysno], r)
   169  	} else {
   170  		sr[sysno] = []Rule{r}
   171  	}
   172  }
   173  
   174  // Merge merges the given SyscallRules.
   175  func (sr SyscallRules) Merge(rules SyscallRules) {
   176  	for sysno, rs := range rules {
   177  		if cur, ok := sr[sysno]; ok {
   178  			// An empty rules means allow all. Honor it when more rules are added.
   179  			if len(cur) == 0 {
   180  				sr[sysno] = append(sr[sysno], Rule{})
   181  			}
   182  			if len(rs) == 0 {
   183  				rs = []Rule{{}}
   184  			}
   185  			sr[sysno] = append(sr[sysno], rs...)
   186  		} else {
   187  			sr[sysno] = rs
   188  		}
   189  	}
   190  }