From 10c4c04d8d161b69efbb94e4bdeab7e7c18e1773 Mon Sep 17 00:00:00 2001 From: Rudolf Polzer Date: Fri, 3 Jan 2025 18:28:23 -0500 Subject: [PATCH] OP_DIV_*: add division by zero checks. Now all the OP_DIV operations emit a warning and proceed on division by zero, instead of potentially crashing the program. This also handles the special case of -2147483648 / -1, which also traps on x86 and thus must be handled. --- prvm_execprogram.h | 44 +++++++++++++++++++++++++++++++++++++++++--- qtypes.h | 4 ++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/prvm_execprogram.h b/prvm_execprogram.h index 173677fa..1988dac0 100644 --- a/prvm_execprogram.h +++ b/prvm_execprogram.h @@ -884,21 +884,59 @@ prvm_eval_t *src; OPC->vector[2] = (prvm_vec_t) OPB->_int * OPA->vector[2]; DISPATCH_OPCODE(); HANDLE_OPCODE(OP_DIV_VF): + if( OPB->_float != 0.0f ) { float temp = 1.0f / OPB->_float; OPC->vector[0] = temp * OPA->vector[0]; OPC->vector[1] = temp * OPA->vector[1]; OPC->vector[2] = temp * OPA->vector[2]; } + else + { + PRE_ERROR(); + VM_Warning(prog, "Attempted division of '%f %f %f' by zero\n", OPA->vector[0], OPA->vector[1], OPA->vector[2]); + OPC->vector[0] = 0.0f; + OPC->vector[1] = 0.0f; + OPC->vector[2] = 0.0f; + } DISPATCH_OPCODE(); HANDLE_OPCODE(OP_DIV_I): - OPC->_int = OPA->_int / OPB->_int; + // NOTE: This also catches the second kind of division that can trap, namely, -2147483648 / -1, + // whose result is not representable as int32_t and raises the same CPU exception. + if( OPB->_int != 0 && (OPB->_int != -1 || OPA->_int != PRVM_INT_MIN) ) + { + OPC->_int = OPA->_int / OPB->_int; + } + else + { + PRE_ERROR(); + VM_Warning(prog, "Attempted division of %d by %d\n", (int)OPA->_int, (int)OPB->_int); + OPC->_int = 0; + } DISPATCH_OPCODE(); HANDLE_OPCODE(OP_DIV_IF): - OPC->_float = ((prvm_vec_t) OPA->_int) / OPB->_float; + if( OPB->_float != 0.0f ) + { + OPC->_float = ((prvm_vec_t) OPA->_int) / OPB->_float; + } + else + { + PRE_ERROR(); + VM_Warning(prog, "Attempted division of %d by zero\n", (int)OPA->_int); + OPC->_float = 0; + } DISPATCH_OPCODE(); HANDLE_OPCODE(OP_DIV_FI): - OPC->_float = OPA->_float / (prvm_vec_t) OPB->_int; + if( OPB->_int != 0 ) + { + OPC->_float = OPA->_float / (prvm_vec_t) OPB->_int; + } + else + { + PRE_ERROR(); + VM_Warning(prog, "Attempted division of %f by zero\n", OPA->_float); + OPC->_float = 0; + } DISPATCH_OPCODE(); HANDLE_OPCODE(OP_CONV_ITOF): OPC->_float = OPA->_int; diff --git a/qtypes.h b/qtypes.h index 7e10a4fa..653364b9 100644 --- a/qtypes.h +++ b/qtypes.h @@ -49,12 +49,16 @@ typedef int64_t prvm_int_t; typedef uint64_t prvm_uint_t; #define PRVM_PRIi PRIi64 #define PRVM_PRIu PRIu64 +#define PRVM_INT_MIN INT64_MIN +#define PRVM_INT_MAX INT64_MAX #else typedef float prvm_vec_t; typedef int32_t prvm_int_t; typedef uint32_t prvm_uint_t; #define PRVM_PRIi PRIi32 #define PRVM_PRIu PRIu32 +#define PRVM_INT_MIN INT32_MIN +#define PRVM_INT_MAX INT32_MAX #endif typedef prvm_vec_t prvm_vec3_t[3]; -- 2.39.5