ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_stbtt_guard.c
Go to the documentation of this file.
1
18
19#include "ra8_stbtt_guard.h"
20
21#include <stddef.h>
22#include <stdint.h>
23
24#include "ra8_attributes.h"
25
44
55typedef enum : uint32_t {
56 k_sfnt_tag_cmap = 0x636D6170U,
57 k_sfnt_tag_head = 0x68656164U,
58 k_sfnt_tag_maxp = 0x6D617870U,
60
79
86typedef enum : uint8_t {
91
111static uint16_t internal_rd_be_u16(const uint8_t* p)
112{
113 return (uint16_t)(((uint32_t)p[0] << k_sfnt_shift_8) | (uint32_t)p[1]);
114}
115
135static uint32_t internal_rd_be_u32(const uint8_t* p)
136{
137 return ((uint32_t)p[0] << k_sfnt_shift_24) | ((uint32_t)p[1] << k_sfnt_shift_16) |
138 ((uint32_t)p[2] << k_sfnt_shift_8) | (uint32_t)p[3];
139}
140
180static bool internal_table_internal_in_bounds(const uint8_t* data,
181 uint64_t buf_len,
182 uint32_t tag,
183 uint64_t t_off)
184{
185 if (tag == (uint32_t)k_sfnt_tag_cmap) {
186 if ((t_off + (uint64_t)k_cmap_header_bytes) > buf_len) {
187 return false;
188 }
189 const uint32_t sub_tables =
190 internal_rd_be_u16(&data[(size_t)(t_off + (uint64_t)k_cmap_num_tables_off)]);
191 const uint64_t sub_end = t_off + (uint64_t)k_cmap_header_bytes +
192 ((uint64_t)sub_tables * (uint64_t)k_cmap_record_bytes);
193 return sub_end <= buf_len;
194 }
195 if (tag == (uint32_t)k_sfnt_tag_head) {
196 return (t_off + (uint64_t)k_head_loc_format_end) <= buf_len;
197 }
198 if (tag == (uint32_t)k_sfnt_tag_maxp) {
199 return (t_off + (uint64_t)k_maxp_num_glyphs_end) <= buf_len;
200 }
201 return true;
202}
203
204bool ra8_stbtt_sfnt_dir_in_bounds(const uint8_t* data, size_t len, uint32_t fontstart)
205{
206 if (data == nullptr) {
207 return false;
208 }
209
210 const uint64_t buf_len = (uint64_t)len;
211 const uint64_t start = (uint64_t)fontstart;
212
213 /* (1) The 12-byte offset table must lie within the buffer before we may
214 * read numTables from it. Computed in uint64_t so a hostile fontstart
215 * (up to UINT32_MAX) cannot wrap the sum. */
216 if ((start + (uint64_t)k_sfnt_offset_table_bytes) > buf_len) {
217 return false;
218 }
219 const uint32_t num_tables =
220 internal_rd_be_u16(&data[(size_t)(start + (uint64_t)k_sfnt_num_tables_off)]);
221
222 /* (2) The whole table directory (num_tables records of 16 bytes) must fit.
223 * num_tables is a uint16 (<= 65535), so the product is bounded. */
224 const uint64_t dir_end = start + (uint64_t)k_sfnt_offset_table_bytes +
225 ((uint64_t)num_tables * (uint64_t)k_sfnt_table_record_bytes);
226 if (dir_end > buf_len) {
227 return false;
228 }
229
230 /* (3) Every table's declared [offset, offset + length) extent must fit, and
231 * (4) the reads stbtt_InitFont makes INSIDE cmap / head / maxp must also stay
232 * in-bounds -- the top-level extent alone does not bound cmap's internal
233 * 8 * numTables sub-directory walk. Both record fields are uint32, so the
234 * sum is at most 0x1FFFFFFFE and cannot overflow uint64_t. The record-header
235 * reads at rec+0 / rec+8 / rec+12 are proven in-bounds by check (2) above
236 * (rec + 16 <= dir_end <= len). */
237 for (uint32_t i = 0U; i < num_tables; ++i) {
238 const uint64_t rec = start + (uint64_t)k_sfnt_offset_table_bytes +
239 ((uint64_t)i * (uint64_t)k_sfnt_table_record_bytes);
240 const uint32_t tag = internal_rd_be_u32(&data[(size_t)(rec + (uint64_t)k_sfnt_record_tag_off)]);
241 const uint64_t t_off =
242 (uint64_t)internal_rd_be_u32(&data[(size_t)(rec + (uint64_t)k_sfnt_record_offset_off)]);
243 const uint64_t t_len =
244 (uint64_t)internal_rd_be_u32(&data[(size_t)(rec + (uint64_t)k_sfnt_record_length_off)]);
245 if ((t_off + t_len) > buf_len) {
246 return false;
247 }
248 if (!internal_table_internal_in_bounds(data, buf_len, tag, t_off)) {
249 return false;
250 }
251 }
252
253 return true;
254}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
sfnt_internal_layout_t
Byte offsets stbtt_InitFont() reads inside cmap / head / maxp.
@ k_maxp_num_glyphs_end
maxp: InitFont reads uint16 at maxp+4 -> +6.
@ k_head_loc_format_end
head: InitFont reads uint16 at head+50 -> +52.
@ k_cmap_header_bytes
cmap: version(2) + numTables(2) before records.
@ k_cmap_num_tables_off
cmap: uint16 numTables offset in the table.
@ k_cmap_record_bytes
cmap: one encoding-record size, bytes.
bool ra8_stbtt_sfnt_dir_in_bounds(const uint8_t *data, size_t len, uint32_t fontstart)
Verify that a font's sfnt table directory lies within its buffer.
static uint32_t internal_rd_be_u32(const uint8_t *p)
Read a big-endian uint32 from a four-byte, in-bounds location.
sfnt_shift_t
Bit-shift distances for assembling big-endian scalars.
@ k_sfnt_shift_16
Two-byte shift.
@ k_sfnt_shift_24
Three-byte shift.
@ k_sfnt_shift_8
One-byte shift.
sfnt_layout_t
Byte offsets and sizes of the sfnt offset table and table records.
@ k_sfnt_table_record_bytes
Size of one table directory record, bytes.
@ k_sfnt_record_offset_off
uint32 table-offset field offset in a record.
@ k_sfnt_offset_table_bytes
sfnt offset table (header) size, bytes.
@ k_sfnt_record_length_off
uint32 table-length field offset in a record.
@ k_sfnt_record_tag_off
4-byte table tag offset in a record.
@ k_sfnt_num_tables_off
uint16 numTables offset in the offset table.
static bool internal_table_internal_in_bounds(const uint8_t *data, uint64_t buf_len, uint32_t tag, uint64_t t_off)
Prove the reads stbtt_InitFont() makes inside a known table stay in-bounds, given the table's already...
static uint16_t internal_rd_be_u16(const uint8_t *p)
Read a big-endian uint16 from a two-byte, in-bounds location.
sfnt_tag_t
Big-endian sfnt table tags whose internal layout stbtt_InitFont() reads with an attacker-controlled c...
@ k_sfnt_tag_cmap
'cmap' – character-to-glyph mapping.
@ k_sfnt_tag_maxp
'maxp' – maximum profile (numGlyphs).
@ k_sfnt_tag_head
'head' – font header (indexToLocFormat).
sfnt (TrueType/OpenType) table-directory bounds guard for stb_truetype.