1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "vendor_boot_img_utils.h"
18
19 #include <string.h>
20
21 #include <android-base/file.h>
22 #include <android-base/result.h>
23 #include <bootimg.h>
24 #include <libavb/libavb.h>
25
26 namespace {
27
28 using android::base::Result;
29
30 // Updates a given buffer by creating a new one.
31 class DataUpdater {
32 public:
DataUpdater(const std::string & old_data)33 DataUpdater(const std::string& old_data) : old_data_(&old_data) {
34 old_data_ptr_ = old_data_->data();
35 new_data_.resize(old_data_->size(), '\0');
36 new_data_ptr_ = new_data_.data();
37 }
38 // Copy |num_bytes| from src to dst.
Copy(uint32_t num_bytes)39 [[nodiscard]] Result<void> Copy(uint32_t num_bytes) {
40 if (num_bytes == 0) return {};
41 if (auto res = CheckAdvance(old_data_ptr_, old_end(), num_bytes, __FUNCTION__); !res.ok())
42 return res;
43 if (auto res = CheckAdvance(new_data_ptr_, new_end(), num_bytes, __FUNCTION__); !res.ok())
44 return res;
45 memcpy(new_data_ptr_, old_data_ptr_, num_bytes);
46 old_data_ptr_ += num_bytes;
47 new_data_ptr_ += num_bytes;
48 return {};
49 }
50 // Replace |old_num_bytes| from src with new data.
Replace(uint32_t old_num_bytes,const std::string & new_data)51 [[nodiscard]] Result<void> Replace(uint32_t old_num_bytes, const std::string& new_data) {
52 return Replace(old_num_bytes, new_data.data(), new_data.size());
53 }
Replace(uint32_t old_num_bytes,const void * new_data,uint32_t new_data_size)54 [[nodiscard]] Result<void> Replace(uint32_t old_num_bytes, const void* new_data,
55 uint32_t new_data_size) {
56 if (auto res = CheckAdvance(old_data_ptr_, old_end(), old_num_bytes, __FUNCTION__);
57 !res.ok())
58 return res;
59 old_data_ptr_ += old_num_bytes;
60
61 if (new_data_size == 0) return {};
62 if (auto res = CheckAdvance(new_data_ptr_, new_end(), new_data_size, __FUNCTION__);
63 !res.ok())
64 return res;
65 memcpy(new_data_ptr_, new_data, new_data_size);
66 new_data_ptr_ += new_data_size;
67 return {};
68 }
69 // Skip |old_skip| from src and |new_skip| from dst, respectively.
Skip(uint32_t old_skip,uint32_t new_skip)70 [[nodiscard]] Result<void> Skip(uint32_t old_skip, uint32_t new_skip) {
71 if (auto res = CheckAdvance(old_data_ptr_, old_end(), old_skip, __FUNCTION__); !res.ok())
72 return res;
73 old_data_ptr_ += old_skip;
74 if (auto res = CheckAdvance(new_data_ptr_, new_end(), new_skip, __FUNCTION__); !res.ok())
75 return res;
76 new_data_ptr_ += new_skip;
77 return {};
78 }
79
Seek(uint32_t offset)80 [[nodiscard]] Result<void> Seek(uint32_t offset) {
81 if (offset > size()) return Errorf("Cannot seek 0x{:x}, size is 0x{:x}", offset, size());
82 old_data_ptr_ = old_begin() + offset;
83 new_data_ptr_ = new_begin() + offset;
84 return {};
85 }
86
Finish()87 std::string Finish() {
88 new_data_ptr_ = nullptr;
89 return std::move(new_data_);
90 }
91
CheckOffset(uint32_t old_offset,uint32_t new_offset)92 [[nodiscard]] Result<void> CheckOffset(uint32_t old_offset, uint32_t new_offset) {
93 if (old_begin() + old_offset != old_cur())
94 return Errorf("Old offset mismatch: expected: 0x{:x}, actual: 0x{:x}", old_offset,
95 old_cur() - old_begin());
96 if (new_begin() + new_offset != new_cur())
97 return Errorf("New offset mismatch: expected: 0x{:x}, actual: 0x{:x}", new_offset,
98 new_cur() - new_begin());
99 return {};
100 }
101
size() const102 uint64_t size() const { return old_data_->size(); }
old_begin() const103 const char* old_begin() const { return old_data_->data(); }
old_cur()104 const char* old_cur() { return old_data_ptr_; }
old_end() const105 const char* old_end() const { return old_data_->data() + old_data_->size(); }
new_begin()106 char* new_begin() { return new_data_.data(); }
new_cur()107 char* new_cur() { return new_data_ptr_; }
new_end()108 char* new_end() { return new_data_.data() + new_data_.size(); }
109
110 private:
111 // Check if it is okay to advance |num_bytes| from |current|.
CheckAdvance(const char * current,const char * end,uint32_t num_bytes,const char * op)112 [[nodiscard]] Result<void> CheckAdvance(const char* current, const char* end,
113 uint32_t num_bytes, const char* op) {
114 auto new_end = current + num_bytes;
115 if (new_end < current /* add overflow */)
116 return Errorf("{}: Addition overflow: 0x{} + 0x{:x} < 0x{}", op, fmt::ptr(current),
117 num_bytes, fmt::ptr(current));
118 if (new_end > end)
119 return Errorf("{}: Boundary overflow: 0x{} + 0x{:x} > 0x{}", op, fmt::ptr(current),
120 num_bytes, fmt::ptr(end));
121 return {};
122 }
123 const std::string* old_data_;
124 std::string new_data_;
125 const char* old_data_ptr_;
126 char* new_data_ptr_;
127 };
128
129 // Get the size of vendor boot header.
get_vendor_boot_header_size(const vendor_boot_img_hdr_v3 * hdr)130 [[nodiscard]] Result<uint32_t> get_vendor_boot_header_size(const vendor_boot_img_hdr_v3* hdr) {
131 if (hdr->header_version == 3) return sizeof(vendor_boot_img_hdr_v3);
132 if (hdr->header_version == 4) return sizeof(vendor_boot_img_hdr_v4);
133 return Errorf("Unrecognized vendor boot header version {}", hdr->header_version);
134 }
135
136 // Check that content contains a valid vendor boot image header with a version at least |version|.
check_vendor_boot_hdr(const std::string & content,uint32_t version)137 [[nodiscard]] Result<void> check_vendor_boot_hdr(const std::string& content, uint32_t version) {
138 // get_vendor_boot_header_size reads header_version, so make sure reading it does not
139 // go out of bounds by ensuring that the content has at least the size of V3 header.
140 if (content.size() < sizeof(vendor_boot_img_hdr_v3)) {
141 return Errorf("Size of vendor boot is 0x{:x}, less than size of V3 header: 0x{:x}",
142 content.size(), sizeof(vendor_boot_img_hdr_v3));
143 }
144 // Now read hdr->header_version and assert the size.
145 auto hdr = reinterpret_cast<const vendor_boot_img_hdr_v3*>(content.data());
146 auto expect_header_size = get_vendor_boot_header_size(hdr);
147 if (!expect_header_size.ok()) return expect_header_size.error();
148 if (content.size() < *expect_header_size) {
149 return Errorf("Size of vendor boot is 0x{:x}, less than size of V{} header: 0x{:x}",
150 content.size(), version, *expect_header_size);
151 }
152 if (memcmp(hdr->magic, VENDOR_BOOT_MAGIC, VENDOR_BOOT_MAGIC_SIZE) != 0) {
153 return Errorf("Vendor boot image magic mismatch");
154 }
155 if (hdr->page_size == 0) {
156 return Errorf("Page size cannot be zero");
157 }
158 if (hdr->header_version < version) {
159 return Errorf("Require vendor boot header V{} but is V{}", version, hdr->header_version);
160 }
161 return {};
162 }
163
164 // Wrapper of ReadFdToString. Seek to the beginning and read the whole file to string.
load_file(android::base::borrowed_fd fd,uint64_t expected_size,const char * what)165 [[nodiscard]] Result<std::string> load_file(android::base::borrowed_fd fd, uint64_t expected_size,
166 const char* what) {
167 if (lseek(fd.get(), 0, SEEK_SET) != 0) {
168 return ErrnoErrorf("Can't seek to the beginning of {} image", what);
169 }
170 std::string content;
171 if (!android::base::ReadFdToString(fd, &content)) {
172 return ErrnoErrorf("Cannot read {} to string", what);
173 }
174 if (content.size() != expected_size) {
175 return Errorf("Size of {} does not match, expected 0x{:x}, read 0x{:x}", what,
176 expected_size, content.size());
177 }
178 return content;
179 }
180
181 // Wrapper of WriteStringToFd. Seek to the beginning and write the whole file to string.
store_file(android::base::borrowed_fd fd,const std::string & data,const char * what)182 [[nodiscard]] Result<void> store_file(android::base::borrowed_fd fd, const std::string& data,
183 const char* what) {
184 if (lseek(fd.get(), 0, SEEK_SET) != 0) {
185 return ErrnoErrorf("Cannot seek to beginning of {} before writing", what);
186 }
187 if (!android::base::WriteStringToFd(data, fd)) {
188 return ErrnoErrorf("Cannot write new content to {}", what);
189 }
190 if (TEMP_FAILURE_RETRY(ftruncate(fd.get(), data.size())) == -1) {
191 return ErrnoErrorf("Truncating new vendor boot image to 0x{:x} fails", data.size());
192 }
193 return {};
194 }
195
196 // Copy AVB footer if it exists in the old buffer.
copy_avb_footer(DataUpdater * updater)197 [[nodiscard]] Result<void> copy_avb_footer(DataUpdater* updater) {
198 if (updater->size() < AVB_FOOTER_SIZE) return {};
199 if (auto res = updater->Seek(updater->size() - AVB_FOOTER_SIZE); !res.ok()) return res;
200 if (memcmp(updater->old_cur(), AVB_FOOTER_MAGIC, AVB_FOOTER_MAGIC_LEN) != 0) return {};
201 return updater->Copy(AVB_FOOTER_SIZE);
202 }
203
204 // round |value| up to a multiple of |page_size|.
205 // aware that this can be integer overflow if value is too large
round_up(uint32_t value,uint32_t page_size)206 inline uint32_t round_up(uint32_t value, uint32_t page_size) {
207 return (value + page_size - 1) / page_size * page_size;
208 }
209
210 // Replace the vendor ramdisk as a whole.
replace_default_vendor_ramdisk(const std::string & vendor_boot,const std::string & new_ramdisk)211 [[nodiscard]] Result<std::string> replace_default_vendor_ramdisk(const std::string& vendor_boot,
212 const std::string& new_ramdisk) {
213 if (auto res = check_vendor_boot_hdr(vendor_boot, 3); !res.ok()) return res.error();
214 auto hdr = reinterpret_cast<const vendor_boot_img_hdr_v3*>(vendor_boot.data());
215 auto hdr_size = get_vendor_boot_header_size(hdr);
216 if (!hdr_size.ok()) return hdr_size.error();
217 // Refer to bootimg.h for details. Numbers are in bytes.
218 const uint32_t o = round_up(*hdr_size, hdr->page_size);
219 const uint32_t p = round_up(hdr->vendor_ramdisk_size, hdr->page_size);
220 const uint32_t q = round_up(hdr->dtb_size, hdr->page_size);
221
222 DataUpdater updater(vendor_boot);
223
224 // Copy header (O bytes), then update fields in header.
225 if (auto res = updater.Copy(o); !res.ok()) return res.error();
226 auto new_hdr = reinterpret_cast<vendor_boot_img_hdr_v3*>(updater.new_begin());
227 new_hdr->vendor_ramdisk_size = new_ramdisk.size();
228 // Because it is unknown how the new ramdisk is fragmented, the whole table is replaced
229 // with a single entry representing the full ramdisk.
230 if (new_hdr->header_version >= 4) {
231 auto new_hdr_v4 = static_cast<vendor_boot_img_hdr_v4*>(new_hdr);
232 new_hdr_v4->vendor_ramdisk_table_entry_size = sizeof(vendor_ramdisk_table_entry_v4);
233 new_hdr_v4->vendor_ramdisk_table_entry_num = 1;
234 new_hdr_v4->vendor_ramdisk_table_size = new_hdr_v4->vendor_ramdisk_table_entry_num *
235 new_hdr_v4->vendor_ramdisk_table_entry_size;
236 }
237
238 // Copy the new ramdisk.
239 if (auto res = updater.Replace(hdr->vendor_ramdisk_size, new_ramdisk); !res.ok())
240 return res.error();
241 const uint32_t new_p = round_up(new_hdr->vendor_ramdisk_size, new_hdr->page_size);
242 if (auto res = updater.Skip(p - hdr->vendor_ramdisk_size, new_p - new_hdr->vendor_ramdisk_size);
243 !res.ok())
244 return res.error();
245 if (auto res = updater.CheckOffset(o + p, o + new_p); !res.ok()) return res.error();
246
247 // Copy DTB (Q bytes).
248 if (auto res = updater.Copy(q); !res.ok()) return res.error();
249
250 if (new_hdr->header_version >= 4) {
251 auto hdr_v4 = static_cast<const vendor_boot_img_hdr_v4*>(hdr);
252 const uint32_t r = round_up(hdr_v4->vendor_ramdisk_table_size, hdr_v4->page_size);
253 const uint32_t s = round_up(hdr_v4->bootconfig_size, hdr_v4->page_size);
254
255 auto new_entry = reinterpret_cast<vendor_ramdisk_table_entry_v4*>(updater.new_cur());
256 auto new_hdr_v4 = static_cast<const vendor_boot_img_hdr_v4*>(new_hdr);
257 auto new_r = round_up(new_hdr_v4->vendor_ramdisk_table_size, new_hdr->page_size);
258 if (auto res = updater.Skip(r, new_r); !res.ok()) return res.error();
259 if (auto res = updater.CheckOffset(o + p + q + r, o + new_p + q + new_r); !res.ok())
260 return res.error();
261
262 // Replace table with single entry representing the full ramdisk.
263 new_entry->ramdisk_size = new_hdr->vendor_ramdisk_size;
264 new_entry->ramdisk_offset = 0;
265 new_entry->ramdisk_type = VENDOR_RAMDISK_TYPE_NONE;
266 memset(new_entry->ramdisk_name, '\0', VENDOR_RAMDISK_NAME_SIZE);
267 memset(new_entry->board_id, '\0', VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE);
268
269 // Copy bootconfig (S bytes).
270 if (auto res = updater.Copy(s); !res.ok()) return res.error();
271 }
272
273 if (auto res = copy_avb_footer(&updater); !res.ok()) return res.error();
274 return updater.Finish();
275 }
276
277 // Find a ramdisk fragment with a unique name. Abort if none or multiple fragments are found.
find_unique_ramdisk(const std::string & ramdisk_name,const vendor_ramdisk_table_entry_v4 * table,uint32_t size)278 [[nodiscard]] Result<const vendor_ramdisk_table_entry_v4*> find_unique_ramdisk(
279 const std::string& ramdisk_name, const vendor_ramdisk_table_entry_v4* table,
280 uint32_t size) {
281 const vendor_ramdisk_table_entry_v4* ret = nullptr;
282 uint32_t idx = 0;
283 const vendor_ramdisk_table_entry_v4* entry = table;
284 for (; idx < size; idx++, entry++) {
285 auto entry_name_c_str = reinterpret_cast<const char*>(entry->ramdisk_name);
286 auto entry_name_len = strnlen(entry_name_c_str, VENDOR_RAMDISK_NAME_SIZE);
287 std::string_view entry_name(entry_name_c_str, entry_name_len);
288 if (entry_name == ramdisk_name) {
289 if (ret != nullptr) {
290 return Errorf("Multiple vendor ramdisk '{}' found, name should be unique",
291 ramdisk_name.c_str());
292 }
293 ret = entry;
294 }
295 }
296 if (ret == nullptr) {
297 return Errorf("Vendor ramdisk '{}' not found", ramdisk_name.c_str());
298 }
299 return ret;
300 }
301
302 // Find the vendor ramdisk fragment with |ramdisk_name| within the content of |vendor_boot|, and
303 // replace it with the content of |new_ramdisk|.
replace_vendor_ramdisk_fragment(const std::string & ramdisk_name,const std::string & vendor_boot,const std::string & new_ramdisk)304 [[nodiscard]] Result<std::string> replace_vendor_ramdisk_fragment(const std::string& ramdisk_name,
305 const std::string& vendor_boot,
306 const std::string& new_ramdisk) {
307 if (auto res = check_vendor_boot_hdr(vendor_boot, 4); !res.ok()) return res.error();
308 auto hdr = reinterpret_cast<const vendor_boot_img_hdr_v4*>(vendor_boot.data());
309 auto hdr_size = get_vendor_boot_header_size(hdr);
310 if (!hdr_size.ok()) return hdr_size.error();
311 // Refer to bootimg.h for details. Numbers are in bytes.
312 const uint32_t o = round_up(*hdr_size, hdr->page_size);
313 const uint32_t p = round_up(hdr->vendor_ramdisk_size, hdr->page_size);
314 const uint32_t q = round_up(hdr->dtb_size, hdr->page_size);
315 const uint32_t r = round_up(hdr->vendor_ramdisk_table_size, hdr->page_size);
316 const uint32_t s = round_up(hdr->bootconfig_size, hdr->page_size);
317
318 uint64_t total_size = (uint64_t)o + p + q + r + s;
319 if (total_size > vendor_boot.size()) {
320 return Errorf("Vendor boot image size is too small, overflow");
321 }
322
323 if ((uint64_t)hdr->vendor_ramdisk_table_entry_num * sizeof(vendor_ramdisk_table_entry_v4) >
324 (uint64_t)o + p + q + r) {
325 return Errorf("Too many vendor ramdisk entries in table, overflow");
326 }
327
328 // Find entry with name |ramdisk_name|.
329 auto old_table_start =
330 reinterpret_cast<const vendor_ramdisk_table_entry_v4*>(vendor_boot.data() + o + p + q);
331 auto find_res =
332 find_unique_ramdisk(ramdisk_name, old_table_start, hdr->vendor_ramdisk_table_entry_num);
333 if (!find_res.ok()) return find_res.error();
334 const vendor_ramdisk_table_entry_v4* replace_entry = *find_res;
335 uint32_t replace_idx = replace_entry - old_table_start;
336
337 // Now reconstruct.
338 DataUpdater updater(vendor_boot);
339
340 // Copy header (O bytes), then update fields in header.
341 if (auto res = updater.Copy(o); !res.ok()) return res.error();
342 auto new_hdr = reinterpret_cast<vendor_boot_img_hdr_v4*>(updater.new_begin());
343
344 // Copy ramdisk fragments, replace for the matching index.
345 {
346 auto old_ramdisk_entry = reinterpret_cast<const vendor_ramdisk_table_entry_v4*>(
347 vendor_boot.data() + o + p + q);
348 uint32_t new_total_ramdisk_size = 0;
349 for (uint32_t new_ramdisk_idx = 0; new_ramdisk_idx < hdr->vendor_ramdisk_table_entry_num;
350 new_ramdisk_idx++, old_ramdisk_entry++) {
351 if (new_ramdisk_idx == replace_idx) {
352 if (auto res = updater.Replace(replace_entry->ramdisk_size, new_ramdisk); !res.ok())
353 return res.error();
354 new_total_ramdisk_size += new_ramdisk.size();
355 } else {
356 if (auto res = updater.Copy(old_ramdisk_entry->ramdisk_size); !res.ok())
357 return res.error();
358 new_total_ramdisk_size += old_ramdisk_entry->ramdisk_size;
359 }
360 }
361 new_hdr->vendor_ramdisk_size = new_total_ramdisk_size;
362 }
363
364 // Pad ramdisk to page boundary.
365 const uint32_t new_p = round_up(new_hdr->vendor_ramdisk_size, new_hdr->page_size);
366 if (auto res = updater.Skip(p - hdr->vendor_ramdisk_size, new_p - new_hdr->vendor_ramdisk_size);
367 !res.ok())
368 return res.error();
369 if (auto res = updater.CheckOffset(o + p, o + new_p); !res.ok()) return res.error();
370
371 // Copy DTB (Q bytes).
372 if (auto res = updater.Copy(q); !res.ok()) return res.error();
373
374 // Copy table, but with corresponding entries modified, including:
375 // - ramdisk_size of the entry replaced
376 // - ramdisk_offset of subsequent entries.
377 for (uint32_t new_total_ramdisk_size = 0, new_entry_idx = 0;
378 new_entry_idx < hdr->vendor_ramdisk_table_entry_num; new_entry_idx++) {
379 auto new_entry = reinterpret_cast<vendor_ramdisk_table_entry_v4*>(updater.new_cur());
380 if (auto res = updater.Copy(hdr->vendor_ramdisk_table_entry_size); !res.ok())
381 return res.error();
382 new_entry->ramdisk_offset = new_total_ramdisk_size;
383
384 if (new_entry_idx == replace_idx) {
385 new_entry->ramdisk_size = new_ramdisk.size();
386 }
387 new_total_ramdisk_size += new_entry->ramdisk_size;
388 }
389
390 // Copy padding of R pages; this is okay because table size is not changed.
391 if (auto res = updater.Copy(r - hdr->vendor_ramdisk_table_entry_num *
392 hdr->vendor_ramdisk_table_entry_size);
393 !res.ok())
394 return res.error();
395 if (auto res = updater.CheckOffset(o + p + q + r, o + new_p + q + r); !res.ok())
396 return res.error();
397
398 // Copy bootconfig (S bytes).
399 if (auto res = updater.Copy(s); !res.ok()) return res.error();
400
401 if (auto res = copy_avb_footer(&updater); !res.ok()) return res.error();
402 return updater.Finish();
403 }
404
405 } // namespace
406
replace_vendor_ramdisk(android::base::borrowed_fd vendor_boot_fd,uint64_t vendor_boot_size,const std::string & ramdisk_name,android::base::borrowed_fd new_ramdisk_fd,uint64_t new_ramdisk_size)407 [[nodiscard]] Result<void> replace_vendor_ramdisk(android::base::borrowed_fd vendor_boot_fd,
408 uint64_t vendor_boot_size,
409 const std::string& ramdisk_name,
410 android::base::borrowed_fd new_ramdisk_fd,
411 uint64_t new_ramdisk_size) {
412 if (new_ramdisk_size > std::numeric_limits<uint32_t>::max()) {
413 return Errorf("New vendor ramdisk is too big");
414 }
415
416 auto vendor_boot = load_file(vendor_boot_fd, vendor_boot_size, "vendor boot");
417 if (!vendor_boot.ok()) return vendor_boot.error();
418 auto new_ramdisk = load_file(new_ramdisk_fd, new_ramdisk_size, "new vendor ramdisk");
419 if (!new_ramdisk.ok()) return new_ramdisk.error();
420
421 Result<std::string> new_vendor_boot;
422 if (ramdisk_name == "default") {
423 new_vendor_boot = replace_default_vendor_ramdisk(*vendor_boot, *new_ramdisk);
424 } else {
425 new_vendor_boot = replace_vendor_ramdisk_fragment(ramdisk_name, *vendor_boot, *new_ramdisk);
426 }
427 if (!new_vendor_boot.ok()) return new_vendor_boot.error();
428 if (auto res = store_file(vendor_boot_fd, *new_vendor_boot, "new vendor boot image"); !res.ok())
429 return res.error();
430
431 return {};
432 }
433