Revert "Reland c91b178b07b0d - Delete dead signin code (SigninGlobalError)"
[chromium-blink-merge.git] / chrome / common / safe_browsing / mach_o_image_reader_mac.cc
blob7f4abd413b77f25baf013c10d1a07bb7370cd514
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 // static
79 bool MachOImageReader::IsMachOMagicValue(uint32_t magic) {
80 return magic == FAT_MAGIC || magic == FAT_CIGAM ||
81 magic == MH_MAGIC || magic == MH_CIGAM ||
82 magic == MH_MAGIC_64 || magic == MH_CIGAM_64;
85 MachOImageReader::MachOImageReader()
86 : data_(),
87 is_fat_(false),
88 is_64_bit_(false),
89 commands_() {
92 MachOImageReader::~MachOImageReader() {}
94 bool MachOImageReader::Initialize(const uint8_t* image, size_t image_size) {
95 if (!image)
96 return false;
98 data_.reset(new ByteSlice(image, image_size));
100 const uint32_t* magic = data_->GetPointerAt<uint32_t>(0);
101 if (!magic)
102 return false;
104 // Check if this is a fat file. Note that the fat_header and fat_arch
105 // structs are always in big endian.
106 is_fat_ = *magic == FAT_MAGIC || *magic == FAT_CIGAM;
107 if (is_fat_) {
108 const fat_header* header = data_->GetPointerAt<fat_header>(0);
109 if (!header)
110 return false;
112 bool do_swap = header->magic == FAT_CIGAM;
113 uint32_t nfat_arch = do_swap ? OSSwapInt32(header->nfat_arch)
114 : header->nfat_arch;
116 size_t offset = sizeof(*header);
117 for (uint32_t i = 0; i < nfat_arch; ++i) {
118 const fat_arch* arch = data_->GetPointerAt<fat_arch>(offset);
119 if (!arch)
120 return false;
122 uint32_t arch_offset = do_swap ? OSSwapInt32(arch->offset) : arch->offset;
123 uint32_t arch_size = do_swap ? OSSwapInt32(arch->size) : arch->size;
125 ByteSlice slice = data_->Slice(arch_offset, arch_size);
126 if (!slice.IsValid())
127 return false;
129 fat_images_.push_back(new MachOImageReader());
130 if (!fat_images_.back()->Initialize(slice.data(), slice.size()))
131 return false;
133 offset += sizeof(*arch);
136 return true;
139 bool do_swap = *magic == MH_CIGAM || *magic == MH_CIGAM_64;
141 // Make sure this is a Mach-O file.
142 is_64_bit_ = *magic == MH_MAGIC_64 || *magic == MH_CIGAM_64;
143 if (!(is_64_bit_ || *magic == MH_MAGIC || do_swap))
144 return false;
146 // Read the full Mach-O image header.
147 if (is_64_bit_) {
148 if (!GetMachHeader64())
149 return false;
150 } else {
151 if (!GetMachHeader())
152 return false;
155 // Collect all the load commands for the binary.
156 const size_t load_command_size = sizeof(load_command);
157 size_t offset = is_64_bit_ ? sizeof(mach_header_64) : sizeof(mach_header);
158 const uint32_t num_commands = do_swap ? OSSwapInt32(GetMachHeader()->ncmds)
159 : GetMachHeader()->ncmds;
160 commands_.resize(num_commands);
161 for (uint32_t i = 0; i < num_commands; ++i) {
162 LoadCommand* command = &commands_[i];
164 command->data.resize(load_command_size);
165 if (!data_->CopyDataAt(offset, load_command_size, &command->data[0])) {
166 return false;
169 uint32_t cmdsize = do_swap ? OSSwapInt32(command->cmdsize())
170 : command->cmdsize();
171 command->data.resize(cmdsize);
172 if (!data_->CopyDataAt(offset, cmdsize, &command->data[0])) {
173 return false;
176 offset += cmdsize;
179 return true;
182 bool MachOImageReader::IsFat() {
183 return is_fat_;
186 std::vector<MachOImageReader*> MachOImageReader::GetFatImages() {
187 DCHECK(is_fat_);
188 std::vector<MachOImageReader*> images;
189 for (auto it = fat_images_.begin(); it != fat_images_.end(); ++it)
190 images.push_back(*it);
191 return images;
194 bool MachOImageReader::Is64Bit() {
195 DCHECK(!is_fat_);
196 return is_64_bit_;
199 const mach_header* MachOImageReader::GetMachHeader() {
200 DCHECK(!is_fat_);
201 return data_->GetPointerAt<mach_header>(0);
204 const mach_header_64* MachOImageReader::GetMachHeader64() {
205 DCHECK(is_64_bit_);
206 DCHECK(!is_fat_);
207 return data_->GetPointerAt<mach_header_64>(0);
210 uint32_t MachOImageReader::GetFileType() {
211 DCHECK(!is_fat_);
212 return GetMachHeader()->filetype;
215 const std::vector<MachOImageReader::LoadCommand>&
216 MachOImageReader::GetLoadCommands() {
217 DCHECK(!is_fat_);
218 return commands_;
221 bool MachOImageReader::GetCodeSignatureInfo(std::vector<uint8_t>* info) {
222 DCHECK(!is_fat_);
223 DCHECK(info->empty());
225 // Find the LC_CODE_SIGNATURE command and cast it to its linkedit format.
226 const linkedit_data_command* lc_code_signature = nullptr;
227 for (const auto& command : commands_) {
228 if (command.cmd() == LC_CODE_SIGNATURE) {
229 lc_code_signature = command.as_command<linkedit_data_command>();
230 break;
233 if (lc_code_signature == nullptr)
234 return false;
236 info->resize(lc_code_signature->datasize);
237 return data_->CopyDataAt(lc_code_signature->dataoff,
238 lc_code_signature->datasize,
239 &(*info)[0]);
242 } // namespace safe_browsing