• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
PmpColumnReader()23 PmpColumnReader::PmpColumnReader()
24     : length_(0),
25       field_type_(PMP_TYPE_INVALID),
26       rows_read_(0) {}
27 
~PmpColumnReader()28 PmpColumnReader::~PmpColumnReader() {}
29 
ReadFile(base::File * file,const PmpFieldType expected_type)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 
ReadString(const uint32 row,std::string * result) const62 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 
ReadUInt32(const uint32 row,uint32 * result) const73 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 
ReadDouble64(const uint32 row,double * result) const83 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 
ReadUInt8(const uint32 row,uint8 * result) const93 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 
ReadUInt64(const uint32 row,uint64 * result) const103 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 
rows_read() const113 uint32 PmpColumnReader::rows_read() const {
114   DCHECK(data_.get() != NULL);
115   return rows_read_;
116 }
117 
ParseData(const PmpFieldType expected_type)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 
IndexStrings()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