Guess: validate peg colours in decode_ui()

Peg colours in the current guess must be within the range of colours
for the current game, just as they must be for completed moves.
Otherwise is_markable() can cause a buffer overrun.

Since there's no way for decode_ui() to report an error, it just ignores
the bad peg colours.  I've also added an assertion to catch this problem
in is_markable().

The following save file demonstrates the problem when loaded in a build
with AddressSanitizer:

SAVEFILE:41:Simon Tatham's Portable Puzzle Collection
VERSION :1:1
GAME    :5:Guess
PARAMS  :9:c6p4g10Bm
CPARAMS :9:c6p4g10Bm
DESC    :8:2de917c0
UI      :7:7,7,7,7
NSTATES :1:1
STATEPOS:1:1
This commit is contained in:
Ben Harris
2023-01-07 20:28:23 +00:00
parent c84af670b5
commit 5279fd24b2

View File

@ -380,6 +380,7 @@ static bool is_markable(const game_params *params, pegrow pegs)
for (i = 0; i < params->npegs; i++) {
int c = pegs->pegs[i];
if (c > 0) {
assert(c <= params->ncolours);
colcount->pegs[c-1]++;
nset++;
}
@ -462,6 +463,9 @@ static void decode_ui(game_ui *ui, const char *encoding)
const char *p = encoding;
for (i = 0; i < ui->curr_pegs->npegs; i++) {
ui->curr_pegs->pegs[i] = atoi(p);
if (ui->curr_pegs->pegs[i] < 0 ||
ui->curr_pegs->pegs[i] > ui->params.ncolours)
ui->curr_pegs->pegs[i] = 0; /* Remove invalid pegs. */
while (*p && isdigit((unsigned char)*p)) p++;
if (*p == '_') {
/* NB: old versions didn't store holds */