mirror of
https://github.com/google/sandboxed-api.git
synced 2024-03-22 13:11:30 +08:00
Safer and more efficient custom syscall policies
Generate syscall jump table without using bpf_helper. Check that any jump in the user provided policy is within the provided policy. PiperOrigin-RevId: 409362089 Change-Id: I31493e52cf868e4b184ff79fcb26beeb75f49773
This commit is contained in:
parent
c95837a6c1
commit
26da6e6b0a
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include <csignal>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
|
@ -59,6 +60,16 @@ constexpr PolicyBuilder::SyscallInitializer kMmapSyscalls = {
|
|||
#endif
|
||||
};
|
||||
|
||||
bool CheckBpfBounds(const sock_filter& filter, size_t max_jmp) {
|
||||
if (BPF_CLASS(filter.code) == BPF_JMP) {
|
||||
if (BPF_OP(filter.code) == BPF_JA) {
|
||||
return filter.k <= max_jmp;
|
||||
}
|
||||
return filter.jt <= max_jmp && filter.jf <= max_jmp;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PolicyBuilder& PolicyBuilder::AllowSyscall(unsigned int num) {
|
||||
|
@ -691,36 +702,61 @@ PolicyBuilder& PolicyBuilder::AddPolicyOnSyscall(unsigned int num, BpfFunc f) {
|
|||
|
||||
PolicyBuilder& PolicyBuilder::AddPolicyOnSyscalls(
|
||||
SyscallInitializer nums, const std::vector<sock_filter>& policy) {
|
||||
auto resolved_policy =
|
||||
ResolveBpfFunc(
|
||||
[nums, policy](bpf_labels& labels) -> std::vector<sock_filter> {
|
||||
std::vector<sock_filter> out;
|
||||
out.reserve(nums.size() + policy.size());
|
||||
for (auto num : nums) {
|
||||
out.insert(out.end(), {SYSCALL(num, JUMP(&labels, do_policy_l))});
|
||||
}
|
||||
out.insert(out.end(), {JUMP(&labels, dont_do_policy_l),
|
||||
LABEL(&labels, do_policy_l)});
|
||||
for (const auto& filter : policy) {
|
||||
// Syscall arch is expected as TRACE value
|
||||
if (filter.code == (BPF_RET | BPF_K) &&
|
||||
(filter.k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRACE &&
|
||||
(filter.k & SECCOMP_RET_DATA) != Syscall::GetHostArch()) {
|
||||
LOG(WARNING)
|
||||
<< "SANDBOX2_TRACE should be used in policy instead of "
|
||||
"TRACE(value)";
|
||||
out.push_back(SANDBOX2_TRACE);
|
||||
} else {
|
||||
out.push_back(filter);
|
||||
}
|
||||
}
|
||||
out.push_back(LOAD_SYSCALL_NR);
|
||||
out.insert(out.end(), {LABEL(&labels, dont_do_policy_l)});
|
||||
return out;
|
||||
});
|
||||
std::deque<sock_filter> out;
|
||||
// Insert and verify the policy.
|
||||
out.insert(out.end(), policy.begin(), policy.end());
|
||||
for (size_t i = 0; i < out.size(); ++i) {
|
||||
sock_filter& filter = out[i];
|
||||
const size_t max_jump = out.size() - i - 1;
|
||||
if (!CheckBpfBounds(filter, max_jump)) {
|
||||
SetError(absl::InvalidArgumentError("bpf jump out of bounds"));
|
||||
return *this;
|
||||
}
|
||||
// Syscall arch is expected as TRACE value
|
||||
if (filter.code == (BPF_RET | BPF_K) &&
|
||||
(filter.k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRACE &&
|
||||
(filter.k & SECCOMP_RET_DATA) != Syscall::GetHostArch()) {
|
||||
LOG(WARNING) << "SANDBOX2_TRACE should be used in policy instead of "
|
||||
"TRACE(value)";
|
||||
filter = SANDBOX2_TRACE;
|
||||
}
|
||||
}
|
||||
// Pre-/Postcondition: Syscall number loaded into A register
|
||||
user_policy_.insert(user_policy_.end(), resolved_policy.begin(),
|
||||
resolved_policy.end());
|
||||
out.push_back(LOAD_SYSCALL_NR);
|
||||
if (out.size() > std::numeric_limits<uint32_t>::max()) {
|
||||
SetError(absl::InvalidArgumentError("syscall policy is too long"));
|
||||
return *this;
|
||||
}
|
||||
// Create jumps for each syscall.
|
||||
size_t do_policy_loc = out.size();
|
||||
// Iterate in reverse order and prepend instruction, so that jumps can be
|
||||
// calculated easily.
|
||||
constexpr size_t kMaxShortJump = 255;
|
||||
bool last = true;
|
||||
for (auto it = std::rbegin(nums); it != std::rend(nums); ++it) {
|
||||
// If syscall is not matched try with the next one.
|
||||
uint8_t jf = 0;
|
||||
// If last syscall on the list does not match skip the policy by jumping
|
||||
// over it.
|
||||
if (last) {
|
||||
if (out.size() > kMaxShortJump) {
|
||||
out.push_front(BPF_STMT(BPF_JMP + BPF_JA, out.size()));
|
||||
} else {
|
||||
jf = out.size();
|
||||
}
|
||||
last = false;
|
||||
}
|
||||
// Add a helper absolute jump if needed - the policy/last helper jump is
|
||||
// out of reach of a short jump.
|
||||
if ((out.size() - do_policy_loc) > kMaxShortJump) {
|
||||
out.push_front(BPF_STMT(BPF_JMP + BPF_JA, out.size() - policy.size()));
|
||||
do_policy_loc = out.size();
|
||||
++jf;
|
||||
}
|
||||
uint8_t jt = out.size() - do_policy_loc;
|
||||
out.push_front(BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, *it, jt, jf));
|
||||
}
|
||||
user_policy_.insert(user_policy_.end(), out.begin(), out.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user