Skip to content

Commit bed228d

Browse files
Goober5000claude
andauthored
avoid undefined behavior from UNREACHABLE on reachable code paths (scp-fs2open#7555)
* avoid undefined behavior from UNREACHABLE on reachable code paths UNREACHABLE expands to __assume(false) / __builtin_unreachable() in release builds, which is undefined behavior if the code is ever actually reached. Many UNREACHABLE sites only guard against unexpected-but- possible runtime states (values from tables, mission/save files, network packets, scripting, or user input), so reaching them should degrade gracefully rather than invoke UB. Replace UNREACHABLE with Assertion(false, ...) at all such sites. It still pops an error in debug builds, but in release it is a harmless no-op that simply falls through to the following code. Sites that are genuinely unreachable by local invariants (e.g. a switch over a bounded random number) are left as UNREACHABLE. A few converted sites had no safe fall-through -- an uninitialized read, null dereference, or null call would have followed -- so they also get a minimal recovery: initialize the value, guard the dereference, return early, or skip the offending item. Where the converted site was a simple guard of the form if (x) { Assertion(false, ...); return; } hoist the check onto the invariant instead: Assertion(!x, ...); if (x) { return; } This states the expectation directly and shows the offending value in the debug popup, while keeping the same graceful runtime degradation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * addendum After consultation with Lafiel, use UNREACHABLE in situations that are effectively impossible even if not provably impossible. UNREACHABLE = "this shouldn't ever happen"; Assertion = "if this ever happens, it's a coding error". Also fix a few other sites and change a couple to Warnings. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent c05c06d commit bed228d

85 files changed

Lines changed: 192 additions & 180 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

code/actions/expression/TypeDefinition.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const TypeDefinition& TypeDefinition::forValueType(ValueType type)
3838
case ValueType::String:
3939
return s_identifier;
4040
default:
41-
UNREACHABLE("Invalid value type!");
41+
UNREACHABLE("Invalid value type %d!", static_cast<int>(type));
4242
return s_integer; // Make compiler happy
4343
}
4444
}

code/ai/aibig.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1870,7 +1870,7 @@ void ai_big_strafe()
18701870

