Tracerestrict: Improve program validator

Check all instruction subtypes and valid enum values
Return failure instead of asserting for invalid instruction subtypes
Identify which instruction triggered validation failure
pull/495/head
Jonathan G Rennison 1 year ago
parent 5b788dcb97
commit f13f8cd740

@ -77,12 +77,12 @@ static void Load_TRRP()
char str[4096];
char *strend = str + seprintf(str, lastof(str), "Trace restrict program %d: %s\nProgram dump:",
index, GetStringPtr(validation_result.GetErrorMessage()));
for (unsigned int i = 0; i < prog->items.size(); i++) {
if (i % 3) {
strend += seprintf(strend, lastof(str), " %08X", prog->items[i]);
} else {
strend += seprintf(strend, lastof(str), "\n%4u: %08X", i, prog->items[i]);
uint fail_offset = validation_result.GetResultData() ^ (1 << 31);
for (uint i = 0; i < (uint)prog->items.size(); i++) {
if ((i % 3) == 0) {
strend += seprintf(strend, lastof(str), "\n%4u:", i);
}
strend += seprintf(strend, lastof(str), (i == fail_offset) ? " [%08X]" : " %08X", prog->items[i]);
}
SlErrorCorrupt(str);
}

@ -450,7 +450,6 @@ void TraceRestrictProgram::Execute(const Train* v, const TraceRestrictProgramInp
break;
}
case TRIT_COND_TRAIN_STATUS: {
bool has_status = false;
switch (static_cast<TraceRestrictTrainStatusValueField>(GetTraceRestrictValue(item))) {
@ -956,11 +955,21 @@ CommandCost TraceRestrictProgram::Validate(const std::vector<TraceRestrictItem>
TraceRestrictItem item = items[i];
TraceRestrictItemType type = GetTraceRestrictType(item);
auto validation_error = [i](StringID str) -> CommandCost {
CommandCost result(str);
result.SetResultData((1 << 31) | (uint)i);
return result;
};
auto unknown_instruction = [&]() {
return validation_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_UNKNOWN_INSTRUCTION);
};
// check multi-word instructions
if (IsTraceRestrictDoubleItem(item)) {
i++;
if (i >= size) {
return_cmd_error(STR_TRACE_RESTRICT_ERROR_OFFSET_TOO_LARGE); // instruction ran off end
return validation_error(STR_TRACE_RESTRICT_ERROR_OFFSET_TOO_LARGE); // instruction ran off end
}
}
@ -969,12 +978,12 @@ CommandCost TraceRestrictProgram::Validate(const std::vector<TraceRestrictItem>
if (type == TRIT_COND_ENDIF) {
if (condstack.empty()) {
return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_NO_IF); // else/endif with no starting if
return validation_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_NO_IF); // else/endif with no starting if
}
if (condflags & TRCF_ELSE) {
// else
if (condstack.back() & TRCSF_SEEN_ELSE) {
return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_DUP_ELSE); // Two else clauses
return validation_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_DUP_ELSE); // Two else clauses
}
HandleCondition(condstack, condflags, true);
condstack.back() |= TRCSF_SEEN_ELSE;
@ -985,15 +994,221 @@ CommandCost TraceRestrictProgram::Validate(const std::vector<TraceRestrictItem>
} else {
if (condflags & (TRCF_OR | TRCF_ELSE)) { // elif/orif
if (condstack.empty()) {
return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_ELIF_NO_IF); // Pre-empt assertions in HandleCondition
return validation_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_ELIF_NO_IF); // Pre-empt assertions in HandleCondition
}
if (condstack.back() & TRCSF_SEEN_ELSE) {
return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_DUP_ELSE); // else clause followed by elif/orif
return validation_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_DUP_ELSE); // else clause followed by elif/orif
}
}
HandleCondition(condstack, condflags, true);
}
const TraceRestrictCondOp condop = GetTraceRestrictCondOp(item);
auto invalid_condition = [&]() -> bool {
switch (condop) {
case TRCO_IS:
case TRCO_ISNOT:
case TRCO_LT:
case TRCO_LTE:
case TRCO_GT:
case TRCO_GTE:
return false;
default:
return true;
}
};
auto invalid_binary_condition = [&]() -> bool {
switch (condop) {
case TRCO_IS:
case TRCO_ISNOT:
return false;
default:
return true;
}
};
auto invalid_order_condition = [&]() -> bool {
if (invalid_binary_condition()) return true;
switch (static_cast<TraceRestrictOrderCondAuxField>(GetTraceRestrictAuxField(item))) {
case TROCAF_STATION:
case TROCAF_WAYPOINT:
case TROCAF_DEPOT:
return false;
default:
return true;
}
};
/* Validation action instruction */
switch (GetTraceRestrictType(item)) {
case TRIT_COND_ENDIF:
case TRIT_COND_UNDEFINED:
break;
case TRIT_COND_TRAIN_LENGTH:
case TRIT_COND_MAX_SPEED:
case TRIT_COND_LOAD_PERCENT:
case TRIT_COND_COUNTER_VALUE:
case TRIT_COND_RESERVED_TILES:
if (invalid_condition()) return unknown_instruction();
break;
case TRIT_COND_CARGO:
case TRIT_COND_TRAIN_GROUP:
case TRIT_COND_TRAIN_IN_SLOT:
case TRIT_COND_TRAIN_OWNER:
case TRIT_COND_RESERVATION_THROUGH:
if (invalid_binary_condition()) return unknown_instruction();
break;
case TRIT_COND_CURRENT_ORDER:
case TRIT_COND_NEXT_ORDER:
case TRIT_COND_LAST_STATION:
if (invalid_order_condition()) return unknown_instruction();
break;
case TRIT_COND_ENTRY_DIRECTION:
if (invalid_binary_condition()) return unknown_instruction();
switch (GetTraceRestrictValue(item)) {
case TRNTSV_NE:
case TRNTSV_SE:
case TRNTSV_SW:
case TRNTSV_NW:
case TRDTSV_FRONT:
case TRDTSV_BACK:
case TRDTSV_TUNBRIDGE_ENTER:
case TRDTSV_TUNBRIDGE_EXIT:
break;
default:
return unknown_instruction();
}
break;
case TRIT_COND_PBS_ENTRY_SIGNAL:
if (invalid_binary_condition()) return unknown_instruction();
switch (static_cast<TraceRestrictPBSEntrySignalAuxField>(GetTraceRestrictAuxField(item))) {
case TRPESAF_VEH_POS:
case TRPESAF_RES_END:
case TRPESAF_RES_END_TILE:
break;
default:
return unknown_instruction();
}
break;
case TRIT_COND_PHYS_PROP:
if (invalid_condition()) return unknown_instruction();
switch (static_cast<TraceRestrictPhysPropCondAuxField>(GetTraceRestrictAuxField(item))) {
case TRPPCAF_WEIGHT:
case TRPPCAF_POWER:
case TRPPCAF_MAX_TE:
break;
default:
return unknown_instruction();
}
break;
case TRIT_COND_PHYS_RATIO:
if (invalid_condition()) return unknown_instruction();
switch (static_cast<TraceRestrictPhysPropRatioCondAuxField>(GetTraceRestrictAuxField(item))) {
case TRPPRCAF_POWER_WEIGHT:
case TRPPRCAF_MAX_TE_WEIGHT:
break;
default:
return unknown_instruction();
}
break;
case TRIT_COND_TIME_DATE_VALUE:
if (invalid_condition()) return unknown_instruction();
switch (static_cast<TraceRestrictTimeDateValueField>(GetTraceRestrictValue(item))) {
case TRTDVF_MINUTE:
case TRTDVF_HOUR:
case TRTDVF_HOUR_MINUTE:
case TRTDVF_DAY:
case TRTDVF_MONTH:
break;
default:
return unknown_instruction();
}
break;
case TRIT_COND_CATEGORY:
if (invalid_binary_condition()) return unknown_instruction();
switch (static_cast<TraceRestrictCatgeoryCondAuxField>(GetTraceRestrictAuxField(item))) {
case TRCCAF_ENGINE_CLASS:
break;
default:
return unknown_instruction();
}
break;
case TRIT_COND_TARGET_DIRECTION:
if (invalid_binary_condition()) return unknown_instruction();
switch (static_cast<TraceRestrictTargetDirectionCondAuxField>(GetTraceRestrictAuxField(item))) {
case TRTDCAF_CURRENT_ORDER:
case TRTDCAF_NEXT_ORDER:
break;
default:
return unknown_instruction();
}
switch (GetTraceRestrictValue(item)) {
case DIAGDIR_NE:
case DIAGDIR_SE:
case DIAGDIR_SW:
case DIAGDIR_NW:
break;
default:
return unknown_instruction();
}
break;
case TRIT_COND_TRAIN_STATUS:
if (invalid_binary_condition()) return unknown_instruction();
switch (static_cast<TraceRestrictTrainStatusValueField>(GetTraceRestrictValue(item))) {
case TRTSVF_EMPTY:
case TRTSVF_FULL:
case TRTSVF_BROKEN_DOWN:
case TRTSVF_NEEDS_REPAIR:
case TRTSVF_REVERSING:
case TRTSVF_HEADING_TO_STATION_WAYPOINT:
case TRTSVF_HEADING_TO_DEPOT:
case TRTSVF_LOADING:
case TRTSVF_WAITING:
case TRTSVF_LOST:
case TRTSVF_REQUIRES_SERVICE:
break;
default:
return unknown_instruction();
}
break;
case TRIT_COND_SLOT_OCCUPANCY:
if (invalid_condition()) return unknown_instruction();
switch (static_cast<TraceRestrictSlotOccupancyCondAuxField>(GetTraceRestrictAuxField(item))) {
case TRSOCAF_OCCUPANTS:
case TRSOCAF_REMAINING:
break;
default:
return unknown_instruction();
}
break;
default:
return unknown_instruction();
}
/* Determine actions_used_flags */
switch (GetTraceRestrictType(item)) {
case TRIT_COND_ENDIF:
case TRIT_COND_UNDEFINED:
@ -1043,13 +1258,29 @@ CommandCost TraceRestrictProgram::Validate(const std::vector<TraceRestrictItem>
break;
default:
return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_UNKNOWN_INSTRUCTION);
/* Validation has already been done, above */
NOT_REACHED();
}
} else {
switch (GetTraceRestrictType(item)) {
case TRIT_PF_DENY:
actions_used_flags |= TRPAUF_PF;
break;
case TRIT_PF_PENALTY:
actions_used_flags |= TRPAUF_PF;
switch (static_cast<TraceRestrictPathfinderPenaltyAuxField>(GetTraceRestrictAuxField(item))) {
case TRPPAF_VALUE:
break;
case TRPPAF_PRESET:
if (GetTraceRestrictValue(item) >= TRPPPI_END) return unknown_instruction();
break;
default:
return unknown_instruction();
}
break;
case TRIT_RESERVE_THROUGH:
@ -1078,8 +1309,7 @@ CommandCost TraceRestrictProgram::Validate(const std::vector<TraceRestrictItem>
break;
default:
NOT_REACHED();
break;
return unknown_instruction();
}
break;
@ -1123,8 +1353,7 @@ CommandCost TraceRestrictProgram::Validate(const std::vector<TraceRestrictItem>
break;
default:
NOT_REACHED();
break;
return unknown_instruction();
}
break;
@ -1139,8 +1368,7 @@ CommandCost TraceRestrictProgram::Validate(const std::vector<TraceRestrictItem>
break;
default:
NOT_REACHED();
break;
return unknown_instruction();
}
break;
@ -1150,26 +1378,72 @@ CommandCost TraceRestrictProgram::Validate(const std::vector<TraceRestrictItem>
case TRIT_NEWS_CONTROL:
actions_used_flags |= TRPAUF_TRAIN_NOT_STUCK;
switch (static_cast<TraceRestrictNewsControlField>(GetTraceRestrictValue(item))) {
case TRNCF_TRAIN_NOT_STUCK:
case TRNCF_CANCEL_TRAIN_NOT_STUCK:
break;
default:
return unknown_instruction();
}
break;
case TRIT_COUNTER:
actions_used_flags |= TRPAUF_CHANGE_COUNTER;
switch (static_cast<TraceRestrictCounterCondOpField>(GetTraceRestrictCondOp(item))) {
case TRCCOF_INCREASE:
case TRCCOF_DECREASE:
case TRCCOF_SET:
break;
default:
return unknown_instruction();
}
break;
case TRIT_PF_PENALTY_CONTROL:
actions_used_flags |= TRPAUF_NO_PBS_BACK_PENALTY;
switch (static_cast<TraceRestrictPfPenaltyControlField>(GetTraceRestrictValue(item))) {
case TRPPCF_NO_PBS_BACK_PENALTY:
case TRPPCF_CANCEL_NO_PBS_BACK_PENALTY:
break;
default:
return unknown_instruction();
}
break;
case TRIT_SPEED_ADAPTATION_CONTROL:
actions_used_flags |= TRPAUF_SPEED_ADAPTATION;
switch (static_cast<TraceRestrictSpeedAdaptationControlField>(GetTraceRestrictValue(item))) {
case TRSACF_SPEED_ADAPT_EXEMPT:
case TRSACF_REMOVE_SPEED_ADAPT_EXEMPT:
break;
default:
return unknown_instruction();
}
break;
case TRIT_SIGNAL_MODE_CONTROL:
actions_used_flags |= TRPAUF_CMB_SIGNAL_MODE_CTRL;
switch (static_cast<TraceRestrictSignalModeControlField>(GetTraceRestrictValue(item))) {
case TRSMCF_NORMAL_ASPECT:
case TRSMCF_SHUNT_ASPECT:
break;
default:
return unknown_instruction();
}
break;
default:
return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_UNKNOWN_INSTRUCTION);
return unknown_instruction();
}
}
}

Loading…
Cancel
Save