mirror of https://github.com/wolfSSL/wolfBoot.git
187 lines
6.3 KiB
C
187 lines
6.3 KiB
C
/* unit-t10xx-qe-firmware.c
|
|
*
|
|
* Regression test for F-8000: qe_check_firmware() in hal/nxp_t10xx.c
|
|
* validated the QE microcode header for self-consistency (magic,
|
|
* version, count, length == computed size) but never bounded the
|
|
* per-microcode code_offset against the declared image length.
|
|
* qe_upload_firmware() then reads code_offset + 4*count bytes from
|
|
* the firmware start and copies them into QE IRAM (and programs the
|
|
* microcode's trap table), so a structurally valid blob with an
|
|
* out-of-image offset makes wolfBoot copy arbitrary memory into the
|
|
* engine and run unauthenticated microcode with serial DMA enabled
|
|
* before the image is authenticated.
|
|
*
|
|
* The test extracts the real qe_* structures and qe_check_firmware()
|
|
* from hal/nxp_t10xx.c (generated by the Makefile) and builds the
|
|
* blobs in memory. The check-only path needs no register access.
|
|
*
|
|
* Copyright (C) 2026 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfBoot.
|
|
*
|
|
* wolfBoot is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfBoot is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
#include <check.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include "printf.h"
|
|
|
|
/* Number of RISC processors the target exposes (real: hal/nxp_t10xx.c). */
|
|
#define QE_MAX_RISC 1
|
|
|
|
/* The real qe_* structures and qe_check_firmware() from hal/nxp_t10xx.c
|
|
* (extracted by the Makefile). */
|
|
#include "t10xx_qe_firmware_extract.h"
|
|
|
|
#define BLOB_SZ 4096
|
|
static uint8_t g_blob[BLOB_SZ];
|
|
|
|
/* Build a self-consistent blob: header, one microcode, 'code_words'
|
|
* words of code at 'code_offset', trailing CRC word. Returns the
|
|
* (self-consistent) declared length. */
|
|
static size_t make_blob(uint32_t code_offset, uint32_t code_words,
|
|
uint32_t *code)
|
|
{
|
|
struct qe_firmware *fw = (struct qe_firmware *)g_blob;
|
|
size_t calc = sizeof(struct qe_firmware);
|
|
uint32_t i;
|
|
|
|
memset(g_blob, 0, sizeof(g_blob));
|
|
fw->header.length = 0; /* filled below */
|
|
fw->header.magic[0] = 'Q';
|
|
fw->header.magic[1] = 'E';
|
|
fw->header.magic[2] = 'F';
|
|
fw->header.version = 1;
|
|
fw->count = 1;
|
|
fw->split = 1;
|
|
fw->microcode[0].count = code_words;
|
|
fw->microcode[0].code_offset = code_offset;
|
|
strncpy((char *)fw->microcode[0].id, "test-ucode", 31);
|
|
calc += (size_t)4 * code_words;
|
|
|
|
/* Materialize the code words only when they actually fit in the
|
|
* blob; an out-of-image offset is exactly what the bounds check
|
|
* must catch, and the check path never reads the words. */
|
|
if (code_offset + 4 * code_words <= BLOB_SZ) {
|
|
for (i = 0; i < code_words; i++)
|
|
((uint32_t *)(void *)(g_blob + code_offset))[i] =
|
|
0xA0000000U + i;
|
|
}
|
|
/* Trailing CRC word placeholder (CRC not checked without
|
|
* ENABLE_QE_CRC32, but the slot must exist for the length). */
|
|
if (calc + sizeof(uint32_t) <= BLOB_SZ)
|
|
*(uint32_t *)(void *)(g_blob + calc) = 0;
|
|
|
|
fw->header.length = (uint32_t)(calc + sizeof(uint32_t));
|
|
return fw->header.length;
|
|
}
|
|
|
|
/* A valid, in-image blob is accepted. */
|
|
START_TEST(test_valid_blob_accepted)
|
|
{
|
|
struct qe_firmware *fw = (struct qe_firmware *)g_blob;
|
|
size_t len = make_blob((uint32_t)sizeof(struct qe_firmware), 16, NULL);
|
|
|
|
(void)fw;
|
|
ck_assert_uint_lt(len, BLOB_SZ);
|
|
ck_assert_int_eq(qe_check_firmware(fw, "QE"), 0);
|
|
}
|
|
END_TEST
|
|
|
|
/* A self-consistent blob whose microcode offset lies past the image
|
|
* must be rejected. Pre-fix it was accepted and the upload read
|
|
* arbitrary memory past the blob. */
|
|
START_TEST(test_mcode_offset_past_image_rejected)
|
|
{
|
|
struct qe_firmware *fw = (struct qe_firmware *)g_blob;
|
|
|
|
(void)make_blob(0x100000, 16, NULL);
|
|
ck_assert_int_eq(qe_check_firmware(fw, "QE"), -1);
|
|
}
|
|
END_TEST
|
|
|
|
/* A self-consistent blob whose microcode words extend past the end of
|
|
* the image must be rejected. */
|
|
START_TEST(test_mcode_words_past_image_rejected)
|
|
{
|
|
struct qe_firmware *fw = (struct qe_firmware *)g_blob;
|
|
uint32_t base = (uint32_t)sizeof(struct qe_firmware);
|
|
uint32_t length;
|
|
|
|
/* Four words: the declared length is base + 4*4 + 4, and stays
|
|
* self-consistent no matter where the words are pointed at. */
|
|
length = (uint32_t)make_blob(base, 4, NULL);
|
|
ck_assert_uint_eq(length, base + 4 * 4 + sizeof(uint32_t));
|
|
|
|
/* Shift the offset 8 bytes into the image: the words now end 8
|
|
* bytes past the declared end. The old self-consistency check
|
|
* passes (it never looked at code_offset); the bounds check must
|
|
* reject. */
|
|
fw->microcode[0].code_offset = base + 8;
|
|
ck_assert_int_eq(qe_check_firmware(fw, "QE"), -1);
|
|
}
|
|
END_TEST
|
|
|
|
/* The pre-existing header checks still hold. */
|
|
START_TEST(test_bad_magic_rejected)
|
|
{
|
|
struct qe_firmware *fw = (struct qe_firmware *)g_blob;
|
|
|
|
make_blob((uint32_t)sizeof(struct qe_firmware), 16, NULL);
|
|
fw->header.magic[0] = 'X';
|
|
ck_assert_int_eq(qe_check_firmware(fw, "QE"), -1);
|
|
}
|
|
END_TEST
|
|
|
|
START_TEST(test_bad_length_rejected)
|
|
{
|
|
struct qe_firmware *fw = (struct qe_firmware *)g_blob;
|
|
|
|
make_blob((uint32_t)sizeof(struct qe_firmware), 16, NULL);
|
|
fw->header.length += 4;
|
|
ck_assert_int_eq(qe_check_firmware(fw, "QE"), -1);
|
|
}
|
|
END_TEST
|
|
|
|
Suite *t10xx_qe_firmware_suite(void)
|
|
{
|
|
Suite *s = suite_create("t10xx-qe-firmware");
|
|
TCase *tc = tcase_create("t10xx-qe-firmware");
|
|
|
|
tcase_add_test(tc, test_valid_blob_accepted);
|
|
tcase_add_test(tc, test_mcode_offset_past_image_rejected);
|
|
tcase_add_test(tc, test_mcode_words_past_image_rejected);
|
|
tcase_add_test(tc, test_bad_magic_rejected);
|
|
tcase_add_test(tc, test_bad_length_rejected);
|
|
suite_add_tcase(s, tc);
|
|
|
|
return s;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
int fails;
|
|
Suite *s = t10xx_qe_firmware_suite();
|
|
SRunner *sr = srunner_create(s);
|
|
|
|
srunner_run_all(sr, CK_NORMAL);
|
|
fails = srunner_ntests_failed(sr);
|
|
srunner_free(sr);
|
|
|
|
return fails;
|
|
}
|