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/bpf_dsl/policy_compiler.h" | 5 #include "sandbox/linux/bpf_dsl/policy_compiler.h" |
6 | 6 |
7 #include <errno.h> | 7 #include <errno.h> |
8 #include <linux/filter.h> | 8 #include <linux/filter.h> |
9 #include <sys/syscall.h> | 9 #include <sys/syscall.h> |
10 | 10 |
11 #include <limits> | 11 #include <limits> |
12 | 12 |
13 #include "base/logging.h" | 13 #include "base/logging.h" |
14 #include "base/macros.h" | 14 #include "base/macros.h" |
15 #include "sandbox/linux/bpf_dsl/bpf_dsl.h" | 15 #include "sandbox/linux/bpf_dsl/bpf_dsl.h" |
16 #include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h" | 16 #include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h" |
17 #include "sandbox/linux/bpf_dsl/codegen.h" | 17 #include "sandbox/linux/bpf_dsl/codegen.h" |
18 #include "sandbox/linux/bpf_dsl/policy.h" | 18 #include "sandbox/linux/bpf_dsl/policy.h" |
19 #include "sandbox/linux/bpf_dsl/seccomp_macros.h" | 19 #include "sandbox/linux/bpf_dsl/seccomp_macros.h" |
20 #include "sandbox/linux/bpf_dsl/syscall_set.h" | 20 #include "sandbox/linux/bpf_dsl/syscall_set.h" |
21 #include "sandbox/linux/seccomp-bpf/die.h" | 21 #include "sandbox/linux/seccomp-bpf/die.h" |
22 #include "sandbox/linux/seccomp-bpf/errorcode.h" | 22 #include "sandbox/linux/seccomp-bpf/errorcode.h" |
| 23 #include "sandbox/linux/seccomp-bpf/syscall.h" |
23 #include "sandbox/linux/system_headers/linux_seccomp.h" | 24 #include "sandbox/linux/system_headers/linux_seccomp.h" |
24 | 25 |
25 namespace sandbox { | 26 namespace sandbox { |
26 namespace bpf_dsl { | 27 namespace bpf_dsl { |
27 | 28 |
28 namespace { | 29 namespace { |
29 | 30 |
30 #if defined(__i386__) || defined(__x86_64__) | 31 #if defined(__i386__) || defined(__x86_64__) |
31 const bool kIsIntel = true; | 32 const bool kIsIntel = true; |
32 #else | 33 #else |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
78 } // namespace | 79 } // namespace |
79 | 80 |
80 struct PolicyCompiler::Range { | 81 struct PolicyCompiler::Range { |
81 uint32_t from; | 82 uint32_t from; |
82 CodeGen::Node node; | 83 CodeGen::Node node; |
83 }; | 84 }; |
84 | 85 |
85 PolicyCompiler::PolicyCompiler(const Policy* policy, TrapRegistry* registry) | 86 PolicyCompiler::PolicyCompiler(const Policy* policy, TrapRegistry* registry) |
86 : policy_(policy), | 87 : policy_(policy), |
87 registry_(registry), | 88 registry_(registry), |
88 escapepc_(0), | |
89 conds_(), | 89 conds_(), |
90 gen_(), | 90 gen_(), |
91 has_unsafe_traps_(HasUnsafeTraps(policy_)) { | 91 has_unsafe_traps_(HasUnsafeTraps(policy_)) { |
92 DCHECK(policy); | 92 DCHECK(policy); |
93 } | 93 } |
94 | 94 |
95 PolicyCompiler::~PolicyCompiler() { | 95 PolicyCompiler::~PolicyCompiler() { |
96 } | 96 } |
97 | 97 |
98 scoped_ptr<CodeGen::Program> PolicyCompiler::Compile() { | 98 scoped_ptr<CodeGen::Program> PolicyCompiler::Compile() { |
99 if (!policy_->InvalidSyscall()->IsDeny()) { | 99 if (!policy_->InvalidSyscall()->IsDeny()) { |
100 SANDBOX_DIE("Policies should deny invalid system calls."); | 100 SANDBOX_DIE("Policies should deny invalid system calls."); |
101 } | 101 } |
102 | 102 |
103 // If our BPF program has unsafe traps, enable support for them. | 103 // If our BPF program has unsafe traps, enable support for them. |
104 if (has_unsafe_traps_) { | 104 if (has_unsafe_traps_) { |
105 // As support for unsafe jumps essentially defeats all the security | 105 // As support for unsafe jumps essentially defeats all the security |
106 // measures that the sandbox provides, we print a big warning message -- | 106 // measures that the sandbox provides, we print a big warning message -- |
107 // and of course, we make sure to only ever enable this feature if it | 107 // and of course, we make sure to only ever enable this feature if it |
108 // is actually requested by the sandbox policy. | 108 // is actually requested by the sandbox policy. |
109 | 109 if (Syscall::Call(-1) == -1 && errno == ENOSYS) { |
110 CHECK_NE(0U, escapepc_) << "UnsafeTrap() requires a valid escape PC"; | 110 SANDBOX_DIE( |
| 111 "Support for UnsafeTrap() has not yet been ported to this " |
| 112 "architecture"); |
| 113 } |
111 | 114 |
112 for (int sysnum : kSyscallsRequiredForUnsafeTraps) { | 115 for (int sysnum : kSyscallsRequiredForUnsafeTraps) { |
113 if (!policy_->EvaluateSyscall(sysnum)->IsAllow()) { | 116 if (!policy_->EvaluateSyscall(sysnum)->IsAllow()) { |
114 SANDBOX_DIE( | 117 SANDBOX_DIE( |
115 "Policies that use UnsafeTrap() must unconditionally allow all " | 118 "Policies that use UnsafeTrap() must unconditionally allow all " |
116 "required system calls"); | 119 "required system calls"); |
117 } | 120 } |
118 } | 121 } |
119 | 122 |
120 if (!registry_->EnableUnsafeTraps()) { | 123 if (!registry_->EnableUnsafeTraps()) { |
121 // We should never be able to get here, as UnsafeTrap() should never | 124 // We should never be able to get here, as UnsafeTrap() should never |
122 // actually return a valid ErrorCode object unless the user set the | 125 // actually return a valid ErrorCode object unless the user set the |
123 // CHROME_SANDBOX_DEBUGGING environment variable; and therefore, | 126 // CHROME_SANDBOX_DEBUGGING environment variable; and therefore, |
124 // "has_unsafe_traps" would always be false. But better double-check | 127 // "has_unsafe_traps" would always be false. But better double-check |
125 // than enabling dangerous code. | 128 // than enabling dangerous code. |
126 SANDBOX_DIE("We'd rather die than enable unsafe traps"); | 129 SANDBOX_DIE("We'd rather die than enable unsafe traps"); |
127 } | 130 } |
128 } | 131 } |
129 | 132 |
130 // Assemble the BPF filter program. | 133 // Assemble the BPF filter program. |
131 scoped_ptr<CodeGen::Program> program(new CodeGen::Program()); | 134 scoped_ptr<CodeGen::Program> program(new CodeGen::Program()); |
132 gen_.Compile(AssemblePolicy(), program.get()); | 135 gen_.Compile(AssemblePolicy(), program.get()); |
133 return program.Pass(); | 136 return program.Pass(); |
134 } | 137 } |
135 | 138 |
136 void PolicyCompiler::DangerousSetEscapePC(uint64_t escapepc) { | |
137 escapepc_ = escapepc; | |
138 } | |
139 | |
140 CodeGen::Node PolicyCompiler::AssemblePolicy() { | 139 CodeGen::Node PolicyCompiler::AssemblePolicy() { |
141 // A compiled policy consists of three logical parts: | 140 // A compiled policy consists of three logical parts: |
142 // 1. Check that the "arch" field matches the expected architecture. | 141 // 1. Check that the "arch" field matches the expected architecture. |
143 // 2. If the policy involves unsafe traps, check if the syscall was | 142 // 2. If the policy involves unsafe traps, check if the syscall was |
144 // invoked by Syscall::Call, and then allow it unconditionally. | 143 // invoked by Syscall::Call, and then allow it unconditionally. |
145 // 3. Check the system call number and jump to the appropriate compiled | 144 // 3. Check the system call number and jump to the appropriate compiled |
146 // system call policy number. | 145 // system call policy number. |
147 return CheckArch(MaybeAddEscapeHatch(DispatchSyscall())); | 146 return CheckArch(MaybeAddEscapeHatch(DispatchSyscall())); |
148 } | 147 } |
149 | 148 |
150 CodeGen::Node PolicyCompiler::CheckArch(CodeGen::Node passed) { | 149 CodeGen::Node PolicyCompiler::CheckArch(CodeGen::Node passed) { |
151 // If the architecture doesn't match SECCOMP_ARCH, disallow the | 150 // If the architecture doesn't match SECCOMP_ARCH, disallow the |
152 // system call. | 151 // system call. |
153 return gen_.MakeInstruction( | 152 return gen_.MakeInstruction( |
154 BPF_LD + BPF_W + BPF_ABS, SECCOMP_ARCH_IDX, | 153 BPF_LD + BPF_W + BPF_ABS, SECCOMP_ARCH_IDX, |
155 gen_.MakeInstruction( | 154 gen_.MakeInstruction( |
156 BPF_JMP + BPF_JEQ + BPF_K, SECCOMP_ARCH, passed, | 155 BPF_JMP + BPF_JEQ + BPF_K, SECCOMP_ARCH, passed, |
157 CompileResult(Kill("Invalid audit architecture in BPF filter")))); | 156 CompileResult(Kill("Invalid audit architecture in BPF filter")))); |
158 } | 157 } |
159 | 158 |
160 CodeGen::Node PolicyCompiler::MaybeAddEscapeHatch(CodeGen::Node rest) { | 159 CodeGen::Node PolicyCompiler::MaybeAddEscapeHatch(CodeGen::Node rest) { |
161 // If no unsafe traps, then simply return |rest|. | 160 // If no unsafe traps, then simply return |rest|. |
162 if (!has_unsafe_traps_) { | 161 if (!has_unsafe_traps_) { |
163 return rest; | 162 return rest; |
164 } | 163 } |
165 | 164 |
166 // We already enabled unsafe traps in Compile, but enable them again to give | 165 // Allow system calls, if they originate from our magic return address |
167 // the trap registry a second chance to complain before we add the backdoor. | 166 // (which we can query by calling Syscall::Call(-1)). |
168 CHECK(registry_->EnableUnsafeTraps()); | 167 uint64_t syscall_entry_point = |
169 | 168 static_cast<uint64_t>(static_cast<uintptr_t>(Syscall::Call(-1))); |
170 // Allow system calls, if they originate from our magic return address. | 169 uint32_t low = static_cast<uint32_t>(syscall_entry_point); |
171 const uint32_t lopc = static_cast<uint32_t>(escapepc_); | 170 uint32_t hi = static_cast<uint32_t>(syscall_entry_point >> 32); |
172 const uint32_t hipc = static_cast<uint32_t>(escapepc_ >> 32); | |
173 | 171 |
174 // BPF cannot do native 64-bit comparisons, so we have to compare | 172 // BPF cannot do native 64-bit comparisons, so we have to compare |
175 // both 32-bit halves of the instruction pointer. If they match what | 173 // both 32-bit halves of the instruction pointer. If they match what |
176 // we expect, we return ERR_ALLOWED. If either or both don't match, | 174 // we expect, we return ERR_ALLOWED. If either or both don't match, |
177 // we continue evalutating the rest of the sandbox policy. | 175 // we continue evalutating the rest of the sandbox policy. |
178 // | 176 // |
179 // For simplicity, we check the full 64-bit instruction pointer even | 177 // For simplicity, we check the full 64-bit instruction pointer even |
180 // on 32-bit architectures. | 178 // on 32-bit architectures. |
181 return gen_.MakeInstruction( | 179 return gen_.MakeInstruction( |
182 BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_LSB_IDX, | 180 BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_LSB_IDX, |
183 gen_.MakeInstruction( | 181 gen_.MakeInstruction( |
184 BPF_JMP + BPF_JEQ + BPF_K, lopc, | 182 BPF_JMP + BPF_JEQ + BPF_K, low, |
185 gen_.MakeInstruction( | 183 gen_.MakeInstruction( |
186 BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_MSB_IDX, | 184 BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_MSB_IDX, |
187 gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, hipc, | 185 gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, hi, |
188 CompileResult(Allow()), rest)), | 186 CompileResult(Allow()), rest)), |
189 rest)); | 187 rest)); |
190 } | 188 } |
191 | 189 |
192 CodeGen::Node PolicyCompiler::DispatchSyscall() { | 190 CodeGen::Node PolicyCompiler::DispatchSyscall() { |
193 // Evaluate all possible system calls and group their ErrorCodes into | 191 // Evaluate all possible system calls and group their ErrorCodes into |
194 // ranges of identical codes. | 192 // ranges of identical codes. |
195 Ranges ranges; | 193 Ranges ranges; |
196 FindRanges(&ranges); | 194 FindRanges(&ranges); |
197 | 195 |
(...skipping 293 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
491 return ErrorCode(argno, | 489 return ErrorCode(argno, |
492 width, | 490 width, |
493 mask, | 491 mask, |
494 value, | 492 value, |
495 &*conds_.insert(passed).first, | 493 &*conds_.insert(passed).first, |
496 &*conds_.insert(failed).first); | 494 &*conds_.insert(failed).first); |
497 } | 495 } |
498 | 496 |
499 } // namespace bpf_dsl | 497 } // namespace bpf_dsl |
500 } // namespace sandbox | 498 } // namespace sandbox |
OLD | NEW |