diff options
Diffstat (limited to 'chromium/sandbox/linux/seccomp-bpf/codegen.cc')
-rw-r--r-- | chromium/sandbox/linux/seccomp-bpf/codegen.cc | 116 |
1 files changed, 68 insertions, 48 deletions
diff --git a/chromium/sandbox/linux/seccomp-bpf/codegen.cc b/chromium/sandbox/linux/seccomp-bpf/codegen.cc index 8fb1701179e..c90bffcad30 100644 --- a/chromium/sandbox/linux/seccomp-bpf/codegen.cc +++ b/chromium/sandbox/linux/seccomp-bpf/codegen.cc @@ -4,6 +4,7 @@ #include <stdio.h> +#include "base/logging.h" #include "sandbox/linux/seccomp-bpf/codegen.h" namespace { @@ -105,6 +106,8 @@ void CodeGen::PrintProgram(const SandboxBPF::Program& program) { fprintf(stderr, "Trap #%d\n", iter->k & SECCOMP_RET_DATA); } else if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) { fprintf(stderr, "errno = %d\n", iter->k & SECCOMP_RET_DATA); + } else if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRACE) { + fprintf(stderr, "Trace #%d\n", iter->k & SECCOMP_RET_DATA); } else if (iter->k == SECCOMP_RET_ALLOW) { fprintf(stderr, "Allowed\n"); } else { @@ -432,67 +435,84 @@ static int PointerCompare(const BasicBlock* block1, // We compare the sequence of instructions in both basic blocks. const Instructions& insns1 = block1->instructions; const Instructions& insns2 = block2->instructions; + // Basic blocks should never be empty. + CHECK(!insns1.empty()); + CHECK(!insns2.empty()); + Instructions::const_iterator iter1 = insns1.begin(); Instructions::const_iterator iter2 = insns2.begin(); for (;; ++iter1, ++iter2) { // If we have reached the end of the sequence of instructions in one or // both basic blocks, we know the relative ordering between the two blocks // and can return. - if (iter1 == insns1.end()) { - return iter2 == insns2.end() ? 0 : -1; - } else if (iter2 == insns2.end()) { - return 1; + if (iter1 == insns1.end() || iter2 == insns2.end()) { + if (iter1 != insns1.end()) { + return 1; + } + if (iter2 != insns2.end()) { + return -1; + } + + // If the two blocks are the same length (and have elementwise-equal code + // and k fields) and their last instructions are neither a JMP nor a RET + // (which is the only way we can reach this point), then we must compare + // their successors. + Instruction* const insns1_last = insns1.back(); + Instruction* const insns2_last = insns2.back(); + CHECK(BPF_CLASS(insns1_last->code) != BPF_JMP && + BPF_CLASS(insns1_last->code) != BPF_RET); + + // Non jumping instructions will always have a valid next instruction. + CHECK(insns1_last->next); + CHECK(insns2_last->next); + return PointerCompare(blocks.find(insns1_last->next)->second, + blocks.find(insns2_last->next)->second, + blocks); } // Compare the individual fields for both instructions. const Instruction& insn1 = **iter1; const Instruction& insn2 = **iter2; - if (insn1.code == insn2.code) { - if (insn1.k == insn2.k) { - // Only conditional jump instructions use the jt_ptr and jf_ptr - // fields. - if (BPF_CLASS(insn1.code) == BPF_JMP) { - if (BPF_OP(insn1.code) != BPF_JA) { - // Recursively compare the "true" and "false" branches. - // A well-formed BPF program can't have any cycles, so we know - // that our recursive algorithm will ultimately terminate. - // In the unlikely event that the programmer made a mistake and - // went out of the way to give us a cyclic program, we will crash - // with a stack overflow. We are OK with that. - int c = PointerCompare(blocks.find(insn1.jt_ptr)->second, - blocks.find(insn2.jt_ptr)->second, - blocks); - if (c == 0) { - c = PointerCompare(blocks.find(insn1.jf_ptr)->second, - blocks.find(insn2.jf_ptr)->second, - blocks); - if (c == 0) { - continue; - } else { - return c; - } - } else { - return c; - } - } else { - int c = PointerCompare(blocks.find(insn1.jt_ptr)->second, - blocks.find(insn2.jt_ptr)->second, - blocks); - if (c == 0) { - continue; - } else { - return c; - } - } - } else { - continue; - } - } else { - return insn1.k - insn2.k; - } - } else { + if (insn1.code != insn2.code) { return insn1.code - insn2.code; } + if (insn1.k != insn2.k) { + return insn1.k - insn2.k; + } + + // Sanity check: If we're looking at a JMP or RET instruction, by definition + // it should be the last instruction of the basic block. + if (BPF_CLASS(insn1.code) == BPF_JMP || BPF_CLASS(insn1.code) == BPF_RET) { + CHECK_EQ(insns1.back(), &insn1); + CHECK_EQ(insns2.back(), &insn2); + } + + // RET instructions terminate execution, and only JMP instructions use the + // jt_ptr and jf_ptr fields. Anything else can continue to the next + // instruction in the basic block. + if (BPF_CLASS(insn1.code) == BPF_RET) { + return 0; + } else if (BPF_CLASS(insn1.code) != BPF_JMP) { + continue; + } + + // Recursively compare the "true" and "false" branches. + // A well-formed BPF program can't have any cycles, so we know + // that our recursive algorithm will ultimately terminate. + // In the unlikely event that the programmer made a mistake and + // went out of the way to give us a cyclic program, we will crash + // with a stack overflow. We are OK with that. + if (BPF_OP(insn1.code) != BPF_JA) { + int c = PointerCompare(blocks.find(insn1.jf_ptr)->second, + blocks.find(insn2.jf_ptr)->second, + blocks); + if (c != 0) { + return c; + } + } + return PointerCompare(blocks.find(insn1.jt_ptr)->second, + blocks.find(insn2.jt_ptr)->second, + blocks); } } |