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,10 +186,12 @@ 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 *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 *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.
/**
@@ -240,29 +242,15 @@ static const char *FindItemValue(const char *name, IniGroup *grp, IniGroup *defa
}
/**
* Output all non-special sections through the template / template variable expansion system.
* @param ifile Loaded INI data.
* 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 DumpSections(IniLoadFile *ifile)
static void DumpLine(IniItem *item, IniGroup *grp, IniGroup *default_grp, OutputStore &output)
{
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};
IniGroup *default_grp = ifile->GetGroup(DEFAULTS_GROUP_NAME, false);
IniGroup *templates_grp = ifile->GetGroup(TEMPLATES_GROUP_NAME, false);
if (templates_grp == nullptr) return;
/* Output every group, using its name as template name. */
for (IniGroup *grp = ifile->group; grp != nullptr; grp = grp->next) {
const char * const *sgn;
for (sgn = special_group_names; *sgn != nullptr; sgn++) if (grp->name == *sgn) break;
if (*sgn != nullptr) continue;
IniItem *template_item = templates_grp->GetItem(grp->name, false); // Find template value.
if (template_item == nullptr || !template_item->value.has_value()) {
fprintf(stderr, "settingsgen: Warning: Cannot find template %s\n", grp->name.c_str());
continue;
}
/* Prefix with #if/#ifdef/#ifndef */
static const char * const pp_lines[] = {"if", "ifdef", "ifndef", nullptr};
@@ -270,26 +258,26 @@ static void DumpSections(IniLoadFile *ifile)
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);
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 = template_item->value->c_str();
const char *txt = item->value->c_str();
while (*txt != '\0') {
if (*txt != '$') {
_stored_output.Add(txt, 1);
output.Add(txt, 1);
txt++;
continue;
}
txt++;
if (*txt == '$') { // Literal $
_stored_output.Add(txt, 1);
output.Add(txt, 1);
txt++;
continue;
}
@@ -308,16 +296,50 @@ static void DumpSections(IniLoadFile *ifile)
if (i > 0) {
/* Find the text to output. */
const char *valitem = FindItemValue(variable, grp, default_grp);
if (valitem != nullptr) _stored_output.Add(valitem);
if (valitem != nullptr) output.Add(valitem);
} else {
_stored_output.Add("$", 1);
output.Add("$", 1);
}
}
_stored_output.Add("\n", 1); // \n after the expanded template.
output.Add("\n", 1); // \n after the expanded template.
while (count > 0) {
_stored_output.Add("#endif\n");
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 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. */
for (IniGroup *grp = ifile->group; grp != nullptr; grp = grp->next) {
const char * const *sgn;
for (sgn = special_group_names; *sgn != nullptr; sgn++) if (grp->name == *sgn) break;
if (*sgn != nullptr) continue;
IniItem *template_item = templates_grp->GetItem(grp->name, false); // Find template value.
if (template_item == nullptr || !template_item->value.has_value()) {
fprintf(stderr, "settingsgen: Warning: Cannot find template %s\n", grp->name.c_str());
continue;
}
DumpLine(template_item, grp, default_grp, _stored_output);
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);
}
}
}
}
@@ -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