Codechange: validate that "max" value of settings fit in their storage

This is an easy mistake to make, so protect us against making such
mistakes, by validating it doesn't happen.
This commit is contained in:
Patric Stout 2021-03-01 13:12:17 +01:00 committed by Patric Stout
parent d7a70c67ba
commit 74aa934441
9 changed files with 122 additions and 57 deletions

View File

@ -21,6 +21,15 @@
#include "zoom_type.h"
#include "openttd.h"
/* Used to validate sizes of "max" value in settings. */
const size_t MAX_SLE_UINT8 = UINT8_MAX;
const size_t MAX_SLE_UINT16 = UINT16_MAX;
const size_t MAX_SLE_UINT32 = UINT32_MAX;
const size_t MAX_SLE_UINT = UINT_MAX;
const size_t MAX_SLE_INT8 = INT8_MAX;
const size_t MAX_SLE_INT16 = INT16_MAX;
const size_t MAX_SLE_INT32 = INT32_MAX;
const size_t MAX_SLE_INT = INT_MAX;
/** Settings profiles and highscore tables. */
enum SettingsProfile {

View File

@ -186,11 +186,13 @@ struct SettingsIniFile : IniLoadFile {
};
OutputStore _stored_output; ///< Temporary storage of the output, until all processing is done.
OutputStore _post_amble_output; ///< Similar to _stored_output, but for the post amble.
static const char *PREAMBLE_GROUP_NAME = "pre-amble"; ///< Name of the group containing the pre amble.
static const char *PREAMBLE_GROUP_NAME = "pre-amble"; ///< Name of the group containing the pre amble.
static const char *POSTAMBLE_GROUP_NAME = "post-amble"; ///< Name of the group containing the post amble.
static const char *TEMPLATES_GROUP_NAME = "templates"; ///< Name of the group containing the templates.
static const char *DEFAULTS_GROUP_NAME = "defaults"; ///< Name of the group containing default values for the template variables.
static const char *TEMPLATES_GROUP_NAME = "templates"; ///< Name of the group containing the templates.
static const char *VALIDATION_GROUP_NAME = "validation"; ///< Name of the group containing the validation statements.
static const char *DEFAULTS_GROUP_NAME = "defaults"; ///< Name of the group containing default values for the template variables.
/**
* Load the INI file.
@ -239,17 +241,84 @@ static const char *FindItemValue(const char *name, IniGroup *grp, IniGroup *defa
return item->value->c_str();
}
/**
* Parse a single entry via a template and output this.
* @param item The template to use for the output.
* @param grp Group current being used for template rendering.
* @param default_grp Default values for items not set in @grp.
* @param output Output to use for result.
*/
static void DumpLine(IniItem *item, IniGroup *grp, IniGroup *default_grp, OutputStore &output)
{
static const int MAX_VAR_LENGTH = 64;
/* Prefix with #if/#ifdef/#ifndef */
static const char * const pp_lines[] = {"if", "ifdef", "ifndef", nullptr};
int count = 0;
for (const char * const *name = pp_lines; *name != nullptr; name++) {
const char *condition = FindItemValue(*name, grp, default_grp);
if (condition != nullptr) {
output.Add("#", 1);
output.Add(*name);
output.Add(" ", 1);
output.Add(condition);
output.Add("\n", 1);
count++;
}
}
/* Output text of the template, except template variables of the form '$[_a-z0-9]+' which get replaced by their value. */
const char *txt = item->value->c_str();
while (*txt != '\0') {
if (*txt != '$') {
output.Add(txt, 1);
txt++;
continue;
}
txt++;
if (*txt == '$') { // Literal $
output.Add(txt, 1);
txt++;
continue;
}
/* Read variable. */
char variable[MAX_VAR_LENGTH];
int i = 0;
while (i < MAX_VAR_LENGTH - 1) {
if (!(txt[i] == '_' || (txt[i] >= 'a' && txt[i] <= 'z') || (txt[i] >= '0' && txt[i] <= '9'))) break;
variable[i] = txt[i];
i++;
}
variable[i] = '\0';
txt += i;
if (i > 0) {
/* Find the text to output. */
const char *valitem = FindItemValue(variable, grp, default_grp);
if (valitem != nullptr) output.Add(valitem);
} else {
output.Add("$", 1);
}
}
output.Add("\n", 1); // \n after the expanded template.
while (count > 0) {
output.Add("#endif\n");
count--;
}
}
/**
* Output all non-special sections through the template / template variable expansion system.
* @param ifile Loaded INI data.
*/
static void DumpSections(IniLoadFile *ifile)
{
static const int MAX_VAR_LENGTH = 64;
static const char * const special_group_names[] = {PREAMBLE_GROUP_NAME, POSTAMBLE_GROUP_NAME, DEFAULTS_GROUP_NAME, TEMPLATES_GROUP_NAME, nullptr};
static const char * const special_group_names[] = {PREAMBLE_GROUP_NAME, POSTAMBLE_GROUP_NAME, DEFAULTS_GROUP_NAME, TEMPLATES_GROUP_NAME, VALIDATION_GROUP_NAME, nullptr};
IniGroup *default_grp = ifile->GetGroup(DEFAULTS_GROUP_NAME, false);
IniGroup *templates_grp = ifile->GetGroup(TEMPLATES_GROUP_NAME, false);
IniGroup *validation_grp = ifile->GetGroup(VALIDATION_GROUP_NAME, false);
if (templates_grp == nullptr) return;
/* Output every group, using its name as template name. */
@ -263,61 +332,14 @@ static void DumpSections(IniLoadFile *ifile)
fprintf(stderr, "settingsgen: Warning: Cannot find template %s\n", grp->name.c_str());
continue;
}
DumpLine(template_item, grp, default_grp, _stored_output);
/* Prefix with #if/#ifdef/#ifndef */
static const char * const pp_lines[] = {"if", "ifdef", "ifndef", nullptr};
int count = 0;
for (const char * const *name = pp_lines; *name != nullptr; name++) {
const char *condition = FindItemValue(*name, grp, default_grp);
if (condition != nullptr) {
_stored_output.Add("#", 1);
_stored_output.Add(*name);
_stored_output.Add(" ", 1);
_stored_output.Add(condition);
_stored_output.Add("\n", 1);
count++;
if (validation_grp != nullptr) {
IniItem *validation_item = validation_grp->GetItem(grp->name, false); // Find template value.
if (validation_item != nullptr && validation_item->value.has_value()) {
DumpLine(validation_item, grp, default_grp, _post_amble_output);
}
}
/* Output text of the template, except template variables of the form '$[_a-z0-9]+' which get replaced by their value. */
const char *txt = template_item->value->c_str();
while (*txt != '\0') {
if (*txt != '$') {
_stored_output.Add(txt, 1);
txt++;
continue;
}
txt++;
if (*txt == '$') { // Literal $
_stored_output.Add(txt, 1);
txt++;
continue;
}
/* Read variable. */
char variable[MAX_VAR_LENGTH];
int i = 0;
while (i < MAX_VAR_LENGTH - 1) {
if (!(txt[i] == '_' || (txt[i] >= 'a' && txt[i] <= 'z') || (txt[i] >= '0' && txt[i] <= '9'))) break;
variable[i] = txt[i];
i++;
}
variable[i] = '\0';
txt += i;
if (i > 0) {
/* Find the text to output. */
const char *valitem = FindItemValue(variable, grp, default_grp);
if (valitem != nullptr) _stored_output.Add(valitem);
} else {
_stored_output.Add("$", 1);
}
}
_stored_output.Add("\n", 1); // \n after the expanded template.
while (count > 0) {
_stored_output.Add("#endif\n");
count--;
}
}
}
@ -476,6 +498,7 @@ int CDECL main(int argc, char *argv[])
}
_stored_output.Clear();
_post_amble_output.Clear();
for (int i = 0; i < mgo.numleft; i++) ProcessIniFile(mgo.argv[i]);
@ -483,6 +506,7 @@ int CDECL main(int argc, char *argv[])
if (output_file == nullptr) {
CopyFile(before_file, stdout);
_stored_output.Write(stdout);
_post_amble_output.Write(stdout);
CopyFile(after_file, stdout);
} else {
static const char * const tmp_output = "tmp2.xxx";
@ -494,6 +518,7 @@ int CDECL main(int argc, char *argv[])
}
CopyFile(before_file, fp);
_stored_output.Write(fp);
_post_amble_output.Write(fp);
CopyFile(after_file, fp);
fclose(fp);

