1
0
mirror of https://github.com/wolfSSL/wolfssl.git synced 2026-10-24 04:09:07 +03:00

Hold the EC point formats in one allocation

The point formats extension stored its formats as a linked list with one heap
node per one byte value, which is the shape the supported groups extension had
before. A node costs sixteen bytes to hold one.

Replace the list with a block holding a count, a capacity and a flat array,
matching what the supported groups extension does now. The block starts with
room for four formats, which is more than are defined, and doubles if a caller
adds more.
This commit is contained in:
Tobias Frauenschlaeger
2026-08-21 22:40:46 +00:00
committed by Tobias Frauenschläger
parent caafb688c3
commit 296ef4be79
5 changed files with 211 additions and 52 deletions

158
src/tls.c
View File

@@ -4947,20 +4947,74 @@ static int TLSX_SupportedCurve_New(SupportedCurve** curve, word16 name,
return 0;
}
static int TLSX_PointFormat_New(PointFormat** point, byte format, void* heap)
/* Formats the first allocation holds. */
#define POINT_FORMATS_INIT_CNT 4
/* Most formats a list can hold. The capacity is a byte. */
#define POINT_FORMATS_MAX_CNT 128
/* Append() grows with (byte)(count + 1), which must not wrap. */
wc_static_assert(POINT_FORMATS_MAX_CNT < 0xFFU);
/* Powers of two, so the doubling in Grow() lands on the maximum exactly. */
wc_static_assert((POINT_FORMATS_INIT_CNT &
(POINT_FORMATS_INIT_CNT - 1)) == 0);
wc_static_assert((POINT_FORMATS_MAX_CNT &
(POINT_FORMATS_MAX_CNT - 1)) == 0);
wc_static_assert(POINT_FORMATS_INIT_CNT <= POINT_FORMATS_MAX_CNT);
/* Make room for the requested number of point formats.
*
* formats The list to grow, allocated when NULL. Updated on reallocation.
* cnt The number of formats the list has to hold.
* heap The heap used for allocation.
* returns 0 on success, otherwise failure.
*/
static int TLSX_PointFormat_Grow(PointFormats** formats, byte cnt, void* heap)
{
if (point == NULL)
PointFormats* list = *formats;
PointFormats* newList;
word16 cap = POINT_FORMATS_INIT_CNT;
if ((list != NULL) && (cnt <= list->cap))
return 0;
if (cnt > POINT_FORMATS_MAX_CNT)
return BAD_FUNC_ARG;
(void)heap;
while (cap < cnt)
cap *= 2;
*point = (PointFormat*)XMALLOC(sizeof(PointFormat), heap,
DYNAMIC_TYPE_TLSX);
if (*point == NULL)
newList = (PointFormats*)XMALLOC(sizeof(PointFormats) + cap, heap,
DYNAMIC_TYPE_TLSX);
if (newList == NULL)
return MEMORY_E;
(*point)->format = format;
(*point)->next = NULL;
newList->count = 0;
newList->cap = (byte)cap;
if (list != NULL) {
newList->count = list->count;
XMEMCPY(newList->format, list->format, list->count);
XFREE(list, heap, DYNAMIC_TYPE_TLSX);
}
*formats = newList;
return 0;
}
static int TLSX_PointFormat_New(PointFormats** formats, byte format,
void* heap)
{
PointFormats* list = NULL;
int ret;
if (formats == NULL)
return BAD_FUNC_ARG;
ret = TLSX_PointFormat_Grow(&list, 1, heap);
if (ret != 0)
return ret;
list->format[list->count++] = format;
*formats = list;
return 0;
}
@@ -4976,14 +5030,10 @@ static void TLSX_SupportedCurve_FreeAll(SupportedCurve* list, void* heap)
(void)heap;
}
static void TLSX_PointFormat_FreeAll(PointFormat* list, void* heap)
static void TLSX_PointFormat_FreeAll(PointFormats* list, void* heap)
{
PointFormat* point;
while ((point = list)) {
list = point->next;
XFREE(point, heap, DYNAMIC_TYPE_TLSX);
}
if (list != NULL)
XFREE(list, heap, DYNAMIC_TYPE_TLSX);
(void)heap;
}
@@ -5009,25 +5059,30 @@ static int TLSX_SupportedCurve_Append(SupportedCurve* list, word16 name,
return ret;
}
static int TLSX_PointFormat_Append(PointFormat* list, byte format, void* heap)
static int TLSX_PointFormat_Append(PointFormats** formats, byte format,
void* heap)
{
int ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG);
PointFormats* list;
int ret;
byte i;
while (list) {
if (list->format == format) {
ret = 0; /* format already in use */
break;
}
if ((formats == NULL) || (*formats == NULL))
return BAD_FUNC_ARG;
if (list->next == NULL) {
ret = TLSX_PointFormat_New(&list->next, format, heap);
break;
}
list = list->next;
list = *formats;
for (i = 0; i < list->count; i++) {
if (list->format[i] == format)
return 0; /* format already in use */
}
return ret;
ret = TLSX_PointFormat_Grow(formats, (byte)(list->count + 1), heap);
if (ret != 0)
return ret;
list = *formats;
list->format[list->count++] = format;
return 0;
}
#if defined(WOLFSSL_TLS13) || !defined(NO_WOLFSSL_CLIENT)
@@ -5192,15 +5247,12 @@ static word16 TLSX_SupportedCurve_GetSize(SupportedCurve* list)
#endif
static word16 TLSX_PointFormat_GetSize(PointFormat* list)
static word16 TLSX_PointFormat_GetSize(PointFormats* list)
{
PointFormat* point;
word16 length = ENUM_LEN; /* list length */
while ((point = list)) {
list = point->next;
length += ENUM_LEN; /* format length */
}
if (list != NULL)
length = (word16)(length + list->count * ENUM_LEN);
return length;
}
@@ -5224,13 +5276,14 @@ static word16 TLSX_SupportedCurve_Write(SupportedCurve* list, byte* output)
#endif
static word16 TLSX_PointFormat_Write(PointFormat* list, byte* output)
static word16 TLSX_PointFormat_Write(PointFormats* list, byte* output)
{
word16 offset = ENUM_LEN;
byte i;
while (list) {
output[offset++] = list->format;
list = list->next;
if (list != NULL) {
for (i = 0; i < list->count; i++)
output[offset++] = list->format[i];
}
output[0] = (byte)(offset - ENUM_LEN);
@@ -6211,10 +6264,19 @@ int TLSX_UseSupportedCurve(TLSX** extensions, word16 name, void* heap, int side)
return WOLFSSL_SUCCESS;
}
/* Add a point format to the extension's list, creating the extension when it
* does not exist yet. Appending may reallocate extension->data.
*
* extensions The list of extensions.
* format The point format to add.
* heap The heap used for allocation.
* returns WOLFSSL_SUCCESS on success, BAD_FUNC_ARG at
* POINT_FORMATS_MAX_CNT formats, otherwise an allocation failure.
*/
int TLSX_UsePointFormat(TLSX** extensions, byte format, void* heap)
{
TLSX* extension = NULL;
PointFormat* point = NULL;
PointFormats* formats = NULL;
int ret = 0;
if (extensions == NULL)
@@ -6223,19 +6285,19 @@ int TLSX_UsePointFormat(TLSX** extensions, byte format, void* heap)
extension = TLSX_Find(*extensions, TLSX_EC_POINT_FORMATS);
if (!extension) {
ret = TLSX_PointFormat_New(&point, format, heap);
ret = TLSX_PointFormat_New(&formats, format, heap);
if (ret != 0)
return ret;
ret = TLSX_Push(extensions, TLSX_EC_POINT_FORMATS, point, heap);
ret = TLSX_Push(extensions, TLSX_EC_POINT_FORMATS, formats, heap);
if (ret != 0) {
XFREE(point, heap, DYNAMIC_TYPE_TLSX);
XFREE(formats, heap, DYNAMIC_TYPE_TLSX);
return ret;
}
}
else {
ret = TLSX_PointFormat_Append((PointFormat*)extension->data, format,
heap);
ret = TLSX_PointFormat_Append((PointFormats**)&extension->data, format,
heap);
if (ret != 0)
return ret;
}
@@ -15523,7 +15585,7 @@ void TLSX_FreeAll(TLSX* list, void* heap)
case TLSX_EC_POINT_FORMATS:
WOLFSSL_MSG("Point Formats extension free");
PF_FREE_ALL((PointFormat*)extension->data, heap);
PF_FREE_ALL((PointFormats*)extension->data, heap);
break;
case TLSX_STATUS_REQUEST:
@@ -15765,7 +15827,7 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType,
break;
case TLSX_EC_POINT_FORMATS:
length += PF_GET_SIZE((PointFormat*)extension->data);
length += PF_GET_SIZE((PointFormats*)extension->data);
break;
case TLSX_STATUS_REQUEST:
@@ -16038,7 +16100,7 @@ static int TLSX_Write(TLSX* list, byte* output, byte* semaphore,
case TLSX_EC_POINT_FORMATS:
WOLFSSL_MSG("Point Formats extension to write");
offset += PF_WRITE((PointFormat*)extension->data,
offset += PF_WRITE((PointFormats*)extension->data,
output + offset);
break;

View File

@@ -45741,6 +45741,7 @@ TEST_CASE testCases[] = {
TEST_DECL(test_TLSX_ALPN_server_response_count),
TEST_DECL(test_TLSX_SupportedCurve_empty_or_unsupported),
TEST_DECL(test_TLSX_SignatureAlgorithmsCert_parse),
TEST_DECL(test_TLSX_PointFormat_list),
TEST_DECL(test_TLSX_MaxFragment_value),
TEST_DECL(test_TLSX_PointFormat_uncompressed_required),
TEST_DECL(test_wolfSSL_CTX_add_client_custom_ext),

View File

@@ -3264,6 +3264,95 @@ int test_TLSX_SignatureAlgorithmsCert_parse(void)
return EXPECT_RESULT();
}
/* The formats live in a counted array that doubles when it fills. No in-tree
* caller reaches the growth path, so drive TLSX_UsePointFormat() directly. */
int test_TLSX_PointFormat_list(void)
{
EXPECT_DECLS;
#if defined(HAVE_SUPPORTED_CURVES) && defined(HAVE_TLS_EXTENSIONS) && \
!defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) && \
(defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
defined(HAVE_CURVE448))
WOLFSSL_CTX* ctx = NULL;
WOLFSSL* ssl = NULL;
TLSX** exts = NULL;
TLSX* extension = NULL;
PointFormats* formats = NULL;
void* heap = NULL;
int ret;
int i;
#ifndef WOLFSSL_NO_TLS12
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
#else
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
#endif
ExpectNotNull(ssl = wolfSSL_new(ctx));
if (ssl != NULL) {
exts = &ssl->extensions;
heap = ssl->heap;
ExpectNull(TLSX_Find(ssl->extensions, TLSX_EC_POINT_FORMATS));
}
if (exts == NULL) {
/* ssl is NULL too, so there is nothing else to release. */
wolfSSL_CTX_free(ctx);
return EXPECT_RESULT();
}
/* The three formats RFC 8422 defines, then a repeat of the first. */
ExpectIntEQ(TLSX_UsePointFormat(exts, WOLFSSL_EC_PF_UNCOMPRESSED, heap),
WOLFSSL_SUCCESS);
ExpectIntEQ(TLSX_UsePointFormat(exts, 1, heap), WOLFSSL_SUCCESS);
ExpectIntEQ(TLSX_UsePointFormat(exts, 2, heap), WOLFSSL_SUCCESS);
ExpectIntEQ(TLSX_UsePointFormat(exts, WOLFSSL_EC_PF_UNCOMPRESSED, heap),
WOLFSSL_SUCCESS);
ExpectNotNull(extension = TLSX_Find(*exts, TLSX_EC_POINT_FORMATS));
if (extension != NULL)
formats = (PointFormats*)extension->data;
ExpectNotNull(formats);
/* The repeat is dropped, and four is still enough. */
ExpectIntEQ(formats == NULL ? -1 : (int)formats->count, 3);
ExpectIntEQ(formats == NULL ? -1 : (int)formats->cap, 4);
if (formats != NULL) {
ExpectIntEQ(formats->format[0], WOLFSSL_EC_PF_UNCOMPRESSED);
ExpectIntEQ(formats->format[1], 1);
ExpectIntEQ(formats->format[2], 2);
}
/* Past the initial capacity the list grows and keeps what it held. */
for (i = 3; EXPECT_SUCCESS() && (i < 10); i++) {
ExpectIntEQ(TLSX_UsePointFormat(exts, (byte)i, heap),
WOLFSSL_SUCCESS);
}
if (extension != NULL)
formats = (PointFormats*)extension->data;
ExpectNotNull(formats);
ExpectIntEQ(formats == NULL ? -1 : (int)formats->count, 10);
ExpectIntGE(formats == NULL ? -1 : (int)formats->cap, 10);
if (formats != NULL) {
ExpectIntEQ(formats->format[0], WOLFSSL_EC_PF_UNCOMPRESSED);
ExpectIntEQ(formats->format[9], 9);
}
/* The bound is internal to src/tls.c, so discover it. 256 values exceed
* any bound a byte capacity can hold. */
ret = WOLFSSL_SUCCESS;
for (i = 10; (ret == WOLFSSL_SUCCESS) && (i < 256); i++)
ret = TLSX_UsePointFormat(exts, (byte)i, heap);
ExpectIntEQ(ret, WC_NO_ERR_TRACE(BAD_FUNC_ARG));
if (extension != NULL)
formats = (PointFormats*)extension->data;
ExpectNotNull(formats);
/* The refused insert leaves the list full. */
if (formats != NULL)
ExpectIntEQ((int)formats->count, (int)formats->cap);
wolfSSL_free(ssl);
wolfSSL_CTX_free(ctx);
#endif
return EXPECT_RESULT();
}
/* The requested code is held in the extension and a response must match. */
int test_TLSX_MaxFragment_value(void)
{

View File

@@ -68,6 +68,7 @@ int test_TLSX_QUIC_TP_non_quic(void);
int test_TLSX_ALPN_server_response_count(void);
int test_TLSX_SupportedCurve_empty_or_unsupported(void);
int test_TLSX_SignatureAlgorithmsCert_parse(void);
int test_TLSX_PointFormat_list(void);
int test_TLSX_MaxFragment_value(void);
int test_TLSX_PointFormat_uncompressed_required(void);
int test_wolfSSL_CTX_add_client_custom_ext(void);

View File

@@ -3889,10 +3889,16 @@ typedef struct SupportedCurve {
struct SupportedCurve* next; /* List Behavior */
} SupportedCurve;
typedef struct PointFormat {
byte format; /* PointFormat */
struct PointFormat* next; /* List Behavior */
} PointFormat;
typedef struct PointFormats {
byte count; /* Formats in use */
byte cap; /* Formats allocated */
/* Ignore "nonstandard extension used : zero-sized array in struct/union"
* MSVC warning */
#ifdef _MSC_VER
#pragma warning(disable: 4200)
#endif
byte format[WC_FLEXIBLE_ARRAY_SIZE]; /* Point formats */
} PointFormats;
WOLFSSL_LOCAL int TLSX_SupportedCurve_Copy(TLSX* src, TLSX** dst, void* heap);
WOLFSSL_LOCAL int TLSX_UseSupportedCurve(TLSX** extensions, word16 name,