From c8f9880efbc2d7c8e2b37afd04376bee498a6be9 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sat, 27 Aug 2022 17:19:30 +0900 Subject: [PATCH] Fix buffer overrun in LE/QE command --- src/cmd2F.c | 46 +++++++++++++--------- src/cmdd.c | 2 +- src/cmdl.c | 25 ++++++------ src/cmdq.c | 24 ++++++------ src/savedata.c | 104 +++++++++++++++++++++---------------------------- src/savedata.h | 4 +- src/variable.h | 3 ++ 7 files changed, 104 insertions(+), 104 deletions(-) diff --git a/src/cmd2F.c b/src/cmd2F.c index 4f81920..a44e065 100644 --- a/src/cmd2F.c +++ b/src/cmd2F.c @@ -380,25 +380,31 @@ void commands2F23() { void commands2F24() { int type = sl_getc(); const char *file_name = sl_getString(0); - int *var = NULL, _var = 0; - int num = 0; + int var, cnt; + struct VarRef vref; char *fname_utf8 = toUTF8(file_name); - switch(type) { + switch (type) { case 0: - var = getCaliVariable(); - num = getCaliValue(); - sysVar[0] = save_load_var_with_file(fname_utf8, var, num); + getCaliArray(&vref); + var = vref.var; + cnt = getCaliValue(); + sysVar[0] = load_vars_from_file(fname_utf8, &vref, cnt); break; case 1: - _var = getCaliValue(); - num = getCaliValue(); - sysVar[0] = save_load_str_with_file(fname_utf8, _var, num); + var = getCaliValue(); + cnt = getCaliValue(); + sysVar[0] = save_load_str_with_file(fname_utf8, var, cnt); + break; + default: + var = getCaliValue(); + cnt = getCaliValue(); + WARNING("Unknown LE command type %d\n", type); break; } free(fname_utf8); - DEBUG_COMMAND("LE(new) %d, %s, %d, %d:\n", type, file_name, var, num); + DEBUG_COMMAND("LE(new) %d, %s, %d, %d:\n", type, file_name, var, cnt); } void commands2F25() { @@ -483,29 +489,31 @@ void commands2F29() { void commands2F2A() { int type = sl_getc(); const char *file_name = sl_getString(0); - int *var, _var = 0, cnt; + int var, cnt; + struct VarRef vref; char *fname_utf8 = toUTF8(file_name); switch(type) { case 0: - var = getCaliVariable(); + getCaliArray(&vref); + var = vref.var; cnt = getCaliValue(); - sysVar[0] = save_save_var_with_file(fname_utf8, var, cnt); + sysVar[0] = save_vars_to_file(fname_utf8, &vref, cnt); break; case 1: - _var = getCaliValue(); - cnt = getCaliValue(); - sysVar[0] = save_save_str_with_file(fname_utf8, _var, cnt); + var = getCaliValue(); + cnt = getCaliValue(); + sysVar[0] = save_save_str_with_file(fname_utf8, var, cnt); break; default: - _var = getCaliValue(); - cnt = getCaliValue(); + var = getCaliValue(); + cnt = getCaliValue(); WARNING("Unknown QE command\n"); break; } free(fname_utf8); - DEBUG_COMMAND("QE(new) %d, %s, %d, %d:\n", type, file_name, _var, cnt); + DEBUG_COMMAND("QE(new) %d, %s, %d, %d:\n", type, file_name, var, cnt); } void commands2F2B() { diff --git a/src/cmdd.c b/src/cmdd.c index 7c2bac0..37e0405 100644 --- a/src/cmdd.c +++ b/src/cmdd.c @@ -79,7 +79,7 @@ void commandDF() { DEBUG_COMMAND("DF %p,%d,%d:\n", data_var, cnt, data); if (data_var.page) { - int maxlen = varPage[data_var.page].size - data_var.index; + int maxlen = v_sliceSize(&data_var); if (cnt > maxlen) { WARNING("%03d:%05x: count exceeds array boundary (%d > %d)\n", sl_getPage(), sl_getIndex(), cnt, maxlen); cnt = maxlen; diff --git a/src/cmdl.c b/src/cmdl.c index df3b0c9..96c9078 100644 --- a/src/cmdl.c +++ b/src/cmdl.c @@ -109,30 +109,31 @@ void commandLT() { void commandLE() { int type = sl_getc(); const char *filename = sl_getString(':'); - int *var, _var = 0; - int num; + int var, cnt; + struct VarRef vref; char *fname_utf8 = toUTF8(filename); - switch(type) { + switch (type) { case 0: /* T2 */ - var = getCaliVariable(); - num = getCaliValue(); - sysVar[0] = save_load_var_with_file(fname_utf8, var, num); + getCaliArray(&vref); + var = vref.var; + cnt = getCaliValue(); + sysVar[0] = load_vars_from_file(fname_utf8, &vref, cnt); break; case 1: /* 456 */ - _var = getCaliValue(); - num = getCaliValue(); - sysVar[0] = save_load_str_with_file(fname_utf8, _var, num); + var = getCaliValue(); + cnt = getCaliValue(); + sysVar[0] = save_load_str_with_file(fname_utf8, var, cnt); break; default: - _var = getCaliValue(); - num = getCaliValue(); + var = getCaliValue(); + cnt = getCaliValue(); WARNING("Unknown LE command %d\n", type); break; } free(fname_utf8); - DEBUG_COMMAND("LE %d,%s,%d,%d:\n",type, filename, _var, num); + DEBUG_COMMAND("LE %d,%s,%d,%d:\n",type, filename, var, cnt); } void commandLL() { diff --git a/src/cmdq.c b/src/cmdq.c index e87c18a..8b30a75 100644 --- a/src/cmdq.c +++ b/src/cmdq.c @@ -83,30 +83,32 @@ void commandQC() { } void commandQE() { - int type = sl_getc(); + int type = sl_getc(); const char *filename = sl_getString(':'); - int *var, _var = 0, cnt; + int var, cnt; + struct VarRef vref; char *fname_utf8 = toUTF8(filename); - switch(type) { + switch (type) { case 0: - var = getCaliVariable(); + getCaliArray(&vref); + var = vref.var; cnt = getCaliValue(); - sysVar[0] = save_save_var_with_file(fname_utf8, var, cnt); + sysVar[0] = save_vars_to_file(fname_utf8, &vref, cnt); break; case 1: - _var = getCaliValue(); - cnt = getCaliValue(); - sysVar[0] = save_save_str_with_file(fname_utf8, _var, cnt); + var = getCaliValue(); + cnt = getCaliValue(); + sysVar[0] = save_save_str_with_file(fname_utf8, var, cnt); break; default: - _var = getCaliValue(); - cnt = getCaliValue(); + var = getCaliValue(); + cnt = getCaliValue(); WARNING("Unknown QE command %d\n", type); break; } free(fname_utf8); WARN_SAVEERR("QE", sysVar[0]); - DEBUG_COMMAND("QE %d,%s,%d,%d:\n", type, filename, _var, cnt); + DEBUG_COMMAND("QE %d,%s,%d,%d:\n", type, filename, var, cnt); } diff --git a/src/savedata.c b/src/savedata.c index df21472..e12c3cc 100644 --- a/src/savedata.c +++ b/src/savedata.c @@ -76,76 +76,62 @@ int save_delete_file(int index) { return 1; /* とりあえず */ } -/* 指定ファイルへの変数の書き込み */ -int save_save_var_with_file(char *fname_utf8, int *start, int cnt) { - int status = 0, size, i; - FILE *fp; - WORD *tmp; - - tmp = (WORD *)malloc(cnt * sizeof(WORD)); - - if (tmp == NULL) { - WARNING("Out of memory\n"); +// QE command +int save_vars_to_file(char *fname_utf8, struct VarRef *src, int cnt) { + // FIXME: System39.exe does not truncate existing file. + FILE *fp = fc_open(fname_utf8, 'w'); + if (!fp) return SAVE_SAVEERR; + + if (cnt > v_sliceSize(src)) { + WARNING("QE: array size too small (size = %d, data count = %d)\n", v_sliceSize(src), cnt); + cnt = v_sliceSize(src); } - - for (i = 0; i < cnt; i++) { - tmp[i] = SDL_SwapLE16((WORD)start[i]); + + int *p = v_resolveRef(src); + while (cnt--) { + fputc(*p & 0xff, fp); + fputc(*p >> 8, fp); + p++; } - - if (NULL == (fp = fc_open(fname_utf8, 'w'))) { - status = SAVE_SAVEERR; goto errexit; - } - - size = fwrite(tmp, sizeof(WORD), cnt, fp); - - if (size != cnt) { - status = SAVE_OTHERERR; - } else { - status = SAVE_SAVEOK0; - } - fclose(fp); scheduleSync(); - errexit: - free(tmp); - - return status; + return SAVE_SAVEOK0; } -/* 指定ファイルからの変数の読み込み */ -int save_load_var_with_file(char *fname_utf8, int *start, int cnt) { - int status = 0, size, i; - FILE *fp; - WORD *tmp; - - tmp = (WORD *)malloc(cnt * sizeof(WORD)); - - if (tmp == NULL) { +// LE command +int load_vars_from_file(char *fname_utf8, struct VarRef *dest, int cnt) { + FILE *fp = fc_open(fname_utf8, 'r'); + if (!fp) + return SAVE_LOADERR; + + WORD *tmp = malloc(cnt * sizeof(WORD)); + if (!tmp) { WARNING("Out of memory\n"); + fclose(fp); return SAVE_LOADERR; } - - if (NULL == (fp = fc_open(fname_utf8, 'r'))) { - status = SAVE_LOADERR; goto errexit; - } - - size = fread(tmp, sizeof(WORD), cnt, fp); + + size_t size = fread(tmp, sizeof(WORD), cnt, fp); + fclose(fp); if (size != cnt) { - status = SAVE_LOADSHORTAGE; - } else { - status = SAVE_LOADOK; - } - - for (i = 0; i < cnt; i++) { - start[i] = SDL_SwapLE16(tmp[i]); + WARNING("LE: data file too small (requested = %d, loaded = %d)", cnt, size); + cnt = size; + // NOTE: System39.exe never returns SAVE_LOADSHORTAGE (254). } - fclose(fp); - errexit: + if (cnt > v_sliceSize(dest)) { + WARNING("LE: array size too small (size = %d, data count = %d)\n", v_sliceSize(dest), cnt); + cnt = v_sliceSize(dest); + } + + int *start = v_resolveRef(dest); + for (int i = 0; i < cnt; i++) + start[i] = SDL_SwapLE16(tmp[i]); + free(tmp); - return status; + return SAVE_LOADOK; } @@ -265,8 +251,8 @@ int save_loadPartial(int no, struct VarRef *vref, int cnt) { if (no >= SAVE_MAXNUMBER) { return SAVE_SAVEERR; } - cnt = min(cnt, varPage[vref->page].size - vref->index); - var = varPage[vref->page].value + vref->index; + cnt = min(cnt, v_sliceSize(vref)); + var = v_resolveRef(vref); saveTop = loadGameData(no, &status, &filesize); if (saveTop == NULL) { @@ -318,8 +304,8 @@ int save_savePartial(int no, struct VarRef *vref, int cnt) { } if (!varPage[vref->page].saveflag) goto errexit; - cnt = min(cnt, varPage[vref->page].size - vref->index); - var = varPage[vref->page].value + vref->index; + cnt = min(cnt, v_sliceSize(vref)); + var = v_resolveRef(vref); saveTop = loadGameData(no, &status, &filesize); if (saveTop == NULL) diff --git a/src/savedata.h b/src/savedata.h index 57468a6..cfe24e1 100644 --- a/src/savedata.h +++ b/src/savedata.h @@ -117,8 +117,8 @@ extern int save_saveAll(int no); extern int save_loadPartial(int no, struct VarRef *vref, int cnt); extern int save_savePartial(int no, struct VarRef *vref, int cnt); extern int save_copyAll(int dstno, int srcno); -extern int save_save_var_with_file(char *fname_utf8, int *start, int cnt); -extern int save_load_var_with_file(char *fname_utf8, int *start, int cnt); +extern int save_vars_to_file(char *fname_utf8, struct VarRef *src, int cnt); +extern int load_vars_from_file(char *fname_utf8, struct VarRef *dest, int cnt); extern int save_save_str_with_file(char *fname_utf8, int start, int cnt); extern int save_load_str_with_file(char *fname_utf8, int start, int cnt); extern BYTE* load_cg_with_file(char *fname_utf8, int *status, long *filesize); diff --git a/src/variable.h b/src/variable.h index a3f77dd..2d43154 100644 --- a/src/variable.h +++ b/src/variable.h @@ -50,6 +50,9 @@ extern double longVar[]; static inline int *v_resolveRef(struct VarRef *r) { return varPage[r->page].value + r->index; } +static inline int v_sliceSize(struct VarRef *r) { + return varPage[r->page].size - r->index; +} const char *v_name(int var); int *v_ref_indexed(int var, int index, struct VarRef *ref);