Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / common / safe_browsing / mach_o_image_reader_mac.cc
blobf4e39e352f2597a53a1bf7bb3c3beb80ff326d6f
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/common/safe_browsing/mach_o_image_reader_mac.h"
7 #include <libkern/OSByteOrder.h>
8 #include <mach-o/fat.h>
9 #include <mach-o/loader.h>
11 #include "base/logging.h"
12 #include "base/numerics/safe_math.h"
14 namespace safe_browsing {
16 // ByteSlice is a bounds-checking view of an arbitrary byte array.
17 class ByteSlice {
18 public:
19 // Creates an invalid byte slice.
20 ByteSlice() : ByteSlice(nullptr, 0) {}
22 // Creates a slice for a given data array.
23 explicit ByteSlice(const uint8_t* data, size_t size)
24 : data_(data), size_(size) {}
25 ~ByteSlice() {}
27 bool IsValid() {
28 return data_ != nullptr;
31 // Creates a sub-slice from the current slice.
32 ByteSlice Slice(size_t at, size_t size) {
33 if (!RangeCheck(at, size))
34 return ByteSlice();
35 return ByteSlice(data_ + at, size);
38 // Casts an offset to a specific type.
39 template <typename T>
40 const T* GetPointerAt(size_t at) {
41 if (!RangeCheck(at, sizeof(T)))
42 return nullptr;
43 return reinterpret_cast<const T*>(data_ + at);
46 // Copies data from an offset to a buffer.
47 bool CopyDataAt(size_t at, size_t size, uint8_t* out_data) {
48 if (!RangeCheck(at, size))
49 return false;
50 memcpy(out_data, data_ + at, size);
51 return true;
54 bool RangeCheck(size_t offset, size_t size) {
55 if (offset >= size_)
56 return false;
57 base::CheckedNumeric<size_t> range(offset);
58 range += size;
59 if (!range.IsValid())
60 return false;
61 return range.ValueOrDie() <= size_;
64 const uint8_t* data() const { return data_; }
65 size_t size() const { return size_; }
67 private:
68 const uint8_t* data_;
69 size_t size_;
71 // Copy and assign allowed.
74 MachOImageReader::LoadCommand::LoadCommand() {}
76 MachOImageReader::LoadCommand::~LoadCommand() {}
78 MachOImageReader::MachOImageReader()
79 : data_(),
80 is_fat_(false),
81 is_64_bit_(false),
82 commands_() {
85 MachOImageReader::~MachOImageReader() {}
87 bool MachOImageReader::Initialize(const uint8_t* image, size_t image_size) {
88 if (!image)
89 return false;
91 data_.reset(new ByteSlice(image, image_size));
93 const uint32_t* magic = data_->GetPointerAt<uint32_t>(0);
94 if (!magic)
95 return false;
97 // Check if this is a fat file. Note that the fat_header and fat_arch
98 // structs are always in big endian.
99 is_fat_ = *magic == FAT_MAGIC || *magic == FAT_CIGAM;
100 if (is_fat_) {
101 const fat_header* header = data_->GetPointerAt<fat_header>(0);
102 if (!header)
103 return false;
105 bool do_swap = header->magic == FAT_CIGAM;
106 uint32_t nfat_arch = do_swap ? OSSwapInt32(header->nfat_arch)
107 : header->nfat_arch;
109 size_t offset = sizeof(*header);
110 for (uint32_t i = 0; i < nfat_arch; ++i) {
111 const fat_arch* arch = data_->GetPointerAt<fat_arch>(offset);
112 if (!arch)
113 return false;
115 uint32_t arch_offset = do_swap ? OSSwapInt32(arch->offset) : arch->offset;
116 uint32_t arch_size = do_swap ? OSSwapInt32(arch->size) : arch->size;
118 ByteSlice slice = data_->Slice(arch_offset, arch_size);
119 if (!slice.IsValid())
120 return false;
122 fat_images_.push_back(new MachOImageReader());
123 if (!fat_images_.back()->Initialize(slice.data(), slice.size()))
124 return false;
126 offset += sizeof(*arch);
129 return true;
132 bool do_swap = *magic == MH_CIGAM || *magic == MH_CIGAM_64;
134 // Make sure this is a Mach-O file.
135 is_64_bit_ = *magic == MH_MAGIC_64 || *magic == MH_CIGAM_64;
136 if (!(is_64_bit_ || *magic == MH_MAGIC || do_swap))
137 return false;
139 // Read the full Mach-O image header.
140 if (is_64_bit_) {
141 if (!GetMachHeader64())
142 return false;
143 } else {
144 if (!GetMachHeader())
145 return false;
148 // Collect all the load commands for the binary.
149 const size_t load_command_size = sizeof(load_command);
150 size_t offset = is_64_bit_ ? sizeof(mach_header_64) : sizeof(mach_header);
151 const uint32_t num_commands = do_swap ? OSSwapInt32(GetMachHeader()->ncmds)
152 : GetMachHeader()->ncmds;
153 commands_.resize(num_commands);
154 for (uint32_t i = 0; i < num_commands; ++i) {
155 LoadCommand* command = &commands_[i];
157 command->data.resize(load_command_size);
158 if (!data_->CopyDataAt(offset, load_command_size, &command->data[0])) {
159 return false;
162 uint32_t cmdsize = do_swap ? OSSwapInt32(command->cmdsize())
163 : command->cmdsize();
164 command->data.resize(cmdsize);
165 if (!data_->CopyDataAt(offset, cmdsize, &command->data[0])) {
166 return false;
169 offset += cmdsize;
172 return true;
175 bool MachOImageReader::IsFat() {
176 return is_fat_;
179 std::vector<MachOImageReader*> MachOImageReader::GetFatImages() {
180 DCHECK(is_fat_);
181 std::vector<MachOImageReader*> images;
182 for (auto it = fat_images_.begin(); it != fat_images_.end(); ++it)
183 images.push_back(*it);
184 return images;
187 bool MachOImageReader::Is64Bit() {
188 DCHECK(!is_fat_);
189 return is_64_bit_;
192 const mach_header* MachOImageReader::GetMachHeader() {
193 DCHECK(!is_fat_);
194 return data_->GetPointerAt<mach_header>(0);
197 const mach_header_64* MachOImageReader::GetMachHeader64() {
198 DCHECK(is_64_bit_);
199 DCHECK(!is_fat_);
200 return data_->GetPointerAt<mach_header_64>(0);
203 uint32_t MachOImageReader::GetFileType() {
204 DCHECK(!is_fat_);
205 return GetMachHeader()->filetype;
208 const std::vector<MachOImageReader::LoadCommand>&
209 MachOImageReader::GetLoadCommands() {
210 DCHECK(!is_fat_);
211 return commands_;
214 bool MachOImageReader::GetCodeSignatureInfo(std::vector<uint8_t>* info) {
215 DCHECK(!is_fat_);
216 DCHECK(info->empty());
218 // Find the LC_CODE_SIGNATURE command and cast it to its linkedit format.
219 const linkedit_data_command* lc_code_signature = nullptr;
220 for (const auto& command : commands_) {
221 if (command.cmd() == LC_CODE_SIGNATURE) {
222 lc_code_signature = command.as_command<linkedit_data_command>();
223 break;
226 if (lc_code_signature == nullptr)
227 return false;
229 info->resize(lc_code_signature->datasize);
230 return data_->CopyDataAt(lc_code_signature->dataoff,
231 lc_code_signature->datasize,
232 &(*info)[0]);
235 } // namespace safe_browsing