Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
michael@0 | 2 | // Use of this source code is governed by a BSD-style license that can be |
michael@0 | 3 | // found in the LICENSE file. |
michael@0 | 4 | |
michael@0 | 5 | #include <string> |
michael@0 | 6 | #include <map> |
michael@0 | 7 | |
michael@0 | 8 | #include "sandbox/win/src/policy_low_level.h" |
michael@0 | 9 | #include "base/basictypes.h" |
michael@0 | 10 | |
michael@0 | 11 | namespace { |
michael@0 | 12 | |
michael@0 | 13 | // A single rule can use at most this amount of memory. |
michael@0 | 14 | const size_t kRuleBufferSize = 1024*4; |
michael@0 | 15 | |
michael@0 | 16 | // The possible states of the string matching opcode generator. |
michael@0 | 17 | enum { |
michael@0 | 18 | PENDING_NONE, |
michael@0 | 19 | PENDING_ASTERISK, // Have seen an '*' but have not generated an opcode. |
michael@0 | 20 | PENDING_QMARK, // Have seen an '?' but have not generated an opcode. |
michael@0 | 21 | }; |
michael@0 | 22 | |
michael@0 | 23 | // The category of the last character seen by the string matching opcode |
michael@0 | 24 | // generator. |
michael@0 | 25 | const uint32 kLastCharIsNone = 0; |
michael@0 | 26 | const uint32 kLastCharIsAlpha = 1; |
michael@0 | 27 | const uint32 kLastCharIsWild = 2; |
michael@0 | 28 | const uint32 kLastCharIsAsterisk = kLastCharIsWild + 4; |
michael@0 | 29 | const uint32 kLastCharIsQuestionM = kLastCharIsWild + 8; |
michael@0 | 30 | } |
michael@0 | 31 | |
michael@0 | 32 | namespace sandbox { |
michael@0 | 33 | |
michael@0 | 34 | // Adding a rule is nothing more than pushing it into an stl container. Done() |
michael@0 | 35 | // is called for the rule in case the code that made the rule in the first |
michael@0 | 36 | // place has not done it. |
michael@0 | 37 | bool LowLevelPolicy::AddRule(int service, PolicyRule* rule) { |
michael@0 | 38 | if (!rule->Done()) { |
michael@0 | 39 | return false; |
michael@0 | 40 | } |
michael@0 | 41 | |
michael@0 | 42 | PolicyRule* local_rule = new PolicyRule(*rule); |
michael@0 | 43 | RuleNode node = {local_rule, service}; |
michael@0 | 44 | rules_.push_back(node); |
michael@0 | 45 | return true; |
michael@0 | 46 | } |
michael@0 | 47 | |
michael@0 | 48 | LowLevelPolicy::~LowLevelPolicy() { |
michael@0 | 49 | // Delete all the rules. |
michael@0 | 50 | typedef std::list<RuleNode> RuleNodes; |
michael@0 | 51 | for (RuleNodes::iterator it = rules_.begin(); it != rules_.end(); ++it) { |
michael@0 | 52 | delete it->rule; |
michael@0 | 53 | } |
michael@0 | 54 | } |
michael@0 | 55 | |
michael@0 | 56 | // Here is where the heavy byte shuffling is done. We take all the rules and |
michael@0 | 57 | // 'compile' them into a single memory region. Now, the rules are in random |
michael@0 | 58 | // order so the first step is to reorganize them into a stl map that is keyed |
michael@0 | 59 | // by the service id and as a value contains a list with all the rules that |
michael@0 | 60 | // belong to that service. Then we enter the big for-loop where we carve a |
michael@0 | 61 | // memory zone for the opcodes and the data and call RebindCopy on each rule |
michael@0 | 62 | // so they all end up nicely packed in the policy_store_. |
michael@0 | 63 | bool LowLevelPolicy::Done() { |
michael@0 | 64 | typedef std::list<RuleNode> RuleNodes; |
michael@0 | 65 | typedef std::list<const PolicyRule*> RuleList; |
michael@0 | 66 | typedef std::map<uint32, RuleList> Mmap; |
michael@0 | 67 | Mmap mmap; |
michael@0 | 68 | |
michael@0 | 69 | for (RuleNodes::iterator it = rules_.begin(); it != rules_.end(); ++it) { |
michael@0 | 70 | mmap[it->service].push_back(it->rule); |
michael@0 | 71 | } |
michael@0 | 72 | |
michael@0 | 73 | PolicyBuffer* current_buffer = &policy_store_->data[0]; |
michael@0 | 74 | char* buffer_end = reinterpret_cast<char*>(current_buffer) + |
michael@0 | 75 | policy_store_->data_size; |
michael@0 | 76 | size_t avail_size = policy_store_->data_size; |
michael@0 | 77 | |
michael@0 | 78 | for (Mmap::iterator it = mmap.begin(); it != mmap.end(); ++it) { |
michael@0 | 79 | uint32 service = (*it).first; |
michael@0 | 80 | if (service >= kMaxServiceCount) { |
michael@0 | 81 | return false; |
michael@0 | 82 | } |
michael@0 | 83 | policy_store_->entry[service] = current_buffer; |
michael@0 | 84 | |
michael@0 | 85 | RuleList::iterator rules_it = (*it).second.begin(); |
michael@0 | 86 | RuleList::iterator rules_it_end = (*it).second.end(); |
michael@0 | 87 | |
michael@0 | 88 | size_t svc_opcode_count = 0; |
michael@0 | 89 | |
michael@0 | 90 | for (; rules_it != rules_it_end; ++rules_it) { |
michael@0 | 91 | const PolicyRule* rule = (*rules_it); |
michael@0 | 92 | size_t op_count = rule->GetOpcodeCount(); |
michael@0 | 93 | |
michael@0 | 94 | size_t opcodes_size = op_count * sizeof(PolicyOpcode); |
michael@0 | 95 | if (avail_size < opcodes_size) { |
michael@0 | 96 | return false; |
michael@0 | 97 | } |
michael@0 | 98 | size_t data_size = avail_size - opcodes_size; |
michael@0 | 99 | PolicyOpcode* opcodes_start = ¤t_buffer->opcodes[svc_opcode_count]; |
michael@0 | 100 | if (!rule->RebindCopy(opcodes_start, opcodes_size, |
michael@0 | 101 | buffer_end, &data_size)) { |
michael@0 | 102 | return false; |
michael@0 | 103 | } |
michael@0 | 104 | size_t used = avail_size - data_size; |
michael@0 | 105 | buffer_end -= used; |
michael@0 | 106 | avail_size -= used; |
michael@0 | 107 | svc_opcode_count += op_count; |
michael@0 | 108 | } |
michael@0 | 109 | |
michael@0 | 110 | current_buffer->opcode_count += svc_opcode_count; |
michael@0 | 111 | size_t policy_byte_count = (svc_opcode_count * sizeof(PolicyOpcode)) |
michael@0 | 112 | / sizeof(current_buffer[0]); |
michael@0 | 113 | current_buffer = ¤t_buffer[policy_byte_count + 1]; |
michael@0 | 114 | } |
michael@0 | 115 | |
michael@0 | 116 | return true; |
michael@0 | 117 | } |
michael@0 | 118 | |
michael@0 | 119 | PolicyRule::PolicyRule(EvalResult action) |
michael@0 | 120 | : action_(action), done_(false) { |
michael@0 | 121 | char* memory = new char[sizeof(PolicyBuffer) + kRuleBufferSize]; |
michael@0 | 122 | buffer_ = reinterpret_cast<PolicyBuffer*>(memory); |
michael@0 | 123 | buffer_->opcode_count = 0; |
michael@0 | 124 | opcode_factory_ = new OpcodeFactory(buffer_, |
michael@0 | 125 | kRuleBufferSize + sizeof(PolicyOpcode)); |
michael@0 | 126 | } |
michael@0 | 127 | |
michael@0 | 128 | PolicyRule::PolicyRule(const PolicyRule& other) { |
michael@0 | 129 | if (this == &other) |
michael@0 | 130 | return; |
michael@0 | 131 | action_ = other.action_; |
michael@0 | 132 | done_ = other.done_; |
michael@0 | 133 | size_t buffer_size = sizeof(PolicyBuffer) + kRuleBufferSize; |
michael@0 | 134 | char* memory = new char[buffer_size]; |
michael@0 | 135 | buffer_ = reinterpret_cast<PolicyBuffer*>(memory); |
michael@0 | 136 | memcpy(buffer_, other.buffer_, buffer_size); |
michael@0 | 137 | |
michael@0 | 138 | char* opcode_buffer = reinterpret_cast<char*>(&buffer_->opcodes[0]); |
michael@0 | 139 | char* next_opcode = &opcode_buffer[GetOpcodeCount() * sizeof(PolicyOpcode)]; |
michael@0 | 140 | opcode_factory_ = |
michael@0 | 141 | new OpcodeFactory(next_opcode, other.opcode_factory_->memory_size()); |
michael@0 | 142 | } |
michael@0 | 143 | |
michael@0 | 144 | // This function get called from a simple state machine implemented in |
michael@0 | 145 | // AddStringMatch() which passes the current state (in state) and it passes |
michael@0 | 146 | // true in last_call if AddStringMatch() has finished processing the input |
michael@0 | 147 | // pattern string and this would be the last call to generate any pending |
michael@0 | 148 | // opcode. The skip_count is the currently accumulated number of '?' seen so |
michael@0 | 149 | // far and once the associated opcode is generated this function sets it back |
michael@0 | 150 | // to zero. |
michael@0 | 151 | bool PolicyRule::GenStringOpcode(RuleType rule_type, |
michael@0 | 152 | StringMatchOptions match_opts, |
michael@0 | 153 | uint16 parameter, int state, bool last_call, |
michael@0 | 154 | int* skip_count, std::wstring* fragment) { |
michael@0 | 155 | |
michael@0 | 156 | // The last opcode must: |
michael@0 | 157 | // 1) Always clear the context. |
michael@0 | 158 | // 2) Preserve the negation. |
michael@0 | 159 | // 3) Remove the 'OR' mode flag. |
michael@0 | 160 | uint32 options = kPolNone; |
michael@0 | 161 | if (last_call) { |
michael@0 | 162 | if (IF_NOT == rule_type) { |
michael@0 | 163 | options = kPolClearContext | kPolNegateEval; |
michael@0 | 164 | } else { |
michael@0 | 165 | options = kPolClearContext; |
michael@0 | 166 | } |
michael@0 | 167 | } else if (IF_NOT == rule_type) { |
michael@0 | 168 | options = kPolUseOREval | kPolNegateEval; |
michael@0 | 169 | } |
michael@0 | 170 | |
michael@0 | 171 | PolicyOpcode* op = NULL; |
michael@0 | 172 | |
michael@0 | 173 | // The fragment string contains the accumulated characters to match with, it |
michael@0 | 174 | // never contains wildcards (unless they have been escaped) and while there |
michael@0 | 175 | // is no fragment there is no new string match opcode to generate. |
michael@0 | 176 | if (fragment->empty()) { |
michael@0 | 177 | // There is no new opcode to generate but in the last call we have to fix |
michael@0 | 178 | // the previous opcode because it was really the last but we did not know |
michael@0 | 179 | // it at that time. |
michael@0 | 180 | if (last_call && (buffer_->opcode_count > 0)) { |
michael@0 | 181 | op = &buffer_->opcodes[buffer_->opcode_count - 1]; |
michael@0 | 182 | op->SetOptions(options); |
michael@0 | 183 | } |
michael@0 | 184 | return true; |
michael@0 | 185 | } |
michael@0 | 186 | |
michael@0 | 187 | if (PENDING_ASTERISK == state) { |
michael@0 | 188 | if (last_call) { |
michael@0 | 189 | op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), |
michael@0 | 190 | kSeekToEnd, match_opts, |
michael@0 | 191 | options); |
michael@0 | 192 | } else { |
michael@0 | 193 | op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), |
michael@0 | 194 | kSeekForward, match_opts, |
michael@0 | 195 | options); |
michael@0 | 196 | } |
michael@0 | 197 | |
michael@0 | 198 | } else if (PENDING_QMARK == state) { |
michael@0 | 199 | op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), |
michael@0 | 200 | *skip_count, match_opts, options); |
michael@0 | 201 | *skip_count = 0; |
michael@0 | 202 | } else { |
michael@0 | 203 | if (last_call) { |
michael@0 | 204 | match_opts = static_cast<StringMatchOptions>(EXACT_LENGHT | match_opts); |
michael@0 | 205 | } |
michael@0 | 206 | op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), 0, |
michael@0 | 207 | match_opts, options); |
michael@0 | 208 | } |
michael@0 | 209 | if (NULL == op) { |
michael@0 | 210 | return false; |
michael@0 | 211 | } |
michael@0 | 212 | ++buffer_->opcode_count; |
michael@0 | 213 | fragment->clear(); |
michael@0 | 214 | return true; |
michael@0 | 215 | } |
michael@0 | 216 | |
michael@0 | 217 | bool PolicyRule::AddStringMatch(RuleType rule_type, int16 parameter, |
michael@0 | 218 | const wchar_t* string, |
michael@0 | 219 | StringMatchOptions match_opts) { |
michael@0 | 220 | if (done_) { |
michael@0 | 221 | // Do not allow to add more rules after generating the action opcode. |
michael@0 | 222 | return false; |
michael@0 | 223 | } |
michael@0 | 224 | |
michael@0 | 225 | const wchar_t* current_char = string; |
michael@0 | 226 | uint32 last_char = kLastCharIsNone; |
michael@0 | 227 | int state = PENDING_NONE; |
michael@0 | 228 | int skip_count = 0; // counts how many '?' we have seen in a row. |
michael@0 | 229 | std::wstring fragment; // accumulates the non-wildcard part of the string. |
michael@0 | 230 | |
michael@0 | 231 | while (L'\0' != *current_char) { |
michael@0 | 232 | switch (*current_char) { |
michael@0 | 233 | case L'*': |
michael@0 | 234 | if (kLastCharIsWild & last_char) { |
michael@0 | 235 | // '**' and '&*' is an error. |
michael@0 | 236 | return false; |
michael@0 | 237 | } |
michael@0 | 238 | if (!GenStringOpcode(rule_type, match_opts, parameter, |
michael@0 | 239 | state, false, &skip_count, &fragment)) { |
michael@0 | 240 | return false; |
michael@0 | 241 | } |
michael@0 | 242 | last_char = kLastCharIsAsterisk; |
michael@0 | 243 | state = PENDING_ASTERISK; |
michael@0 | 244 | break; |
michael@0 | 245 | case L'?': |
michael@0 | 246 | if (kLastCharIsAsterisk == last_char) { |
michael@0 | 247 | // '*?' is an error. |
michael@0 | 248 | return false; |
michael@0 | 249 | } |
michael@0 | 250 | if (!GenStringOpcode(rule_type, match_opts, parameter, |
michael@0 | 251 | state, false, &skip_count, &fragment)) { |
michael@0 | 252 | return false; |
michael@0 | 253 | } |
michael@0 | 254 | ++skip_count; |
michael@0 | 255 | last_char = kLastCharIsQuestionM; |
michael@0 | 256 | state = PENDING_QMARK; |
michael@0 | 257 | break; |
michael@0 | 258 | case L'/': |
michael@0 | 259 | // Note: "/?" is an escaped '?'. Eat the slash and fall through. |
michael@0 | 260 | if (L'?' == current_char[1]) { |
michael@0 | 261 | ++current_char; |
michael@0 | 262 | } |
michael@0 | 263 | default: |
michael@0 | 264 | fragment += *current_char; |
michael@0 | 265 | last_char = kLastCharIsAlpha; |
michael@0 | 266 | } |
michael@0 | 267 | ++current_char; |
michael@0 | 268 | } |
michael@0 | 269 | |
michael@0 | 270 | if (!GenStringOpcode(rule_type, match_opts, parameter, |
michael@0 | 271 | state, true, &skip_count, &fragment)) { |
michael@0 | 272 | return false; |
michael@0 | 273 | } |
michael@0 | 274 | return true; |
michael@0 | 275 | } |
michael@0 | 276 | |
michael@0 | 277 | bool PolicyRule::AddNumberMatch(RuleType rule_type, int16 parameter, |
michael@0 | 278 | unsigned long number, RuleOp comparison_op) { |
michael@0 | 279 | if (done_) { |
michael@0 | 280 | // Do not allow to add more rules after generating the action opcode. |
michael@0 | 281 | return false; |
michael@0 | 282 | } |
michael@0 | 283 | uint32 opts = (rule_type == IF_NOT)? kPolNegateEval : kPolNone; |
michael@0 | 284 | |
michael@0 | 285 | if (EQUAL == comparison_op) { |
michael@0 | 286 | if (NULL == opcode_factory_->MakeOpNumberMatch(parameter, number, opts)) { |
michael@0 | 287 | return false; |
michael@0 | 288 | } |
michael@0 | 289 | } else if (AND == comparison_op) { |
michael@0 | 290 | if (NULL == opcode_factory_->MakeOpUlongAndMatch(parameter, number, opts)) { |
michael@0 | 291 | return false; |
michael@0 | 292 | } |
michael@0 | 293 | } |
michael@0 | 294 | ++buffer_->opcode_count; |
michael@0 | 295 | return true; |
michael@0 | 296 | } |
michael@0 | 297 | |
michael@0 | 298 | bool PolicyRule::Done() { |
michael@0 | 299 | if (done_) { |
michael@0 | 300 | return true; |
michael@0 | 301 | } |
michael@0 | 302 | if (NULL == opcode_factory_->MakeOpAction(action_, kPolNone)) { |
michael@0 | 303 | return false; |
michael@0 | 304 | } |
michael@0 | 305 | ++buffer_->opcode_count; |
michael@0 | 306 | done_ = true; |
michael@0 | 307 | return true; |
michael@0 | 308 | } |
michael@0 | 309 | |
michael@0 | 310 | bool PolicyRule::RebindCopy(PolicyOpcode* opcode_start, size_t opcode_size, |
michael@0 | 311 | char* data_start, size_t* data_size) const { |
michael@0 | 312 | size_t count = buffer_->opcode_count; |
michael@0 | 313 | for (size_t ix = 0; ix != count; ++ix) { |
michael@0 | 314 | if (opcode_size < sizeof(PolicyOpcode)) { |
michael@0 | 315 | return false; |
michael@0 | 316 | } |
michael@0 | 317 | PolicyOpcode& opcode = buffer_->opcodes[ix]; |
michael@0 | 318 | *opcode_start = opcode; |
michael@0 | 319 | if (OP_WSTRING_MATCH == opcode.GetID()) { |
michael@0 | 320 | // For this opcode argument 0 is a delta to the string and argument 1 |
michael@0 | 321 | // is the length (in chars) of the string. |
michael@0 | 322 | const wchar_t* str = opcode.GetRelativeString(0); |
michael@0 | 323 | size_t str_len; |
michael@0 | 324 | opcode.GetArgument(1, &str_len); |
michael@0 | 325 | str_len = str_len * sizeof(wchar_t); |
michael@0 | 326 | if ((*data_size) < str_len) { |
michael@0 | 327 | return false; |
michael@0 | 328 | } |
michael@0 | 329 | *data_size -= str_len; |
michael@0 | 330 | data_start -= str_len; |
michael@0 | 331 | memcpy(data_start, str, str_len); |
michael@0 | 332 | // Recompute the string displacement |
michael@0 | 333 | ptrdiff_t delta = data_start - reinterpret_cast<char*>(opcode_start); |
michael@0 | 334 | opcode_start->SetArgument(0, delta); |
michael@0 | 335 | } |
michael@0 | 336 | ++opcode_start; |
michael@0 | 337 | opcode_size -= sizeof(PolicyOpcode); |
michael@0 | 338 | } |
michael@0 | 339 | |
michael@0 | 340 | return true; |
michael@0 | 341 | } |
michael@0 | 342 | |
michael@0 | 343 | PolicyRule::~PolicyRule() { |
michael@0 | 344 | delete [] reinterpret_cast<char*>(buffer_); |
michael@0 | 345 | delete opcode_factory_; |
michael@0 | 346 | } |
michael@0 | 347 | |
michael@0 | 348 | } // namespace sandbox |