Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(289)

Side by Side Diff: sandbox/linux/seccomp-bpf/sandbox_bpf.cc

Issue 530133003: bpf_dsl: support arbitrary (arg & mask) == val expressions (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Reorder function definitions slightly Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 6
7 // Some headers on Android are missing cdefs: crbug.com/172337. 7 // Some headers on Android are missing cdefs: crbug.com/172337.
8 // (We can't use OS_ANDROID here since build_config.h is not included). 8 // (We can't use OS_ANDROID here since build_config.h is not included).
9 #if defined(ANDROID) 9 #if defined(ANDROID)
10 #include <sys/cdefs.h> 10 #include <sys/cdefs.h>
11 #endif 11 #endif
12 12
13 #include <errno.h> 13 #include <errno.h>
14 #include <fcntl.h> 14 #include <fcntl.h>
15 #include <string.h> 15 #include <string.h>
16 #include <sys/prctl.h> 16 #include <sys/prctl.h>
17 #include <sys/stat.h> 17 #include <sys/stat.h>
18 #include <sys/syscall.h> 18 #include <sys/syscall.h>
19 #include <sys/types.h> 19 #include <sys/types.h>
20 #include <time.h> 20 #include <time.h>
21 #include <unistd.h> 21 #include <unistd.h>
22 22
23 #include <limits>
24
23 #include "base/compiler_specific.h" 25 #include "base/compiler_specific.h"
24 #include "base/logging.h" 26 #include "base/logging.h"
25 #include "base/macros.h" 27 #include "base/macros.h"
26 #include "base/memory/scoped_ptr.h" 28 #include "base/memory/scoped_ptr.h"
27 #include "base/posix/eintr_wrapper.h" 29 #include "base/posix/eintr_wrapper.h"
28 #include "sandbox/linux/seccomp-bpf/codegen.h" 30 #include "sandbox/linux/seccomp-bpf/codegen.h"
29 #include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h" 31 #include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h"
30 #include "sandbox/linux/seccomp-bpf/syscall.h" 32 #include "sandbox/linux/seccomp-bpf/syscall.h"
31 #include "sandbox/linux/seccomp-bpf/syscall_iterator.h" 33 #include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
32 #include "sandbox/linux/seccomp-bpf/verifier.h" 34 #include "sandbox/linux/seccomp-bpf/verifier.h"
33 #include "sandbox/linux/services/linux_syscalls.h" 35 #include "sandbox/linux/services/linux_syscalls.h"
34 36
35 namespace sandbox { 37 namespace sandbox {
36 38
37 namespace { 39 namespace {
38 40
39 const int kExpectedExitCode = 100; 41 const int kExpectedExitCode = 100;
40 42
41 int popcount(uint32_t x) { 43 bool HasExactlyOneBit(uint64_t x) {
42 return __builtin_popcount(x); 44 // Common trick; e.g., see http://stackoverflow.com/a/108329.
45 return x != 0 && (x & (x - 1)) == 0;
43 } 46 }
44 47
45 #if !defined(NDEBUG) 48 #if !defined(NDEBUG)
46 void WriteFailedStderrSetupMessage(int out_fd) { 49 void WriteFailedStderrSetupMessage(int out_fd) {
47 const char* error_string = strerror(errno); 50 const char* error_string = strerror(errno);
48 static const char msg[] = 51 static const char msg[] =
49 "You have reproduced a puzzling issue.\n" 52 "You have reproduced a puzzling issue.\n"
50 "Please, report to crbug.com/152530!\n" 53 "Please, report to crbug.com/152530!\n"
51 "Failed to set up stderr: "; 54 "Failed to set up stderr: ";
52 if (HANDLE_EINTR(write(out_fd, msg, sizeof(msg) - 1)) > 0 && error_string && 55 if (HANDLE_EINTR(write(out_fd, msg, sizeof(msg) - 1)) > 0 && error_string &&
(...skipping 809 matching lines...) Expand 10 before | Expand all | Expand 10 after
862 } 865 }
863 866
864 Instruction* SandboxBPF::CondExpression(CodeGen* gen, const ErrorCode& cond) { 867 Instruction* SandboxBPF::CondExpression(CodeGen* gen, const ErrorCode& cond) {
865 // We can only inspect the six system call arguments that are passed in 868 // We can only inspect the six system call arguments that are passed in
866 // CPU registers. 869 // CPU registers.
867 if (cond.argno_ < 0 || cond.argno_ >= 6) { 870 if (cond.argno_ < 0 || cond.argno_ >= 6) {
868 SANDBOX_DIE( 871 SANDBOX_DIE(
869 "Internal compiler error; invalid argument number " 872 "Internal compiler error; invalid argument number "
870 "encountered"); 873 "encountered");
871 } 874 }
872 875 if (cond.width_ != ErrorCode::TP_32BIT &&
873 // BPF programs operate on 32bit entities. Load both halfs of the 64bit 876 cond.width_ != ErrorCode::TP_64BIT) {
874 // system call argument and then generate suitable conditional statements. 877 SANDBOX_DIE("Internal compiler error; invalid argument width");
875 Instruction* msb_head = gen->MakeInstruction( 878 }
876 BPF_LD + BPF_W + BPF_ABS, SECCOMP_ARG_MSB_IDX(cond.argno_)); 879 if ((cond.value_ & cond.mask_) != cond.value_) {
877 Instruction* msb_tail = msb_head; 880 SANDBOX_DIE("Internal compiler error; invalid masked value");
878 Instruction* lsb_head = gen->MakeInstruction(
879 BPF_LD + BPF_W + BPF_ABS, SECCOMP_ARG_LSB_IDX(cond.argno_));
880 Instruction* lsb_tail = lsb_head;
881
882 // Emit a suitable comparison statement.
883 switch (cond.op_) {
884 case ErrorCode::OP_EQUAL:
885 // Compare the least significant bits for equality
886 lsb_tail = gen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K,
887 static_cast<uint32_t>(cond.value_),
888 RetExpression(gen, *cond.passed_),
889 RetExpression(gen, *cond.failed_));
890 gen->JoinInstructions(lsb_head, lsb_tail);
891
892 // If we are looking at a 64bit argument, we need to also compare the
893 // most significant bits.
894 if (cond.width_ == ErrorCode::TP_64BIT) {
895 msb_tail =
896 gen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K,
897 static_cast<uint32_t>(cond.value_ >> 32),
898 lsb_head,
899 RetExpression(gen, *cond.failed_));
900 gen->JoinInstructions(msb_head, msb_tail);
901 }
902 break;
903 case ErrorCode::OP_HAS_ALL_BITS:
904 // Check the bits in the LSB half of the system call argument. Our
905 // OP_HAS_ALL_BITS operator passes, iff all of the bits are set. This is
906 // different from the kernel's BPF_JSET operation which passes, if any of
907 // the bits are set.
908 // Of course, if there is only a single set bit (or none at all), then
909 // things get easier.
910 {
911 uint32_t lsb_bits = static_cast<uint32_t>(cond.value_);
912 int lsb_bit_count = popcount(lsb_bits);
913 if (lsb_bit_count == 0) {
914 // No bits are set in the LSB half. The test will always pass.
915 lsb_head = RetExpression(gen, *cond.passed_);
916 lsb_tail = NULL;
917 } else if (lsb_bit_count == 1) {
918 // Exactly one bit is set in the LSB half. We can use the BPF_JSET
919 // operator.
920 lsb_tail = gen->MakeInstruction(BPF_JMP + BPF_JSET + BPF_K,
921 lsb_bits,
922 RetExpression(gen, *cond.passed_),
923 RetExpression(gen, *cond.failed_));
924 gen->JoinInstructions(lsb_head, lsb_tail);
925 } else {
926 // More than one bit is set in the LSB half. We need to combine
927 // BPF_AND and BPF_JEQ to test whether all of these bits are in fact
928 // set in the system call argument.
929 gen->JoinInstructions(
930 lsb_head,
931 gen->MakeInstruction(BPF_ALU + BPF_AND + BPF_K,
932 lsb_bits,
933 lsb_tail = gen->MakeInstruction(
934 BPF_JMP + BPF_JEQ + BPF_K,
935 lsb_bits,
936 RetExpression(gen, *cond.passed_),
937 RetExpression(gen, *cond.failed_))));
938 }
939 }
940
941 // If we are looking at a 64bit argument, we need to also check the bits
942 // in the MSB half of the system call argument.
943 if (cond.width_ == ErrorCode::TP_64BIT) {
944 uint32_t msb_bits = static_cast<uint32_t>(cond.value_ >> 32);
945 int msb_bit_count = popcount(msb_bits);
946 if (msb_bit_count == 0) {
947 // No bits are set in the MSB half. The test will always pass.
948 msb_head = lsb_head;
949 } else if (msb_bit_count == 1) {
950 // Exactly one bit is set in the MSB half. We can use the BPF_JSET
951 // operator.
952 msb_tail = gen->MakeInstruction(BPF_JMP + BPF_JSET + BPF_K,
953 msb_bits,
954 lsb_head,
955 RetExpression(gen, *cond.failed_));
956 gen->JoinInstructions(msb_head, msb_tail);
957 } else {
958 // More than one bit is set in the MSB half. We need to combine
959 // BPF_AND and BPF_JEQ to test whether all of these bits are in fact
960 // set in the system call argument.
961 gen->JoinInstructions(
962 msb_head,
963 gen->MakeInstruction(
964 BPF_ALU + BPF_AND + BPF_K,
965 msb_bits,
966 gen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K,
967 msb_bits,
968 lsb_head,
969 RetExpression(gen, *cond.failed_))));
970 }
971 }
972 break;
973 case ErrorCode::OP_HAS_ANY_BITS:
974 // Check the bits in the LSB half of the system call argument. Our
975 // OP_HAS_ANY_BITS operator passes, iff any of the bits are set. This maps
976 // nicely to the kernel's BPF_JSET operation.
977 {
978 uint32_t lsb_bits = static_cast<uint32_t>(cond.value_);
979 if (!lsb_bits) {
980 // No bits are set in the LSB half. The test will always fail.
981 lsb_head = RetExpression(gen, *cond.failed_);
982 lsb_tail = NULL;
983 } else {
984 lsb_tail = gen->MakeInstruction(BPF_JMP + BPF_JSET + BPF_K,
985 lsb_bits,
986 RetExpression(gen, *cond.passed_),
987 RetExpression(gen, *cond.failed_));
988 gen->JoinInstructions(lsb_head, lsb_tail);
989 }
990 }
991
992 // If we are looking at a 64bit argument, we need to also check the bits
993 // in the MSB half of the system call argument.
994 if (cond.width_ == ErrorCode::TP_64BIT) {
995 uint32_t msb_bits = static_cast<uint32_t>(cond.value_ >> 32);
996 if (!msb_bits) {
997 // No bits are set in the MSB half. The test will always fail.
998 msb_head = lsb_head;
999 } else {
1000 msb_tail = gen->MakeInstruction(BPF_JMP + BPF_JSET + BPF_K,
1001 msb_bits,
1002 RetExpression(gen, *cond.passed_),
1003 lsb_head);
1004 gen->JoinInstructions(msb_head, msb_tail);
1005 }
1006 }
1007 break;
1008 default:
1009 // TODO(markus): Need to add support for OP_GREATER
1010 SANDBOX_DIE("Not implemented");
1011 break;
1012 } 881 }
1013 882
1014 // Ensure that we never pass a 64bit value, when we only expect a 32bit 883 Instruction* passed = RetExpression(gen, *cond.passed_);
1015 // value. This is somewhat complicated by the fact that on 64bit systems, 884 Instruction* failed = RetExpression(gen, *cond.failed_);
1016 // callers could legitimately pass in a non-zero value in the MSB, iff the 885
1017 // LSB has been sign-extended into the MSB. 886 // We want to emit code to check "(arg & mask) == value" where arg, mask, and
1018 if (cond.width_ == ErrorCode::TP_32BIT) { 887 // value are 64-bit values, but the BPF machine is only 32-bit. We implement
1019 if (cond.value_ >> 32) { 888 // this by independently testing the upper and lower 32-bits and continuing to
889 // "passed" if both evaluate true, or to "failed" if either evaluate false.
890 return CondExpressionHalf(
891 gen,
892 cond,
893 UpperHalf,
894 CondExpressionHalf(gen, cond, LowerHalf, passed, failed),
895 failed);
896 }
897
898 Instruction* SandboxBPF::CondExpressionHalf(CodeGen* gen,
899 const ErrorCode& cond,
900 ArgHalf half,
901 Instruction* passed,
902 Instruction* failed) {
903 if (cond.width_ == ErrorCode::TP_32BIT && half == UpperHalf) {
904 // Special logic for sanity checking the upper 32-bits of 32-bit system
905 // call arguments.
906
907 if ((cond.mask_ >> 32) != 0) {
1020 SANDBOX_DIE( 908 SANDBOX_DIE(
1021 "Invalid comparison of a 32bit system call argument " 909 "Invalid comparison of a 32bit system call argument "
1022 "against a 64bit constant; this test is always false."); 910 "against a 64bit constant; this test is always false.");
1023 } 911 }
1024 912
913 // TODO(mdempsky): Compile Unexpected64bitArgument() just per program.
1025 Instruction* invalid_64bit = RetExpression(gen, Unexpected64bitArgument()); 914 Instruction* invalid_64bit = RetExpression(gen, Unexpected64bitArgument());
1026 #if __SIZEOF_POINTER__ > 4 915
1027 invalid_64bit = gen->MakeInstruction( 916 const uint32_t upper = SECCOMP_ARG_MSB_IDX(cond.argno_);
1028 BPF_JMP + BPF_JEQ + BPF_K, 917 const uint32_t lower = SECCOMP_ARG_LSB_IDX(cond.argno_);
1029 0xFFFFFFFF, 918
1030 gen->MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 919 if (sizeof(void*) == 4) {
1031 SECCOMP_ARG_LSB_IDX(cond.argno_), 920 // On 32-bit platforms, the upper 32-bits should always be 0:
jln (very slow on Chromium) 2014/09/04 02:08:36 I think it would be redundant with your previous c
mdempsky 2014/09/04 17:26:48 Yeah, it should be redundant, but I went ahead and
1032 gen->MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, 921 // LDW [upper]
1033 0x80000000, 922 // JEQ 0, passed, invalid
1034 lsb_head, 923 return gen->MakeInstruction(
1035 invalid_64bit)), 924 BPF_LD + BPF_W + BPF_ABS,
1036 invalid_64bit); 925 upper,
1037 #endif 926 gen->MakeInstruction(
1038 gen->JoinInstructions( 927 BPF_JMP + BPF_JEQ + BPF_K, 0, passed, invalid_64bit));
1039 msb_tail, 928 }
929
930 // On 64-bit platforms, the upper 32-bits may be 0 or ~0; but we only allow
931 // ~0 if the sign bit of the lower 32-bits is set too:
932 // LDW [upper]
933 // JEQ 0, passed, (next)
934 // JEQ ~0, (next), invalid
935 // LDW [lower]
936 // JSET (1<<31), passed, invalid
937 //
938 // TODO(mdempsky): The JSET instruction could perhaps jump to passed->next
939 // instead, as the first instruction of passed should be "LDW [lower]".
940 return gen->MakeInstruction(
941 BPF_LD + BPF_W + BPF_ABS,
942 upper,
1040 gen->MakeInstruction( 943 gen->MakeInstruction(
1041 BPF_JMP + BPF_JEQ + BPF_K, 0, lsb_head, invalid_64bit)); 944 BPF_JMP + BPF_JEQ + BPF_K,
945 0,
946 passed,
947 gen->MakeInstruction(
948 BPF_JMP + BPF_JEQ + BPF_K,
949 std::numeric_limits<uint32_t>::max(),
950 gen->MakeInstruction(
951 BPF_LD + BPF_W + BPF_ABS,
952 lower,
953 gen->MakeInstruction(BPF_JMP + BPF_JSET + BPF_K,
954 1U << 31,
955 passed,
956 invalid_64bit)),
957 invalid_64bit)));
1042 } 958 }
1043 959
1044 return msb_head; 960 const uint32_t idx = (half == UpperHalf) ? SECCOMP_ARG_MSB_IDX(cond.argno_)
961 : SECCOMP_ARG_LSB_IDX(cond.argno_);
962 const uint32_t mask = (half == UpperHalf) ? cond.mask_ >> 32 : cond.mask_;
963 const uint32_t value = (half == UpperHalf) ? cond.value_ >> 32 : cond.value_;
964
965 // Emit a suitable instruction sequence for (arg & mask) == value.
966
967 // For (arg & 0) == 0, just return passed.
968 if (mask == 0) {
969 return passed;
jln (very slow on Chromium) 2014/09/04 02:08:35 Shouldn't you assert that value == 0 here again? T
mdempsky 2014/09/04 17:26:48 Fair enough, done.
970 }
971
972 // For (arg & ~0) == value, emit:
973 // LDW [idx]
974 // JEQ value, passed, failed
975 if (mask == std::numeric_limits<uint32_t>::max()) {
976 return gen->MakeInstruction(
977 BPF_LD + BPF_W + BPF_ABS,
978 idx,
979 gen->MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed));
980 }
981
982 // For (arg & mask) == 0, emit:
983 // LDW [idx]
984 // JSET mask, failed, passed
985 // (Note: failed and passed are intentionally swapped.)
986 if (value == 0) {
987 return gen->MakeInstruction(
988 BPF_LD + BPF_W + BPF_ABS,
989 idx,
990 gen->MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, mask, failed, passed));
991 }
992
993 // For (arg & x) == x where x is a single-bit value, emit:
994 // LDW [idx]
995 // JSET mask, passed, failed
996 if (mask == value && HasExactlyOneBit(mask)) {
997 return gen->MakeInstruction(
998 BPF_LD + BPF_W + BPF_ABS,
999 idx,
1000 gen->MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, mask, passed, failed));
1001 }
1002
1003 // Generic fallback:
1004 // LDW [idx]
1005 // AND mask
1006 // JEQ value, passed, failed
1007 return gen->MakeInstruction(
1008 BPF_LD + BPF_W + BPF_ABS,
1009 idx,
1010 gen->MakeInstruction(
1011 BPF_ALU + BPF_AND + BPF_K,
1012 mask,
1013 gen->MakeInstruction(
1014 BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed)));
1045 } 1015 }
1046 1016
1047 ErrorCode SandboxBPF::Unexpected64bitArgument() { 1017 ErrorCode SandboxBPF::Unexpected64bitArgument() {
1048 return Kill("Unexpected 64bit argument detected"); 1018 return Kill("Unexpected 64bit argument detected");
1049 } 1019 }
1050 1020
1051 ErrorCode SandboxBPF::Trap(Trap::TrapFnc fnc, const void* aux) { 1021 ErrorCode SandboxBPF::Trap(Trap::TrapFnc fnc, const void* aux) {
1052 return Trap::MakeTrap(fnc, aux, true /* Safe Trap */); 1022 return Trap::MakeTrap(fnc, aux, true /* Safe Trap */);
1053 } 1023 }
1054 1024
(...skipping 17 matching lines...) Expand all
1072 intptr_t SandboxBPF::ForwardSyscall(const struct arch_seccomp_data& args) { 1042 intptr_t SandboxBPF::ForwardSyscall(const struct arch_seccomp_data& args) {
1073 return Syscall::Call(args.nr, 1043 return Syscall::Call(args.nr,
1074 static_cast<intptr_t>(args.args[0]), 1044 static_cast<intptr_t>(args.args[0]),
1075 static_cast<intptr_t>(args.args[1]), 1045 static_cast<intptr_t>(args.args[1]),
1076 static_cast<intptr_t>(args.args[2]), 1046 static_cast<intptr_t>(args.args[2]),
1077 static_cast<intptr_t>(args.args[3]), 1047 static_cast<intptr_t>(args.args[3]),
1078 static_cast<intptr_t>(args.args[4]), 1048 static_cast<intptr_t>(args.args[4]),
1079 static_cast<intptr_t>(args.args[5])); 1049 static_cast<intptr_t>(args.args[5]));
1080 } 1050 }
1081 1051
1052 ErrorCode SandboxBPF::CondMaskedEqual(int argno,
1053 ErrorCode::ArgType width,
1054 uint64_t mask,
1055 uint64_t value,
1056 const ErrorCode& passed,
1057 const ErrorCode& failed) {
1058 return ErrorCode(argno,
1059 width,
1060 mask,
1061 value,
1062 &*conds_->insert(passed).first,
1063 &*conds_->insert(failed).first);
1064 }
1065
1082 ErrorCode SandboxBPF::Cond(int argno, 1066 ErrorCode SandboxBPF::Cond(int argno,
1083 ErrorCode::ArgType width, 1067 ErrorCode::ArgType width,
1084 ErrorCode::Operation op, 1068 ErrorCode::Operation op,
1085 uint64_t value, 1069 uint64_t value,
1086 const ErrorCode& passed, 1070 const ErrorCode& passed,
1087 const ErrorCode& failed) { 1071 const ErrorCode& failed) {
1088 return ErrorCode(argno, 1072 switch (op) {
1089 width, 1073 case ErrorCode::OP_EQUAL: {
1090 op, 1074 // Convert to "(arg & ~0) == value".
1091 value, 1075 const uint64_t mask = (width == ErrorCode::TP_64BIT)
1092 &*conds_->insert(passed).first, 1076 ? std::numeric_limits<uint64_t>::max()
1093 &*conds_->insert(failed).first); 1077 : std::numeric_limits<uint32_t>::max();
1078 return CondMaskedEqual(argno, width, mask, value, passed, failed);
1079 }
1080 case ErrorCode::OP_HAS_ALL_BITS:
1081 // Convert to "(arg & value) == value".
1082 return CondMaskedEqual(argno, width, value, value, passed, failed);
1083 case ErrorCode::OP_HAS_ANY_BITS:
1084 // Convert to "(arg & value) == 0", but swap passed and failed.
1085 return CondMaskedEqual(argno, width, value, 0, failed, passed);
1086 default:
1087 SANDBOX_DIE("Not implemented");
1088 }
1094 } 1089 }
1095 1090
1096 ErrorCode SandboxBPF::Kill(const char* msg) { 1091 ErrorCode SandboxBPF::Kill(const char* msg) {
1097 return Trap(BPFFailure, const_cast<char*>(msg)); 1092 return Trap(BPFFailure, const_cast<char*>(msg));
1098 } 1093 }
1099 1094
1100 SandboxBPF::SandboxStatus SandboxBPF::status_ = STATUS_UNKNOWN; 1095 SandboxBPF::SandboxStatus SandboxBPF::status_ = STATUS_UNKNOWN;
1101 1096
1102 } // namespace sandbox 1097 } // namespace sandbox
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698