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: | |
jln (very slow on Chromium)
2012/12/14 02:28:02
nit: indent the case:
| |
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 |