18711871
/*
18721872
if ( aip->goal_objnum != aip->target_objnum ) {
1873-
UNREACHABLE("The goal objnum does not match the target objnum in ai_big_strafe(), please report to the SCP!"); // what is going on here? - Get Alan
1873+
Assertion(false, "The goal objnum does not match the target objnum in ai_big_strafe(), please report to the SCP!"); // what is going on here? - Get Alan
18741874
aip->mode = AIM_NONE;
18751875
return;
18761876
}

code/ai/aicode.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2734,8 +2734,8 @@ void ai_evade_object(object *evader, object *evaded)
27342734
Assert(evader->instance != -1);
27352735
Assert(Ships[evader->instance].ai_index != -1);
27362736

2737+
Assertion(evaded != evader, "Ship %s is trying to evade itself. Please report to the SCP!", Ships[evader->instance].ship_name); // Bogus! Who tried to get me to evade myself! Trace out and fix!
27372738
if (evaded == evader) {
2738-
UNREACHABLE("Ship %s is trying to evade itself. Please report to the SCP!", Ships[evader->instance].ship_name); // Bogus! Who tried to get me to evade myself! Trace out and fix!
27392739
return;
27402740
}
27412741

@@ -4997,8 +4997,8 @@ void ai_waypoints()
49974997
ai_info *aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
49984998

49994999
// sanity checking for stuff that should never happen
5000+
Assertion(aip->wp_index != INVALID_WAYPOINT_POSITION, "Waypoints should have been started already!");
50005001
if (aip->wp_index == INVALID_WAYPOINT_POSITION) {
5001-
UNREACHABLE("Waypoints should have been started already!");
50025002
ai_start_waypoints(Pl_objp, (aip->wp_list_index < 0) ? 0 : aip->wp_list_index, WPF_REPEAT, 0);
50035003
}
50045004

@@ -11020,8 +11020,8 @@ void ai_guard()
1102011020

1102111021
guard_objp = &Objects[aip->guard_objnum];
1102211022

11023+
Assertion(guard_objp != Pl_objp, "The ship %s has been found to be guarding itself in ai_guard. Please report to the SCP!", shipp->ship_name); // This seems illegal. Why is a ship guarding itself?
1102311024
if (guard_objp == Pl_objp) {
11024-
UNREACHABLE("The ship %s has been found to be guarding itself in ai_guard. Please report to the SCP!", shipp->ship_name); // This seems illegal. Why is a ship guarding itself?
1102511025
aip->guard_objnum = -1;
1102611026
return;
1102711027
}
@@ -13889,7 +13889,7 @@ int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, int allowed_path_
1388913889
// Cyborg: to avoid divide by zero and avoid a logic path that should not exist check for 0 allowed paths -- Coverity 1523287
1389013890
if (num_allowed_paths == 0) {
1389113891
bay_path = Ai_last_arrive_path % bay->num_paths;
13892-
UNREACHABLE("Parent_shipp in ai_acquire_emerge_path somehow does not have any allowed bay paths!");
13892+
Assertion(false, "Parent_shipp in ai_acquire_emerge_path somehow does not have any allowed bay paths!");
1389313893
} else {
1389413894
// cycle through the allowed paths
1389513895
bay_path = allowed_bay_paths[Ai_last_arrive_path % num_allowed_paths];
@@ -14382,7 +14382,7 @@ void ai_execute_behavior(ai_info *aip)
1438214382
ai_lua(aip);
1438314383
break;
1438414384
default:
14385-
UNREACHABLE("Unknown AI Mode! Get a coder!");
14385+
UNREACHABLE("Unknown AI Mode %d! Get a coder!", aip->mode);
1438614386
break;
1438714387
}
1438814388

@@ -15022,7 +15022,7 @@ int aas_1(object *objp, ai_info *aip, vec3d *safe_pos)
1502215022
return 1;
1502315023

1502415024
} else {
15025-
UNREACHABLE("aas_1 has been passed an invalid object type of %d, please report to the SCP!", objp->type);
15025+
Assertion(false, "aas_1 has been passed an invalid object type of %d, please report to the SCP!", objp->type);
1502615026
}
1502715027

1502815028
return 0;
@@ -16398,7 +16398,7 @@ void ai_ship_hit(object *objp_ship, object *hit_objp, const vec3d *hit_normal)
1639816398
} else if (hit_objp->type == OBJ_SHIP) {
1639916399
objp_hitter = hit_objp;
1640016400
} else {
16401-
UNREACHABLE("Should never happen.");
16401+
Assertion(false, "ai_ship_hit has been passed an invalid object type of %d, please report to the SCP!", hit_objp->type);
1640216402
return;
1640316403
}
1640416404
Assert(objp_hitter != nullptr);
@@ -16485,7 +16485,7 @@ void ai_ship_hit(object *objp_ship, object *hit_objp, const vec3d *hit_normal)
1648516485
objp_hitter = hit_objp;
1648616486
hitter_objnum = OBJ_INDEX(hit_objp);
1648716487
} else {
16488-
UNREACHABLE("ai_ship_hit has been passed an invalid object type of %d, please report to the SCP!", hit_objp->type);
16488+
Assertion(false, "ai_ship_hit has been passed an invalid object type of %d, please report to the SCP!", hit_objp->type);
1648916489
return;
1649016490
}
1649116491

@@ -16616,7 +16616,7 @@ void ai_ship_hit(object *objp_ship, object *hit_objp, const vec3d *hit_normal)
1661616616
case AIM_LUA:
1661716617
return;
1661816618
default:
16619-
UNREACHABLE("Unknown AI Mode! Get a coder!");
16619+
UNREACHABLE("Unknown AI Mode %d! Get a coder!", aip->mode);
1662016620
}
1662116621

1662216622
if (timestamp_elapsed(aip->ok_to_target_timestamp)) {

code/ai/aigoals.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,7 +1253,7 @@ void ai_add_goal_sub_sexp( int sexp, ai_goal_type type, ai_info *aip, ai_goal *a
12531253
} else if ( op == OP_AI_IGNORE_NEW ) {
12541254
aigp->ai_mode = AI_GOAL_IGNORE_NEW;
12551255
} else
1256-
UNREACHABLE("Coding error: unhandled AI goal in ai_add_goal_sub_sexp!");
1256+
UNREACHABLE("Coding error: unhandled AI goal %d in ai_add_goal_sub_sexp!", op);
12571257

12581258
break;
12591259

@@ -2056,7 +2056,7 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
20562056
if (target_ship_entry->shipp()->subsys_info[SUBSYSTEM_TURRET].type_count == 0)
20572057
return ai_achievability::NOT_ACHIEVABLE;
20582058
} else {
2059-
UNREACHABLE("Target name %s is not an arrived ship!", aigp->target_name);
2059+
Assertion(false, "Target name %s is not an arrived ship!", aigp->target_name);
20602060
return ai_achievability::NOT_ACHIEVABLE; // force this goal to be invalid
20612061
}
20622062
}
@@ -2105,8 +2105,8 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
21052105
return ai_achievability::NOT_KNOWN;
21062106

21072107
// we must also determine if we're prevented from docking for any reason
2108+
Assertion(target_ship_entry && target_ship_entry->has_shipp(), "Target name %s is not an arrived ship!", aigp->target_name);
21082109
if (!target_ship_entry || !target_ship_entry->has_shipp()) {
2109-
UNREACHABLE("Target name %s is not an arrived ship!", aigp->target_name);
21102110
return ai_achievability::NOT_ACHIEVABLE; // force this goal to be invalid
21112111
}
21122112
auto goal_objp = target_ship_entry->objp();
@@ -2163,7 +2163,7 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
21632163
if (aip->goal_objnum != target_ship_entry->objnum)
21642164
return ai_achievability::NOT_KNOWN;
21652165
} else {
2166-
UNREACHABLE("Target name %s is not an arrived ship!", aigp->target_name);
2166+
Assertion(false, "Target name %s is not an arrived ship!", aigp->target_name);
21672167
return ai_achievability::NOT_ACHIEVABLE; // force this goal to be invalid
21682168
}
21692169
}
@@ -2177,14 +2177,14 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
21772177
aigp->ai_submode = ship_find_subsys( target_ship_entry->shipp(), aigp->docker.name );
21782178
aigp->flags.remove(AI::Goal_Flags::Subsys_needs_fixup);
21792179
} else {
2180-
UNREACHABLE("Target name %s is not an arrived ship!", aigp->target_name);
2180+
Assertion(false, "Target name %s is not an arrived ship!", aigp->target_name);
21812181
return ai_achievability::NOT_ACHIEVABLE; // force this goal to be invalid
21822182
}
21832183
}
21842184
} else if ( ((aigp->ai_mode == AI_GOAL_IGNORE) || (aigp->ai_mode == AI_GOAL_IGNORE_NEW)) && (status == SHIP_STATUS_ARRIVED) ) {
21852185
// for ignoring a ship, call the ai_ignore object function, then declare the goal satisfied
2186+
Assertion(target_ship_entry && target_ship_entry->has_objp(), "Target name %s is not an arrived ship!", aigp->target_name);
21862187
if (!target_ship_entry || !target_ship_entry->has_objp()) {
2187-
UNREACHABLE("Target name %s is not an arrived ship!", aigp->target_name);
21882188
return ai_achievability::NOT_ACHIEVABLE; // force this goal to be invalid
21892189
}
21902190
auto ignored = target_ship_entry->objp();
@@ -2233,16 +2233,16 @@ ai_achievability ai_mission_goal_achievable( int objnum, ai_goal *aigp )
22332233
{
22342234
// short circuit a couple of cases. Ship not arrived shouldn't happen. Ship gone means
22352235
// we mark the goal as not achievable.
2236+
Assertion(status != SHIP_STATUS_NOT_ARRIVED, "Ship %s cannot rearm a target %s that hasn't arrived; get Allender or a SCP member", shipp->ship_name, aigp->target_name); // get Allender. this shouldn't happen!!!
22362237
if ( status == SHIP_STATUS_NOT_ARRIVED ) {
2237-
UNREACHABLE("Ship %s cannot rearm a target %s that hasn't arrived; get Allender or a SCP member", shipp->ship_name, aigp->target_name); // get Allender. this shouldn't happen!!!
22382238
return ai_achievability::NOT_ACHIEVABLE;
22392239
}
22402240

22412241
if ( status == SHIP_STATUS_GONE )
22422242
return ai_achievability::NOT_ACHIEVABLE;
22432243

2244+
Assertion(target_ship_entry && target_ship_entry->has_shipp(), "Target name %s is not an arrived ship!", aigp->target_name);
22442245
if ( !target_ship_entry || !target_ship_entry->has_shipp() ) {
2245-
UNREACHABLE("Target name %s is not an arrived ship!", aigp->target_name);
22462246
return ai_achievability::NOT_ACHIEVABLE;
22472247
}
22482248

code/bmpman/bmpman.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -599,8 +599,8 @@ int bm_create_3d(int bpp, int w, int h, int d, void* data) {
599599
return -1;
600600

601601
// make sure that we have valid data
602+
Assertion(data != nullptr, "No valid data received for 3D Bitmap creation!");
602603
if (data == nullptr) {
603-
UNREACHABLE("No valid data received for 3D Bitmap creation!");
604604
return -1;
605605
}
606606

@@ -1210,7 +1210,7 @@ static int bm_load_info(BM_TYPE type, const char *filename, CFILE *img_cfp, int
12101210
}
12111211
}
12121212
else {
1213-
UNREACHABLE("Unknown file type specified! This is probably a coding error.");
1213+
UNREACHABLE("Unknown file type %d specified! This is probably a coding error.", type);
12141214

12151215
return -1;
12161216
}

code/cfile/cfile.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -832,7 +832,7 @@ static int cfget_cfile_block()
832832
mprintf(("Out of cfile blocks! Currently opened files:\n"));
833833
dump_opened_files();
834834

835-
UNREACHABLE("There are no more free cfile blocks. This means that there are too many files opened by FSO.\n"
835+
Warning(LOCATION, "There are no more free cfile blocks. This means that there are too many files opened by FSO.\n"
836836
"This is probably caused by a programming or scripting error where a file does not get closed."); // out of free cfile blocks
837837
return -1;
838838
}

code/controlconfig/controlsconfig.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,7 @@ bool control_config_remove_binding(int ctrl, selItem item, bool API_Access)
804804

805805
default:
806806
// Coder forgot to add a case!
807-
UNREACHABLE("Unhandled selItem case.");
807+
UNREACHABLE("Unhandled selItem case: %i", static_cast<int>(item));
808808
}
809809

810810
if (success)
@@ -3120,7 +3120,7 @@ void control_get_axes_readings(int *axis_v, float frame_time)
31203120
case CC_TYPE_AXIS_BTN_POS:
31213121
default:
31223122
//This should never happen, especially with the above Assertion. This is required as incomplete switches on an enum generate warnings
3123-
UNREACHABLE("Unhandled control item type");
3123+
UNREACHABLE("Unhandled control item type %d", static_cast<int>(item.type));
31243124
break;
31253125
}
31263126
}

code/controlconfig/controlsconfigcommon.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2388,7 +2388,7 @@ SCP_string ValToCID(CID id) {
23882388

23892389
if (it == mCIDNameToVal.cend()) {
23902390
// Shouldn't happen
2391-
Error(LOCATION, "Unknown CID value %i", id);
2391+
UNREACHABLE("Unknown CID value %i", id);
23922392
return "NONE";
23932393

23942394
} else {
@@ -2518,9 +2518,11 @@ SCP_string ValToJoy(const CC_bind &bind) {
25182518
if (it == mAxisNameToVal.end()) {
25192519
// should never happen
25202520
UNREACHABLE("Unknown error occured during reverse lookup of joy input string.");
2521-
} // else print out value
2522-
2523-
str = it->first;
2521+
str = "NONE";
2522+
} else {
2523+
// print out value
2524+
str = it->first;
2525+
}
25242526

25252527
/* } else if (bind.flags & CCF_HAT) {
25262528
// TODO Still currently encoded as buttons

code/cutscene/VideoPresenter.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ VideoPresenter::VideoPresenter(const MovieProperties& props) : _properties(props
4949
material_set_unlit(&_rgb_material, _planeTextureHandles[0], 1.0f, true, false);
5050
break;
5151
default:
52-
UNREACHABLE("Unhandled enum value!");
52+
UNREACHABLE("Unhandled enum value %d!", static_cast<int>(props.pixelFormat));
5353
break;
5454
}
5555
}
@@ -86,7 +86,7 @@ void VideoPresenter::uploadVideoFrame(const VideoFramePtr& frame) {
8686
bpp = 32;
8787
break;
8888
default:
89-
UNREACHABLE("Unhandled enum value!");
89+
UNREACHABLE("Unhandled enum value %d!", static_cast<int>(_properties.pixelFormat));
9090
break;
9191
}
9292

@@ -145,7 +145,7 @@ void VideoPresenter::displayFrame(float x1, float y1, float x2, float y2, float
145145
gr_render_primitives(&_rgb_material, PRIM_TYPE_TRISTRIP, &layout, 0, 4, gr_immediate_buffer_handle, offset);
146146
break;
147147
default:
148-
UNREACHABLE("Unhandled enum value!");
148+
UNREACHABLE("Unhandled enum value %d!", static_cast<int>(_properties.pixelFormat));
149149
break;
150150
}
151151
}

code/debris/debris.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ void debris_process_post(object * obj, float frame_time)
337337
break;
338338

339339
default:
340-
UNREACHABLE("Unhandled case %d for electrical arc creation in debris_process_post()!", n);
340+
Assertion(false, "Unhandled case %d for electrical arc creation in debris_process_post()!", n);
341341
}
342342
}
343343
}

0 commit comments

Comments
 (0)