/* append area to existing structure, return new total size if successful */ int fmap_append_area(struct fmap **fmap, uint32_t offset, uint32_t size, const uint8_t *name, uint16_t flags) { struct fmap_area *area; int orig_size, new_size; if ((fmap == NULL || *fmap == NULL) || (name == NULL)) return -1; /* too many areas */ if ((*fmap)->nareas >= 0xffff) return -1; orig_size = fmap_size(*fmap); new_size = orig_size + sizeof(*area); *fmap = realloc(*fmap, new_size); if (*fmap == NULL) return -1; area = (struct fmap_area *)((uint8_t *)*fmap + orig_size); memset(area, 0, sizeof(*area)); memcpy(&area->offset, &offset, sizeof(area->offset)); memcpy(&area->size, &size, sizeof(area->size)); memccpy(&area->name, name, '\0', FMAP_STRLEN); memcpy(&area->flags, &flags, sizeof(area->flags)); (*fmap)->nareas++; return new_size; }
partitioned_file_t *partitioned_file_create(const char *filename, struct buffer *flashmap) { assert(filename); assert(flashmap); assert(flashmap->data); if (fmap_find((const uint8_t *)flashmap->data, flashmap->size) != 0) { ERROR("Attempted to create a partitioned image out of something that isn't an FMAP\n"); return NULL; } struct fmap *bootstrap_fmap = (struct fmap *)flashmap->data; const struct fmap_area *fmap_area = fmap_find_area(bootstrap_fmap, SECTION_NAME_FMAP); if (!fmap_area) { ERROR("Provided FMAP missing '%s' region\n", SECTION_NAME_FMAP); return NULL; } if (count_selected_fmap_entries(bootstrap_fmap, partitioned_file_fmap_select_children_of, fmap_area)) { ERROR("Provided FMAP's '%s' region contains other regions\n", SECTION_NAME_FMAP); return NULL; } int fmap_len = fmap_size(bootstrap_fmap); if (fmap_len < 0) { ERROR("Unable to determine size of provided FMAP\n"); return NULL; } assert((size_t)fmap_len <= flashmap->size); if ((uint32_t)fmap_len > fmap_area->size) { ERROR("Provided FMAP's '%s' region needs to be at least %d bytes\n", SECTION_NAME_FMAP, fmap_len); return NULL; } partitioned_file_t *file = partitioned_file_create_flat(filename, bootstrap_fmap->size); if (!file) return NULL; struct buffer fmap_region; buffer_splice(&fmap_region, &file->buffer, fmap_area->offset, fmap_area->size); memcpy(fmap_region.data, bootstrap_fmap, fmap_len); if (!partitioned_file_write_region(file, &fmap_region)) { partitioned_file_close(file); return NULL; } file->fmap = (struct fmap *)(file->buffer.data + fmap_area->offset); return file; }
static int fmap_size_test(void) { status = fail; if (fmap_size(NULL) >= 0) { printf("FAILURE: failed to abort on NULL pointer input\n"); goto fmap_size_test_exit; } status = pass; fmap_size_test_exit: return status; }
partitioned_file_t *partitioned_file_reopen(const char *filename, partitioned_file_flat_decider_t flat_override) { assert(filename); partitioned_file_t *file = reopen_flat_file(filename); if (!file) return NULL; if (flat_override && flat_override(&file->buffer)) { INFO("Opening image as a flat file in response to explicit request\n"); return file; } long fmap_region_offset = fmap_find((const uint8_t *)file->buffer.data, file->buffer.size); if (fmap_region_offset < 0) { INFO("Opening image as a flat file because it doesn't contain any FMAP\n"); return file; } file->fmap = (struct fmap *)(file->buffer.data + fmap_region_offset); if (file->fmap->size > file->buffer.size) { int fmap_region_size = fmap_size(file->fmap); ERROR("FMAP records image size as %u, but file is only %zu bytes%s\n", file->fmap->size, file->buffer.size, fmap_region_offset == 0 && (signed)file->buffer.size == fmap_region_size ? " (is it really an image, or *just* an FMAP?)" : " (did something truncate this file?)"); partitioned_file_close(file); return NULL; } const struct fmap_area *fmap_fmap_entry = fmap_find_area(file->fmap, SECTION_NAME_FMAP); if ((long)fmap_fmap_entry->offset != fmap_region_offset) { ERROR("FMAP's '%s' section doesn't point back to FMAP start (did something corrupt this file?)\n", SECTION_NAME_FMAP); partitioned_file_close(file); return NULL; } return file; }
/* brute force linear search */ static long int fmap_lsearch(const uint8_t *image, size_t len) { unsigned long int offset; int fmap_found = 0; for (offset = 0; offset < len - strlen(FMAP_SIGNATURE); offset++) { if (is_valid_fmap((const struct fmap *)&image[offset])) { fmap_found = 1; break; } } if (!fmap_found) return -1; if (offset + fmap_size((const struct fmap *)&image[offset]) > len) return -1; return offset; }
/* brute force linear search */ static long int fmap_lsearch(const uint8_t *image, size_t len) { long int offset; int fmap_found = 0; for (offset = 0; offset < len - strlen(FMAP_SIGNATURE); offset++) { if (!memcmp(&image[offset], FMAP_SIGNATURE, strlen(FMAP_SIGNATURE))) { fmap_found = 1; break; } } if (!fmap_found) return -1; if (offset + fmap_size((struct fmap *)&image[offset]) > len) return -1; return offset; }
/* if image length is a power of 2, use binary search */ static long int fmap_bsearch(const uint8_t *image, size_t len) { long int offset = -1; int fmap_found = 0, stride; /* * For efficient operation, we start with the largest stride possible * and then decrease the stride on each iteration. Also, check for a * remainder when modding the offset with the previous stride. This * makes it so that each offset is only checked once. */ for (stride = len / 2; stride >= 1; stride /= 2) { if (fmap_found) break; for (offset = 0; offset < len - strlen(FMAP_SIGNATURE); offset += stride) { if ((offset % (stride * 2) == 0) && (offset != 0)) continue; if (!memcmp(&image[offset], FMAP_SIGNATURE, strlen(FMAP_SIGNATURE))) { fmap_found = 1; break; } } } if (!fmap_found) return -1; if (offset + fmap_size((struct fmap *)&image[offset]) > len) return -1; return offset; }
static int fmap_get_csum_test(struct fmap *fmap) { uint8_t *digest = NULL, *image = NULL; /* assume 0x100-0x10100 is marked "static" and is filled with 0x00 */ int image_size = 0x20000; uint8_t csum[SHA_DIGEST_SIZE] = { 0x1a, 0xdc, 0x95, 0xbe, 0xbe, 0x9e, 0xea, 0x8c, 0x11, 0x2d, 0x40, 0xcd, 0x04, 0xab, 0x7a, 0x8d, 0x75, 0xc4, 0xf9, 0x61 }; status = fail; if ((fmap_get_csum(NULL, image_size, &digest) >= 0) || (fmap_get_csum(image, image_size, NULL) >= 0)) { printf("failed to abort on NULL pointer input\n"); goto fmap_get_csum_test_exit; } image = calloc(image_size, 1); memcpy(image, fmap, fmap_size(fmap)); if (fmap_get_csum(image, image_size, &digest) != SHA_DIGEST_SIZE) { printf("FAILURE: failed to calculate checksum\n"); goto fmap_get_csum_test_exit; } if (memcmp(digest, csum, SHA_DIGEST_SIZE)) { printf("FAILURE: checksum is incorrect\n"); goto fmap_get_csum_test_exit; } status = pass; fmap_get_csum_test_exit: free(image); free(digest); return status; }
static int fmap_find_test(struct fmap *fmap) { uint8_t *buf; size_t total_size, offset; status = fail; /* * Note: In these tests, we'll use fmap_find() and control usage of * lsearch and bsearch by using a power-of-2 total_size. For lsearch, * use total_size - 1. For bsearch, use total_size. */ total_size = 0x100000; buf = calloc(total_size, 1); /* test if image length is zero */ if (fmap_find(buf, 0) >= 0) { printf("FAILURE: failed to abort on zero-length image\n"); goto fmap_find_test_exit; } /* test if no fmap exists */ if (fmap_find(buf, total_size - 1) >= 0) { printf("FAILURE: lsearch returned false positive\n"); goto fmap_find_test_exit; } if (fmap_find(buf, total_size) >= 0) { printf("FAILURE: bsearch returned false positive\n"); goto fmap_find_test_exit; } /* simple test case: fmap at (total_size / 2) + 1 */ offset = (total_size / 2) + 1; memcpy(&buf[offset], fmap, fmap_size(fmap)); if ((unsigned)fmap_find(buf, total_size - 1) != offset) { printf("FAILURE: lsearch failed to find fmap\n"); goto fmap_find_test_exit; } if ((unsigned)fmap_find(buf, total_size) != offset) { printf("FAILURE: bsearch failed to find fmap\n"); goto fmap_find_test_exit; } /* test bsearch if offset is at 0 */ offset = 0; memset(buf, 0, total_size); memcpy(buf, fmap, fmap_size(fmap)); if ((unsigned)fmap_find(buf, total_size) != offset) { printf("FAILURE: bsearch failed to find fmap at offset 0\n"); goto fmap_find_test_exit; } /* test overrun detection */ memset(buf, 0, total_size); memcpy(&buf[total_size - fmap_size(fmap) + 1], fmap, fmap_size(fmap) + 1); if (fmap_find(buf, total_size - 1) >= 0) { printf("FAILURE: lsearch failed to catch overrun\n"); goto fmap_find_test_exit; } if (fmap_find(buf, total_size) >= 0) { printf("FAILURE: bsearch failed to catch overrun\n"); goto fmap_find_test_exit; } status = pass; fmap_find_test_exit: free(buf); return status; }