Take libakgl 0.7.0, and refuse an over-long type or field name
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 3m6s
akbasic CI Build / sanitizers (push) Failing after 3m59s
akbasic CI Build / coverage (push) Failing after 3m25s
akbasic CI Build / akgl_build (push) Failing after 20s
akbasic CI Build / mutation_test (push) Failing after 3m15s
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 3m6s
akbasic CI Build / sanitizers (push) Failing after 3m59s
akbasic CI Build / coverage (push) Failing after 3m25s
akbasic CI Build / akgl_build (push) Failing after 20s
akbasic CI Build / mutation_test (push) Failing after 3m15s
0.6.0 and 0.7.0 break nothing here: 0.6.0 is three arcade-physics fixes and a physics.max_timestep property this port does not use, and 0.7.0 reports failures libakstdlib's wrappers were already catching and takes libakerror 2.0.1 so it can drop the exit-status trap its own suites needed. That is the same defect include/akbasic/error.h guards for this band, now fixed at the source rather than worked around in two places. The floor moves to 0.7.0 anyway. The soname carries MAJOR.MINOR while the major is 0, so deciding for ourselves which of libakgl's minor releases were really compatible is exactly the judgement it exists to take away. 0.7.0 is largely about a defect class worth checking for here rather than assuming past: ten unterminated strncpy calls into fixed-width name fields. Ours are clean -- every one is bounded to size - 1 and every buffer is either explicitly terminated afterwards or memset first, checked site by site. But two of them, both written last week in src/structtype.c, truncated an over-long name silently, and truncation is an error everywhere else in this interpreter. It matters more for a name than for a value: two long type names trimmed to the same 31 characters collide, and the second is then reported as redeclaring a type the program never wrote. Both are refused now, showing the name as written. The check was unreachable as first drafted, because the prescan read words into a buffer the size of the limit and so trimmed them before anything could look -- the scratch is twice the limit now, which is what makes the two cases distinguishable at all. tests/struct_types.c pins that it stays reachable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -58,6 +58,18 @@ static const char *skip_lineno(const char *cursor)
|
||||
return skip_space(cursor);
|
||||
}
|
||||
|
||||
/*
|
||||
* Room to read a word that is *too long* and still see that it was.
|
||||
*
|
||||
* Reading into a buffer the size of the limit truncates before anything can
|
||||
* check, so an over-long name would arrive already trimmed to fit and be
|
||||
* accepted -- which is how the length check below was unreachable when it was
|
||||
* first written. Twice the limit is enough to tell the two cases apart, and a
|
||||
* word longer than this is still longer than the limit, so it is refused either
|
||||
* way.
|
||||
*/
|
||||
#define STRUCT_WORD_SCRATCH (AKBASIC_MAX_STRUCT_NAME * 2)
|
||||
|
||||
/** @brief Copy the next whitespace-delimited word, uppercased. Empty when the line ends. */
|
||||
static const char *next_word(const char *cursor, char *dest, size_t len)
|
||||
{
|
||||
@@ -205,8 +217,8 @@ static akerr_ErrorContext *scan_names(akbasic_Runtime *obj)
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
akbasic_StructTypeTable *table = &obj->structtypes;
|
||||
char word[AKBASIC_MAX_STRUCT_NAME];
|
||||
char name[AKBASIC_MAX_STRUCT_NAME];
|
||||
char word[STRUCT_WORD_SCRATCH];
|
||||
char name[STRUCT_WORD_SCRATCH];
|
||||
const char *cursor = NULL;
|
||||
int64_t i = 0;
|
||||
int open = -1;
|
||||
@@ -256,8 +268,15 @@ static akerr_ErrorContext *scan_names(akbasic_Runtime *obj)
|
||||
FAIL_ZERO_RETURN(errctx, (table->count < AKBASIC_MAX_STRUCT_TYPES), AKBASIC_ERR_BOUNDS,
|
||||
"More than %d TYPE declarations", AKBASIC_MAX_STRUCT_TYPES);
|
||||
|
||||
FAIL_ZERO_RETURN(errctx, (strlen(name) < AKBASIC_MAX_STRUCT_NAME), AKBASIC_ERR_BOUNDS,
|
||||
"TYPE name \"%s\" exceeds the %d character limit",
|
||||
name, AKBASIC_MAX_STRUCT_NAME - 1);
|
||||
open = table->count;
|
||||
memset(&table->types[open], 0, sizeof(table->types[open]));
|
||||
/* The memset is what terminates this: bounded at size - 1, the last byte
|
||||
is the zero it already wrote. Refused above rather than truncated,
|
||||
because two long names truncating to the same prefix would collide
|
||||
silently -- and truncation is an error everywhere else here. */
|
||||
strncpy(table->types[open].name, name, sizeof(table->types[open].name) - 1);
|
||||
table->types[open].used = true;
|
||||
table->types[open].firstline = i;
|
||||
@@ -278,8 +297,8 @@ static akerr_ErrorContext *parse_field(akbasic_StructTypeTable *table, akbasic_S
|
||||
const char *line, int64_t lineno)
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
char fieldname[AKBASIC_MAX_STRUCT_NAME];
|
||||
char word[AKBASIC_MAX_STRUCT_NAME];
|
||||
char fieldname[STRUCT_WORD_SCRATCH];
|
||||
char word[STRUCT_WORD_SCRATCH];
|
||||
const char *cursor = NULL;
|
||||
akbasic_StructField *field = NULL;
|
||||
akbasic_Type valuetype = AKBASIC_TYPE_UNDEFINED;
|
||||
@@ -303,8 +322,13 @@ static akerr_ErrorContext *parse_field(akbasic_StructTypeTable *table, akbasic_S
|
||||
}
|
||||
|
||||
PASS(errctx, type_from_suffix(fieldname, &valuetype));
|
||||
FAIL_ZERO_RETURN(errctx, (strlen(fieldname) < AKBASIC_MAX_STRUCT_NAME), AKBASIC_ERR_BOUNDS,
|
||||
"Field name \"%s\" exceeds the %d character limit",
|
||||
fieldname, AKBASIC_MAX_STRUCT_NAME - 1);
|
||||
field = &type->fields[type->fieldcount];
|
||||
memset(field, 0, sizeof(*field));
|
||||
/* Terminated by the memset above; refused rather than truncated, for the
|
||||
same reason a type name is. */
|
||||
strncpy(field->name, fieldname, sizeof(field->name) - 1);
|
||||
field->valuetype = valuetype;
|
||||
field->typeindex = -1;
|
||||
|
||||
Reference in New Issue
Block a user