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