OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" | 5 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" |
6 #include "sandbox/linux/seccomp-bpf/syscall_iterator.h" | 6 #include "sandbox/linux/seccomp-bpf/syscall_iterator.h" |
7 #include "sandbox/linux/seccomp-bpf/verifier.h" | 7 #include "sandbox/linux/seccomp-bpf/verifier.h" |
8 | 8 |
9 | 9 |
10 namespace playground2 { | 10 namespace playground2 { |
(...skipping 24 matching lines...) Expand all Loading... |
35 if (!(sysnum & 0x40000000u)) { | 35 if (!(sysnum & 0x40000000u)) { |
36 continue; | 36 continue; |
37 } | 37 } |
38 #else | 38 #else |
39 if (sysnum & 0x40000000u) { | 39 if (sysnum & 0x40000000u) { |
40 continue; | 40 continue; |
41 } | 41 } |
42 #endif | 42 #endif |
43 #endif | 43 #endif |
44 ErrorCode code = evaluate_syscall(sysnum, aux); | 44 ErrorCode code = evaluate_syscall(sysnum, aux); |
45 uint32_t computed_ret = EvaluateBPF(program, data, err); | 45 if (!VerifyErrorCode(program, &data, code, err)) { |
| 46 return false; |
| 47 } |
| 48 } |
| 49 return true; |
| 50 } |
| 51 |
| 52 bool Verifier::VerifyErrorCode(const std::vector<struct sock_filter>& program, |
| 53 struct arch_seccomp_data *data, |
| 54 const ErrorCode& code, const char **err) { |
| 55 if (code.error_type_ == ErrorCode::ET_SIMPLE || |
| 56 code.error_type_ == ErrorCode::ET_TRAP) { |
| 57 uint32_t computed_ret = EvaluateBPF(program, *data, err); |
46 if (*err) { | 58 if (*err) { |
47 return false; | 59 return false; |
48 } else if (computed_ret != code.err()) { | 60 } else if (computed_ret != code.err()) { |
49 *err = "Exit code from BPF program doesn't match"; | 61 *err = "Exit code from BPF program doesn't match"; |
50 return false; | 62 return false; |
51 } | 63 } |
| 64 } else if (code.error_type_ == ErrorCode::ET_COND) { |
| 65 if (code.argno_ < 0 || code.argno_ >= 6) { |
| 66 *err = "Invalid argument number in error code"; |
| 67 return false; |
| 68 } |
| 69 switch (code.op_) { |
| 70 case ErrorCode::OP_EQUAL: |
| 71 // Verify that we can check a 32bit value (or the LSB of a 64bit value) |
| 72 // for equality. |
| 73 data->args[code.argno_] = code.value_; |
| 74 if (!VerifyErrorCode(program, data, *code.passed_, err)) { |
| 75 return false; |
| 76 } |
| 77 |
| 78 // Change the value to no longer match and verify that this is detected |
| 79 // as an inequality. |
| 80 data->args[code.argno_] = code.value_ ^ 0x55AA55AA; |
| 81 if (!VerifyErrorCode(program, data, *code.failed_, err)) { |
| 82 return false; |
| 83 } |
| 84 |
| 85 // BPF programs can only ever operate on 32bit values. So, we have |
| 86 // generated additional BPF instructions that inspect the MSB. Verify |
| 87 // that they behave as intended. |
| 88 if (code.width_ == ErrorCode::TP_32BIT) { |
| 89 if (code.value_ >> 32) { |
| 90 SANDBOX_DIE("Invalid comparison of a 32bit system call argument " |
| 91 "against a 64bit constant; this test is always false."); |
| 92 } |
| 93 |
| 94 // If the system call argument was intended to be a 32bit parameter, |
| 95 // verify that it is a fatal error if a 64bit value is ever passed |
| 96 // here. |
| 97 data->args[code.argno_] = 0x100000000ull; |
| 98 if (!VerifyErrorCode(program, data, Sandbox::Unexpected64bitArgument(), |
| 99 err)) { |
| 100 return false; |
| 101 } |
| 102 } else { |
| 103 // If the system call argument was intended to be a 64bit parameter, |
| 104 // verify that we can handle (in-)equality for the MSB. This is |
| 105 // essentially the same test that we did earlier for the LSB. |
| 106 // We only need to verify the behavior of the inequality test. We |
| 107 // know that the equality test already passed, as unlike the kernel |
| 108 // the Verifier does operate on 64bit quantities. |
| 109 data->args[code.argno_] = code.value_ ^ 0x55AA55AA00000000ull; |
| 110 if (!VerifyErrorCode(program, data, *code.failed_, err)) { |
| 111 return false; |
| 112 } |
| 113 } |
| 114 break; |
| 115 default: // TODO(markus): We can only check for equality so far. |
| 116 *err = "Unsupported operation in conditional error code"; |
| 117 return false; |
| 118 } |
| 119 } else { |
| 120 *err = "Attempting to return invalid error code from BPF program"; |
| 121 return false; |
52 } | 122 } |
53 return true; | 123 return true; |
54 } | 124 } |
55 | 125 |
56 uint32_t Verifier::EvaluateBPF(const std::vector<struct sock_filter>& program, | 126 uint32_t Verifier::EvaluateBPF(const std::vector<struct sock_filter>& program, |
57 const struct arch_seccomp_data& data, | 127 const struct arch_seccomp_data& data, |
58 const char **err) { | 128 const char **err) { |
59 *err = NULL; | 129 *err = NULL; |
60 if (program.size() < 1 || program.size() >= SECCOMP_MAX_PROGRAM_SIZE) { | 130 if (program.size() < 1 || program.size() >= SECCOMP_MAX_PROGRAM_SIZE) { |
61 *err = "Invalid program length"; | 131 *err = "Invalid program length"; |
62 return 0; | 132 return 0; |
63 } | 133 } |
64 for (State state(program, data); !*err; ++state.ip) { | 134 for (State state(program, data); !*err; ++state.ip) { |
65 if (state.ip >= program.size()) { | 135 if (state.ip >= program.size()) { |
66 *err = "Invalid instruction pointer in BPF program"; | 136 *err = "Invalid instruction pointer in BPF program"; |
67 break; | 137 break; |
68 } | 138 } |
69 const struct sock_filter& insn = program[state.ip]; | 139 const struct sock_filter& insn = program[state.ip]; |
70 switch (BPF_CLASS(insn.code)) { | 140 switch (BPF_CLASS(insn.code)) { |
71 case BPF_LD: | 141 case BPF_LD: |
72 Ld(&state, insn, err); | 142 Ld(&state, insn, err); |
73 break; | 143 break; |
74 case BPF_JMP: | 144 case BPF_JMP: |
75 Jmp(&state, insn, err); | 145 Jmp(&state, insn, err); |
76 break; | 146 break; |
77 case BPF_RET: | 147 case BPF_RET: { |
78 return Ret(&state, insn, err); | 148 uint32_t r = Ret(&state, insn, err); |
| 149 switch (r & SECCOMP_RET_ACTION) { |
| 150 case SECCOMP_RET_TRAP: |
| 151 case SECCOMP_RET_ERRNO: |
| 152 case SECCOMP_RET_ALLOW: |
| 153 break; |
| 154 case SECCOMP_RET_KILL: // We don't ever generate this |
| 155 case SECCOMP_RET_TRACE: // We don't ever generate this |
| 156 case SECCOMP_RET_INVALID: // Should never show up in BPF program |
| 157 default: |
| 158 *err = "Unexpected return code found in BPF program"; |
| 159 return 0; |
| 160 } |
| 161 return r; } |
79 default: | 162 default: |
80 *err = "Unexpected instruction in BPF program"; | 163 *err = "Unexpected instruction in BPF program"; |
81 break; | 164 break; |
82 } | 165 } |
83 } | 166 } |
84 return 0; | 167 return 0; |
85 } | 168 } |
86 | 169 |
87 void Verifier::Ld(State *state, const struct sock_filter& insn, | 170 void Verifier::Ld(State *state, const struct sock_filter& insn, |
88 const char **err) { | 171 const char **err) { |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
159 uint32_t Verifier::Ret(State *, const struct sock_filter& insn, | 242 uint32_t Verifier::Ret(State *, const struct sock_filter& insn, |
160 const char **err) { | 243 const char **err) { |
161 if (BPF_SRC(insn.code) != BPF_K) { | 244 if (BPF_SRC(insn.code) != BPF_K) { |
162 *err = "Invalid BPF_RET instruction"; | 245 *err = "Invalid BPF_RET instruction"; |
163 return 0; | 246 return 0; |
164 } | 247 } |
165 return insn.k; | 248 return insn.k; |
166 } | 249 } |
167 | 250 |
168 } // namespace | 251 } // namespace |
OLD | NEW |