View File

@ -20,6 +20,9 @@ SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def,
SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extra, $startup),
SDT_END = SDT_END()
[validation]
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for $base.$var exceeds storage size");
[defaults]
flags = 0
guiflags = SGF_PER_COMPANY

View File

@ -14,6 +14,9 @@ SDT_CHR = SDT_CHR($base, $var, $flags, $guiflags, $def,
SDT_STR = SDT_STR($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extra, $startup),
SDT_END = SDT_END()
[validation]
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for $base.$var exceeds storage size");
[defaults]
flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC
guiflags = SGF_NONE

View File

@ -46,6 +46,13 @@ SDT_OMANY = SDT_OMANY($base, $var, $type, $flags, $guiflags, $def, $ma
SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extra, $startup),
SDT_END = SDT_END()
[validation]
SDTG_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTG_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTC_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDT_OMANY = static_assert($max <= MAX_$type, "Maximum value for $base.$var exceeds storage size");
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for $base.$var exceeds storage size");
[defaults]
flags = 0
guiflags = SGF_NONE

View File

@ -26,6 +26,10 @@ SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def,
SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extra, $startup),
SDTG_END = SDTG_END()
[validation]
SDTG_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTG_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
[defaults]
flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC
guiflags = SGF_NONE

View File

@ -77,6 +77,14 @@ SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min,
SDT_NULL = SDT_NULL($length, $from, $to),
SDT_END = SDT_END()
[validation]
SDTG_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTG_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTC_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDTC_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
SDT_OMANY = static_assert($max <= MAX_$type, "Maximum value for $base.$var exceeds storage size");
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for $base.$var exceeds storage size");
[defaults]
flags = 0
guiflags = SGF_NONE

View File

@ -18,6 +18,9 @@ SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def,
SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extra, $startup),
SDTG_END = SDTG_END()
[validation]
SDTG_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
[defaults]
flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC
guiflags = SGF_NONE

View File

@ -14,6 +14,9 @@ SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def,
SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extra, $startup),
SDT_END = SDT_END()
[validation]
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for $base.$var exceeds storage size");
[defaults]
base = WindowDesc
flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC