1
0
Fork 0

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.
pull/8795/head
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