mirror of
https://github.com/linux-msm/qdl.git
synced 2026-02-25 13:12:25 -08:00
The `part_entry_lba` field in `struct gpt_header` is uint64_t, but the local `lba` variable was declared as `unsigned int`, silently truncating values above UINT_MAX. The buffer `lba_buf[10]` is also too small: a 64-bit value can be up to 20 decimal digits, requiring at least 21 bytes including the null terminator. Combined with an unchecked sprintf(), this is a stack buffer overflow for any LBA value exceeding 9 digits. Fix by: - declaring `lba` as uint64_t to match the source type - increasing lba_buf to 21 bytes - using snprintf() with sizeof to prevent overruns - using PRIu64 for correct format specifier Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
284 lines
6.6 KiB
C
284 lines
6.6 KiB
C
// SPDX-License-Identifier: BSD-3-Clause
|
|
/*
|
|
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#define _FILE_OFFSET_BITS 64
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <assert.h>
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
#include <inttypes.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <unistd.h>
|
|
|
|
#include "qdl.h"
|
|
#include "gpt.h"
|
|
|
|
struct gpt_guid {
|
|
uint32_t data1;
|
|
uint16_t data2;
|
|
uint16_t data3;
|
|
uint8_t data4[8];
|
|
} __attribute__((packed));
|
|
|
|
static const struct gpt_guid gpt_zero_guid = {0};
|
|
|
|
struct gpt_header {
|
|
uint8_t signature[8];
|
|
uint32_t revision;
|
|
uint32_t header_size;
|
|
uint32_t header_crc32;
|
|
uint32_t reserved;
|
|
uint64_t current_lba;
|
|
uint64_t backup_lba;
|
|
uint64_t first_usable_lba;
|
|
uint64_t last_usable_lba;
|
|
struct gpt_guid disk_guid;
|
|
uint64_t part_entry_lba;
|
|
uint32_t num_part_entries;
|
|
uint32_t part_entry_size;
|
|
uint32_t part_array_crc32;
|
|
uint8_t reserved2[420];
|
|
} __attribute__((packed));
|
|
|
|
struct gpt_entry {
|
|
struct gpt_guid type_guid;
|
|
struct gpt_guid unique_guid;
|
|
uint64_t first_lba;
|
|
uint64_t last_lba;
|
|
uint64_t attrs;
|
|
uint16_t name_utf16le[36];
|
|
} __attribute__((packed));
|
|
|
|
struct gpt_partition {
|
|
const char *name;
|
|
unsigned int partition;
|
|
unsigned int start_sector;
|
|
unsigned int num_sectors;
|
|
|
|
struct gpt_partition *next;
|
|
};
|
|
|
|
static struct gpt_partition *gpt_partitions;
|
|
static struct gpt_partition *gpt_partitions_last;
|
|
|
|
static void utf16le_to_utf8(uint16_t *in, size_t in_len, uint8_t *out, size_t out_len)
|
|
{
|
|
uint32_t codepoint;
|
|
uint16_t high;
|
|
uint16_t low;
|
|
uint16_t w;
|
|
size_t i;
|
|
size_t j = 0;
|
|
|
|
for (i = 0; i < in_len; i++) {
|
|
w = in[i];
|
|
|
|
if (w >= 0xd800 && w <= 0xdbff) {
|
|
high = w - 0xd800;
|
|
|
|
if (i < in_len) {
|
|
w = in[++i];
|
|
if (w >= 0xdc00 && w <= 0xdfff) {
|
|
low = w - 0xdc00;
|
|
codepoint = (((uint32_t)high << 10) | low) + 0x10000;
|
|
} else {
|
|
/* Surrogate without low surrogate */
|
|
codepoint = 0xfffd;
|
|
}
|
|
} else {
|
|
/* Lone high surrogate at end of string */
|
|
codepoint = 0xfffd;
|
|
}
|
|
} else if (w >= 0xdc00 && w <= 0xdfff) {
|
|
/* Low surrogate without high */
|
|
codepoint = 0xfffd;
|
|
} else {
|
|
codepoint = w;
|
|
}
|
|
|
|
if (codepoint == 0)
|
|
break;
|
|
|
|
if (codepoint <= 0x7f) {
|
|
if (j + 1 >= out_len)
|
|
break;
|
|
out[j++] = (uint8_t)codepoint;
|
|
} else if (codepoint <= 0x7ff) {
|
|
if (j + 2 >= out_len)
|
|
break;
|
|
out[j++] = 0xc0 | ((codepoint >> 6) & 0x1f);
|
|
out[j++] = 0x80 | (codepoint & 0x3f);
|
|
} else if (codepoint <= 0xffff) {
|
|
if (j + 3 >= out_len)
|
|
break;
|
|
out[j++] = 0xe0 | ((codepoint >> 12) & 0x0f);
|
|
out[j++] = 0x80 | ((codepoint >> 6) & 0x3f);
|
|
out[j++] = 0x80 | (codepoint & 0x3f);
|
|
} else if (codepoint <= 0x10ffff) {
|
|
if (j + 4 >= out_len)
|
|
break;
|
|
out[j++] = 0xf0 | ((codepoint >> 18) & 0x07);
|
|
out[j++] = 0x80 | ((codepoint >> 12) & 0x3f);
|
|
out[j++] = 0x80 | ((codepoint >> 6) & 0x3f);
|
|
out[j++] = 0x80 | (codepoint & 0x3f);
|
|
}
|
|
}
|
|
|
|
out[j] = '\0';
|
|
}
|
|
|
|
static int gpt_load_table_from_partition(struct qdl_device *qdl, unsigned int phys_partition, bool *eof)
|
|
{
|
|
struct gpt_partition *partition;
|
|
struct gpt_entry *entry;
|
|
struct gpt_header gpt;
|
|
uint8_t buf[4096];
|
|
struct read_op op;
|
|
unsigned int offset;
|
|
uint64_t lba;
|
|
char lba_buf[21];
|
|
uint16_t name_utf16le[36];
|
|
char name[36 * 4];
|
|
int ret;
|
|
unsigned int i;
|
|
|
|
memset(&op, 0, sizeof(op));
|
|
|
|
op.sector_size = qdl->sector_size;
|
|
op.start_sector = "1";
|
|
op.num_sectors = 1;
|
|
op.partition = phys_partition;
|
|
|
|
memset(&buf, 0, sizeof(buf));
|
|
ret = firehose_read_buf(qdl, &op, &gpt, sizeof(gpt));
|
|
if (ret) {
|
|
/* Assume that we're beyond the last partition */
|
|
*eof = true;
|
|
return -1;
|
|
}
|
|
|
|
if (memcmp(gpt.signature, "EFI PART", 8)) {
|
|
ux_err("partition %d has not GPT header\n", phys_partition);
|
|
return 0;
|
|
}
|
|
|
|
if (gpt.part_entry_size > qdl->sector_size || gpt.num_part_entries > 1024) {
|
|
ux_debug("partition %d has invalid GPT header\n", phys_partition);
|
|
return -1;
|
|
}
|
|
|
|
ux_debug("Loading GPT table from physical partition %d\n", phys_partition);
|
|
for (i = 0; i < gpt.num_part_entries; i++) {
|
|
offset = (i * gpt.part_entry_size) % qdl->sector_size;
|
|
|
|
if (offset == 0) {
|
|
lba = gpt.part_entry_lba + i * gpt.part_entry_size / qdl->sector_size;
|
|
snprintf(lba_buf, sizeof(lba_buf), "%" PRIu64, lba);
|
|
op.start_sector = lba_buf;
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
ret = firehose_read_buf(qdl, &op, buf, sizeof(buf));
|
|
if (ret) {
|
|
ux_err("failed to read GPT partition entries from %d:%u\n", phys_partition, lba);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
entry = (struct gpt_entry *)(buf + offset);
|
|
|
|
if (!memcmp(&entry->type_guid, &gpt_zero_guid, sizeof(struct gpt_guid)))
|
|
continue;
|
|
|
|
memcpy(name_utf16le, entry->name_utf16le, sizeof(name_utf16le));
|
|
utf16le_to_utf8(name_utf16le, 36, (uint8_t *)name, sizeof(name));
|
|
|
|
partition = calloc(1, sizeof(*partition));
|
|
partition->name = strdup(name);
|
|
partition->partition = phys_partition;
|
|
partition->start_sector = entry->first_lba;
|
|
/* if first_lba == last_lba there is 1 sector worth of data (IE: add 1 below) */
|
|
partition->num_sectors = entry->last_lba - entry->first_lba + 1;
|
|
|
|
ux_debug(" %3d: %s start sector %u, num sectors %u\n", i, partition->name,
|
|
partition->start_sector, partition->num_sectors);
|
|
|
|
if (gpt_partitions) {
|
|
gpt_partitions_last->next = partition;
|
|
gpt_partitions_last = partition;
|
|
} else {
|
|
gpt_partitions = partition;
|
|
gpt_partitions_last = partition;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gpt_load_tables(struct qdl_device *qdl)
|
|
{
|
|
unsigned int i;
|
|
bool eof = false;
|
|
int ret = 0;
|
|
|
|
if (gpt_partitions)
|
|
return 0;
|
|
|
|
for (i = 0; ; i++) {
|
|
ret = gpt_load_table_from_partition(qdl, i, &eof);
|
|
if (ret)
|
|
break;
|
|
}
|
|
|
|
return eof ? 0 : ret;
|
|
}
|
|
|
|
int gpt_find_by_name(struct qdl_device *qdl, const char *name, int *phys_partition,
|
|
unsigned int *start_sector, unsigned int *num_sectors)
|
|
{
|
|
struct gpt_partition *gpt_part;
|
|
bool found = false;
|
|
int ret;
|
|
|
|
if (qdl->dev_type == QDL_DEVICE_SIM)
|
|
return 0;
|
|
|
|
ret = gpt_load_tables(qdl);
|
|
if (ret < 0)
|
|
return -1;
|
|
|
|
for (gpt_part = gpt_partitions; gpt_part; gpt_part = gpt_part->next) {
|
|
if (*phys_partition >= 0 && gpt_part->partition != (unsigned int)(*phys_partition))
|
|
continue;
|
|
|
|
if (strcmp(gpt_part->name, name))
|
|
continue;
|
|
|
|
if (found) {
|
|
ux_err("duplicate candidates for partition \"%s\" found\n", name);
|
|
return -1;
|
|
}
|
|
|
|
*phys_partition = gpt_part->partition;
|
|
*start_sector = gpt_part->start_sector;
|
|
*num_sectors = gpt_part->num_sectors;
|
|
|
|
found = true;
|
|
}
|
|
|
|
if (!found) {
|
|
if (*phys_partition >= 0)
|
|
ux_err("no partition \"%s\" found on physical partition %d\n", name, *phys_partition);
|
|
else
|
|
ux_err("no partition \"%s\" found\n", name);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|