summaryrefslogtreecommitdiffstats
path: root/mojo/public/cpp/bindings/lib/bindings_serialization.cc
blob: d5f778536847173015d3326b625750bacf72e29b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"

#include <assert.h>

#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
#include "mojo/public/cpp/bindings/lib/bounds_checker.h"
#include "mojo/public/cpp/bindings/lib/validation_errors.h"

namespace mojo {
namespace internal {

namespace {

const size_t kAlignment = 8;

template<typename T>
T AlignImpl(T t) {
  return t + (kAlignment - (t % kAlignment)) % kAlignment;
}

}  // namespace

size_t Align(size_t size) {
  return AlignImpl(size);
}

char* AlignPointer(char* ptr) {
  return reinterpret_cast<char*>(AlignImpl(reinterpret_cast<uintptr_t>(ptr)));
}

bool IsAligned(const void* ptr) {
  return !(reinterpret_cast<uintptr_t>(ptr) % kAlignment);
}

void EncodePointer(const void* ptr, uint64_t* offset) {
  if (!ptr) {
    *offset = 0;
    return;
  }

  const char* p_obj = reinterpret_cast<const char*>(ptr);
  const char* p_slot = reinterpret_cast<const char*>(offset);
  assert(p_obj > p_slot);

  *offset = static_cast<uint64_t>(p_obj - p_slot);
}

const void* DecodePointerRaw(const uint64_t* offset) {
  if (!*offset)
    return NULL;
  return reinterpret_cast<const char*>(offset) + *offset;
}

bool ValidateEncodedPointer(const uint64_t* offset) {
  // Cast to uintptr_t so overflow behavior is well defined.
  return reinterpret_cast<uintptr_t>(offset) + *offset >=
      reinterpret_cast<uintptr_t>(offset);
}

void EncodeHandle(Handle* handle, std::vector<Handle>* handles) {
  if (handle->is_valid()) {
    handles->push_back(*handle);
    handle->set_value(static_cast<MojoHandle>(handles->size() - 1));
  } else {
    handle->set_value(kEncodedInvalidHandleValue);
  }
}

void DecodeHandle(Handle* handle, std::vector<Handle>* handles) {
  if (handle->value() == kEncodedInvalidHandleValue) {
    *handle = Handle();
    return;
  }
  assert(handle->value() < handles->size());
  // Just leave holes in the vector so we don't screw up other indices.
  *handle = FetchAndReset(&handles->at(handle->value()));
}

bool ValidateStructHeader(const void* data,
                          uint32_t min_num_bytes,
                          uint32_t min_num_fields,
                          BoundsChecker* bounds_checker) {
  assert(min_num_bytes >= sizeof(StructHeader));

  if (!IsAligned(data)) {
    ReportValidationError(VALIDATION_ERROR_MISALIGNED_OBJECT);
    return false;
  }
  if (!bounds_checker->IsValidRange(data, sizeof(StructHeader))) {
    ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
    return false;
  }

  const StructHeader* header = static_cast<const StructHeader*>(data);

  // TODO(yzshen): Currently our binding code cannot handle structs of smaller
  // size or with fewer fields than the version that it sees. That needs to be
  // changed in order to provide backward compatibility.
  if (header->num_bytes < min_num_bytes ||
      header->num_fields < min_num_fields) {
    ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
    return false;
  }

  if (!bounds_checker->ClaimMemory(data, header->num_bytes)) {
    ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
    return false;
  }

  return true;
}

}  // namespace internal
}  // namespace mojo