1 // Copyright 2013 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. 4 5 #include "chrome/utility/media_galleries/pmp_column_reader.h" 6 7 #include <cstring> 8 9 #include "base/files/file.h" 10 #include "base/files/file_util.h" 11 #include "base/logging.h" 12 #include "base/threading/thread_restrictions.h" 13 14 namespace picasa { 15 16 namespace { 17 18 COMPILE_ASSERT(sizeof(double) == 8, double_must_be_8_bytes_long); 19 const int64 kPmpMaxFilesize = 50*1024*1024; // Arbitrary maximum of 50 MB. 20 21 } // namespace 22 23 PmpColumnReader::PmpColumnReader() 24 : length_(0), 25 field_type_(PMP_TYPE_INVALID), 26 rows_read_(0) {} 27 28 PmpColumnReader::~PmpColumnReader() {} 29 30 bool PmpColumnReader::ReadFile(base::File* file, 31 const PmpFieldType expected_type) { 32 DCHECK(!data_.get()); 33 base::ThreadRestrictions::AssertIOAllowed(); 34 35 if (!file->IsValid()) 36 return false; 37 38 base::File::Info info; 39 if (!file->GetInfo(&info)) 40 return false; 41 length_ = info.size; 42 43 if (length_ < kPmpHeaderSize || length_ > kPmpMaxFilesize) 44 return false; 45 46 data_.reset(new uint8[length_]); 47 48 char* data_begin = reinterpret_cast<char*>(data_.get()); 49 50 DCHECK(length_ < kint32max); // ReadFile expects an int. 51 52 bool success = file->Read(0, data_begin, length_) && 53 ParseData(expected_type); 54 55 // If any of the reading or parsing fails, prevent Read* calls. 56 if (!success) 57 rows_read_ = 0; 58 59 return success; 60 } 61 62 bool PmpColumnReader::ReadString(const uint32 row, std::string* result) const { 63 DCHECK(data_.get() != NULL); 64 65 if (field_type_ != PMP_TYPE_STRING || row >= rows_read_) 66 return false; 67 68 DCHECK_LT(row, strings_.size()); 69 *result = strings_[row]; 70 return true; 71 } 72 73 bool PmpColumnReader::ReadUInt32(const uint32 row, uint32* result) const { 74 DCHECK(data_.get() != NULL); 75 76 if (field_type_ != PMP_TYPE_UINT32 || row >= rows_read_) 77 return false; 78 79 *result = reinterpret_cast<uint32*>(data_.get() + kPmpHeaderSize)[row]; 80 return true; 81 } 82 83 bool PmpColumnReader::ReadDouble64(const uint32 row, double* result) const { 84 DCHECK(data_.get() != NULL); 85 86 if (field_type_ != PMP_TYPE_DOUBLE64 || row >= rows_read_) 87 return false; 88 89 *result = reinterpret_cast<double*>(data_.get() + kPmpHeaderSize)[row]; 90 return true; 91 } 92 93 bool PmpColumnReader::ReadUInt8(const uint32 row, uint8* result) const { 94 DCHECK(data_.get() != NULL); 95 96 if (field_type_ != PMP_TYPE_UINT8 || row >= rows_read_) 97 return false; 98 99 *result = reinterpret_cast<uint8*>(data_.get() + kPmpHeaderSize)[row]; 100 return true; 101 } 102 103 bool PmpColumnReader::ReadUInt64(const uint32 row, uint64* result) const { 104 DCHECK(data_.get() != NULL); 105 106 if (field_type_ != PMP_TYPE_UINT64 || row >= rows_read_) 107 return false; 108 109 *result = reinterpret_cast<uint64*>(data_.get() + kPmpHeaderSize)[row]; 110 return true; 111 } 112 113 uint32 PmpColumnReader::rows_read() const { 114 DCHECK(data_.get() != NULL); 115 return rows_read_; 116 } 117 118 bool PmpColumnReader::ParseData(const PmpFieldType expected_type) { 119 DCHECK(data_.get() != NULL); 120 DCHECK_GE(length_, kPmpHeaderSize); 121 122 // Check all magic bytes. 123 if (memcmp(&kPmpMagic1, &data_[kPmpMagic1Offset], sizeof(kPmpMagic1)) != 0 || 124 memcmp(&kPmpMagic2, &data_[kPmpMagic2Offset], sizeof(kPmpMagic2)) != 0 || 125 memcmp(&kPmpMagic3, &data_[kPmpMagic3Offset], sizeof(kPmpMagic3)) != 0 || 126 memcmp(&kPmpMagic4, &data_[kPmpMagic4Offset], sizeof(kPmpMagic4)) != 0) { 127 return false; 128 } 129 130 uint16 field_type_data = 131 *(reinterpret_cast<uint16*>(&data_[kPmpFieldType1Offset])); 132 133 // Verify if field type matches second declaration 134 if (field_type_data != 135 *(reinterpret_cast<uint16*>(&data_[kPmpFieldType2Offset]))) { 136 return false; 137 } 138 139 field_type_ = static_cast<PmpFieldType>(field_type_data); 140 141 if (field_type_ != expected_type) 142 return false; 143 144 rows_read_ = *(reinterpret_cast<uint32*>(&data_[kPmpRowCountOffset])); 145 146 // Sanity check against malicious row field. 147 if (rows_read_ > (kPmpMaxFilesize - kPmpHeaderSize)) 148 return false; 149 150 DCHECK_GE(length_, kPmpHeaderSize); 151 int64 body_length = length_ - kPmpHeaderSize; 152 int64 expected_body_length = 0; 153 switch (field_type_) { 154 case PMP_TYPE_STRING: 155 expected_body_length = IndexStrings(); 156 break; 157 case PMP_TYPE_UINT32: 158 expected_body_length = static_cast<int64>(rows_read_) * sizeof(uint32); 159 break; 160 case PMP_TYPE_DOUBLE64: 161 expected_body_length = static_cast<int64>(rows_read_) * sizeof(double); 162 break; 163 case PMP_TYPE_UINT8: 164 expected_body_length = static_cast<int64>(rows_read_) * sizeof(uint8); 165 break; 166 case PMP_TYPE_UINT64: 167 expected_body_length = static_cast<int64>(rows_read_) * sizeof(uint64); 168 break; 169 default: 170 return false; 171 break; 172 } 173 174 return body_length == expected_body_length; 175 } 176 177 int64 PmpColumnReader::IndexStrings() { 178 DCHECK(data_.get() != NULL); 179 DCHECK_GE(length_, kPmpHeaderSize); 180 181 strings_.reserve(rows_read_); 182 183 int64 bytes_parsed = kPmpHeaderSize; 184 const uint8* data_cursor = data_.get() + kPmpHeaderSize; 185 186 while (strings_.size() < rows_read_) { 187 const uint8* string_end = static_cast<const uint8*>( 188 memchr(data_cursor, '\0', length_ - bytes_parsed)); 189 190 // Fail if cannot find null termination. String runs on past file end. 191 if (string_end == NULL) 192 return -1; 193 194 // Length of string. (+1 to include the termination character). 195 ptrdiff_t length_in_bytes = string_end - data_cursor + 1; 196 197 strings_.push_back(reinterpret_cast<const char*>(data_cursor)); 198 data_cursor += length_in_bytes; 199 bytes_parsed += length_in_bytes; 200 } 201 202 return bytes_parsed - kPmpHeaderSize; 203 } 204 205 } // namespace picasa 206