@@ -119,31 +119,9 @@ static void r_map_plane(int y, int x1, int x2)
119119 fixed_t length ;
120120 unsigned index ;
121121
122- /* y indexes cachedheight[]/cacheddistance[]/cachedxstep[]/cachedystep[]
123- * below, all sized SCREENHEIGHT - a y outside that range (observed on
124- * this port: y=255 against a 200-entry array, well past even
125- * viewheight) is an out-of-bounds array write, not just a "debug
126- * assertion". This used to be gated behind CONFIG_GAMES_NXDOOM_
127- * RANGECHECK and fatal (i_error(), which tears down the whole process
128- * on what vanilla Doom would just render as one glitched span) - both
129- * wrong: the memory-safety check must not be optional, and killing the
130- * entire game over one bad plane span is worse than just not drawing
131- * it. Clamp y into range instead of touching memory outside the
132- * buffers' real bounds - this still renders the span (as one glitched
133- * row, the same "wrong but visible" failure mode vanilla DOOM has) so
134- * a bad plane doesn't leave a blank gap on screen either.
135- *
136- * The clamp bound must be viewheight, not SCREENHEIGHT: this y is
137- * stored into ds_y and later used by r_draw_span() to index
138- * ylookup[] (r_draw.c), which r_init_buffer() only populates for
139- * [0, viewheight) - viewheight can be smaller than SCREENHEIGHT (a
140- * sub-window within the physical screen), so entries from viewheight
141- * up to SCREENHEIGHT are zero-initialized (NULL) pointers. Clamping
142- * to SCREENHEIGHT - 1 instead of viewheight - 1 traded the original
143- * out-of-bounds write for a NULL-pointer-plus-offset framebuffer
144- * write - confirmed on real hardware as a load/store exception at a
145- * small virtual address. viewheight is always <= SCREENHEIGHT, so
146- * this bound is safe for cachedheight[]/etc. too.
122+ /* Ensure array indices are in range before access. The bound is
123+ * viewheight rather than SCREENHEIGHT because r_init_buffer() only
124+ * populates ylookup[] for [0, viewheight).
147125 */
148126
149127 if (x2 < x1 || x1 < 0 || x2 >= viewwidth )
@@ -199,34 +177,14 @@ static void r_map_plane(int y, int x1, int x2)
199177 spanfunc ();
200178}
201179
202- /* Row indices into spanstart[] (sized SCREENHEIGHT) that are only ever
203- * safe to use as an array index within that range - t1/b1/t2/b2 in
204- * r_make_spans() below are also compared directly against each other to
205- * drive the span-tracking state machine (including vanilla DOOM's 0xff
206- * sentinel for "no span"/edge-of-plane), and that comparison logic must
207- * see the real, un-clamped values or the sentinel handling breaks. Only
208- * the array touches themselves need guarding.
209- */
210-
211180static inline boolean r_row_in_range (int row )
212181{
213182 return row >= 0 && row < SCREENHEIGHT ;
214183}
215184
216185static void r_make_spans (int x , int t1 , int b1 , int t2 , int b2 )
217186{
218- /* t1/b1/t2/b2 come from a visplane's top[]/bottom[] arrays. In valid
219- * play these are either a real screen row or vanilla DOOM's 0xff
220- * (255) "no span here" sentinel; the loop conditions normally keep
221- * that sentinel away from spanstart[]. A malformed renderer state
222- * can violate that invariant, however: row 255 was observed reaching
223- * r_map_plane() on real hardware, after spanstart[t1]/[b1] had already
224- * been evaluated as the call argument. Guard every spanstart[] touch
225- * directly instead of altering t1/b1/t2/b2, so the state-machine
226- * comparisons and normal sentinel handling remain unchanged. An
227- * invalid closing row uses column zero as its bounded fallback; an
228- * invalid opening row is ignored.
229- */
187+ /* Check that row is in range before indexing arrays. */
230188
231189 while (t1 < t2 && t1 <= b1 )
232190 {
@@ -270,13 +228,6 @@ static void r_make_spans(int x, int t1, int b1, int t2, int b2)
270228void r_init_planes (void )
271229{
272230#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS
273- /* These renderer scratch buffers are sized for a comfortable margin
274- * above vanilla DOOM's original limits and, on a DRAM-constrained
275- * target, blow the internal DRAM budget as static arrays - opt-in
276- * heap allocation instead (comes out of the PSRAM-backed user heap
277- * on this target) via CONFIG_GAMES_NXDOOM_HEAP_BUFFERS.
278- */
279-
280231 visplanes = malloc (sizeof (visplane_t ) * CONFIG_GAMES_NXDOOM_MAXVISPLANES );
281232 openings = malloc (sizeof (short ) * MAXOPENINGS );
282233 drawsegs = malloc (sizeof (drawseg_t ) * CONFIG_GAMES_NXDOOM_MAXDRAWSEGS );
@@ -286,29 +237,12 @@ void r_init_planes(void)
286237 if (visplanes == NULL || openings == NULL || drawsegs == NULL ||
287238 vissprites == NULL )
288239 {
289- /* i_error() doesn't necessarily terminate the whole board on this
290- * flat, single address-space build (see the comment below on
291- * relaunch) - free whatever partially succeeded so a failed
292- * allocation attempt doesn't leak across a subsequent relaunch.
293- */
294-
295- free (visplanes );
296- free (openings );
297- free (drawsegs );
298- free (vissprites );
299- visplanes = NULL ;
300- openings = NULL ;
301- drawsegs = NULL ;
302- vissprites = NULL ;
240+ r_shutdown_planes ();
303241
304242 i_error ("r_init_planes: failed to allocate renderer buffers" );
305243 }
306244
307- /* i_quit() can be followed by another r_init_planes() call within the
308- * same boot (relaunching the game via nxpkg on this flat, single
309- * address-space build), so these heap buffers must be freed on exit
310- * or every relaunch leaks the previous allocation permanently.
311- */
245+ /* Free these on exit; the game can be started again in this process. */
312246
313247 i_at_exit (r_shutdown_planes , true);
314248#endif
@@ -323,15 +257,29 @@ void r_init_planes(void)
323257void r_shutdown_planes (void )
324258{
325259#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS
326- free (visplanes );
327- free (openings );
328- free (drawsegs );
329- free (vissprites );
330-
331- visplanes = NULL ;
332- openings = NULL ;
333- drawsegs = NULL ;
334- vissprites = NULL ;
260+ if (visplanes != NULL )
261+ {
262+ free (visplanes );
263+ visplanes = NULL ;
264+ }
265+
266+ if (openings != NULL )
267+ {
268+ free (openings );
269+ openings = NULL ;
270+ }
271+
272+ if (drawsegs != NULL )
273+ {
274+ free (drawsegs );
275+ drawsegs = NULL ;
276+ }
277+
278+ if (vissprites != NULL )
279+ {
280+ free (vissprites );
281+ vissprites = NULL ;
282+ }
335283#endif
336284}
337285
0 commit comments