fix(bitscrambler): validate and safely load LUT data

Reject LUT loads that exceed the hardware address space and assemble
each 32-bit LUT word via memcpy so unaligned and partial inputs do not
read past the caller buffer. Add regression tests for oversized and
partial unaligned LUT loads.
This commit is contained in:
morris
2026-07-09 18:54:55 +08:00
parent a68dbf4033
commit c284036a36
3 changed files with 67 additions and 2 deletions

View File

@@ -89,6 +89,7 @@ esp_err_t bitscrambler_load_program(bitscrambler_handle_t handle, const void *pr
* @return
* - ESP_OK
* - ESP_ERR_INVALID_ARG: Invalid handle or lut pointer
* - ESP_ERR_INVALID_SIZE: LUT data exceeds hardware capacity
*/
esp_err_t bitscrambler_load_lut(bitscrambler_handle_t handle, void *lut, size_t size_bytes);

View File

@@ -23,6 +23,8 @@ static const char *TAG = "bitscrambler";
#define BITSCRAMBLER_BINARY_VER 1 //max version we're compatible with
#define BITSCRAMBLER_HW_REV 0
// LUT index register is 11 bits wide, so the LUT address space is 2048 bytes.
#define BITSCRAMBLER_LUT_MAX_BYTES (1U << 11)
// After a reset, it can take a few cycles for the BitScrambler to actually be
// reset. We check this many times for this; if it takes longer the hardware
@@ -241,12 +243,28 @@ esp_err_t bitscrambler_load_lut(bitscrambler_handle_t handle, void *lut, size_t
if (!handle || !lut) {
return ESP_ERR_INVALID_ARG;
}
uint32_t *lut_words = (uint32_t*)lut;
if (size_bytes > BITSCRAMBLER_LUT_MAX_BYTES) {
return ESP_ERR_INVALID_SIZE;
}
const uint8_t *lut_bytes = (const uint8_t *)lut;
bitscrambler_lut_width_t lut_width = bitscrambler_ll_get_lut_width(handle->hw, handle->cfg.dir);
bitscrambler_ll_set_lut_width(handle->hw, handle->cfg.dir, BITSCRAMBLER_LUT_WIDTH_32BIT);
size_t size_words = (size_bytes + 3) / 4;
for (int w = 0; w < size_words; w++) {
bitscrambler_ll_lutmem_write(handle->hw, handle->cfg.dir, w, lut_words[w]);
// Assemble each LUT entry byte-wise before writing it to hardware:
// 1) callers are allowed to pass unaligned buffers, so reading via a
// uint32_t * could fault or perform an unaligned access on some targets;
// 2) the final LUT word may be only partially supplied, and copying the
// valid bytes into a zero-initialized word avoids reading past the end
// of the caller's buffer and leaking adjacent memory into the LUT.
uint32_t lut_word = 0;
size_t offset = w * sizeof(lut_word);
size_t bytes_to_copy = size_bytes - offset;
if (bytes_to_copy > sizeof(lut_word)) {
bytes_to_copy = sizeof(lut_word);
}
memcpy(&lut_word, lut_bytes + offset, bytes_to_copy);
bitscrambler_ll_lutmem_write(handle->hw, handle->cfg.dir, w, lut_word);
}
bitscrambler_ll_set_lut_width(handle->hw, handle->cfg.dir, lut_width);
return ESP_OK;

View File

@@ -5,6 +5,7 @@
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "sdkconfig.h"
#include "unity.h"
#include "unity_test_utils.h"
@@ -171,6 +172,51 @@ TEST_CASE("BitScrambler with LUT32", "[bs]")
free(data_out);
}
TEST_CASE("BitScrambler loads partial LUT from unaligned buffer", "[bs]")
{
const size_t len = 32;
const uint32_t expected_lut_words[] = {
0xA0011111,
0xA0022222,
0xA0033333,
0x00000044,
};
const uint32_t lut_source_words[] = {
0xA0011111,
0xA0022222,
0xA0033333,
0xA0044444,
};
uint8_t lut_bytes[sizeof(lut_source_words) + 4] = {0};
uint8_t *unaligned_lut = lut_bytes + 1;
const size_t lut_size = 13;
uint8_t *data_in = heap_caps_aligned_calloc(8, 1, len, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
uint32_t *data_out = heap_caps_aligned_calloc(8, 1, len * 4, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
TEST_ASSERT_NOT_NULL(data_in);
TEST_ASSERT_NOT_NULL(data_out);
memcpy(unaligned_lut, lut_source_words, sizeof(lut_source_words));
unaligned_lut[lut_size] = 0xAB;
unaligned_lut[lut_size + 1] = 0xCD;
unaligned_lut[lut_size + 2] = 0xEF;
bitscrambler_handle_t bs;
TEST_ESP_OK(bitscrambler_loopback_create(&bs, SOC_BITSCRAMBLER_ATTACH_GPSPI2, len * 4));
TEST_ESP_OK(bitscrambler_load_program(bs, bitscrambler_program_lut32));
TEST_ESP_OK(bitscrambler_load_lut(bs, unaligned_lut, lut_size));
size_t res_len = 0;
TEST_ESP_OK(bitscrambler_loopback_run(bs, data_in, len, data_out, len * 4, &res_len));
bitscrambler_free(bs);
for (size_t i = 0; i < res_len / 4; i++) {
TEST_ASSERT_EQUAL(expected_lut_words[i % 4], data_out[i]);
}
TEST_ASSERT_EQUAL(len * 4, res_len);
free(data_in);
free(data_out);
}
TEST_CASE("BitScrambler with loop instruction", "[bs]")
{
uint8_t data_in[] = {0x00, 0xFF, 0x55};