mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Fix signed integer overflow issues
It seemed that the consensus in our discussions of signed integer overflow, which invokes undefined behavior in C, was that integer arithmetic should be two's complement and there should be no warning for overflows. I have implemented that by converting values to unsigned types when appropriate. These changes will mostly preserve existing behavior, except for a few cases that were being handled incorrectly before. The case of dividing INT_MIN by -1 previously resulted in a CPU exception and program termination. Now, that case is detected and results in a warning and a value of INT_MIN. Similarly, INT_MIN % -1 would have resulted in a CPU exception. Since this is a mathematically valid operation with a result of 0, it now simply gives that result without a warning. I noticed that in rpn.c, there were attempts in certain operation handlers to validate the nVal members of the source expressions even when the expressions may have been relocatable expressions with meaningless numbers for the nVal member. This could have caused spurious errors/warnings, so I made those handlers confirm that isReloc is false before validating nVal. Also, integer constants that are too large now result in a warning. The post-conversion values have not been changed, in order to preserve backward compatibility.
This commit is contained in:
@@ -319,7 +319,7 @@ void rpn_ADD(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
expr->nVal = (src1->nVal + src2->nVal);
|
||||
expr->nVal = ((uint32_t)src1->nVal + (uint32_t)src2->nVal);
|
||||
pushbyte(expr, RPN_ADD);
|
||||
}
|
||||
|
||||
@@ -327,7 +327,7 @@ void rpn_SUB(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
expr->nVal = (src1->nVal - src2->nVal);
|
||||
expr->nVal = ((uint32_t)src1->nVal - (uint32_t)src2->nVal);
|
||||
pushbyte(expr, RPN_SUB);
|
||||
}
|
||||
|
||||
@@ -360,15 +360,18 @@ void rpn_SHL(struct Expression *expr, const struct Expression *src1,
|
||||
{
|
||||
joinexpr();
|
||||
|
||||
if (src1->nVal < 0)
|
||||
warning("Left shift of negative value: %d", src1->nVal);
|
||||
if (!expr->isReloc) {
|
||||
if (src1->nVal < 0)
|
||||
warning("Left shift of negative value: %d", src1->nVal);
|
||||
|
||||
if (src2->nVal < 0)
|
||||
fatalerror("Shift by negative value: %d", src2->nVal);
|
||||
else if (src2->nVal >= 32)
|
||||
fatalerror("Shift by too big value: %d", src2->nVal);
|
||||
if (src2->nVal < 0)
|
||||
fatalerror("Shift by negative value: %d", src2->nVal);
|
||||
else if (src2->nVal >= 32)
|
||||
fatalerror("Shift by too big value: %d", src2->nVal);
|
||||
|
||||
expr->nVal = ((uint32_t)src1->nVal << src2->nVal);
|
||||
}
|
||||
|
||||
expr->nVal = (src1->nVal << src2->nVal);
|
||||
pushbyte(expr, RPN_SHL);
|
||||
}
|
||||
|
||||
@@ -376,12 +379,16 @@ void rpn_SHR(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
if (src2->nVal < 0)
|
||||
fatalerror("Shift by negative value: %d", src2->nVal);
|
||||
else if (src2->nVal >= 32)
|
||||
fatalerror("Shift by too big value: %d", src2->nVal);
|
||||
|
||||
expr->nVal = (src1->nVal >> src2->nVal);
|
||||
if (!expr->isReloc) {
|
||||
if (src2->nVal < 0)
|
||||
fatalerror("Shift by negative value: %d", src2->nVal);
|
||||
else if (src2->nVal >= 32)
|
||||
fatalerror("Shift by too big value: %d", src2->nVal);
|
||||
|
||||
expr->nVal = (src1->nVal >> src2->nVal);
|
||||
}
|
||||
|
||||
pushbyte(expr, RPN_SHR);
|
||||
}
|
||||
|
||||
@@ -389,7 +396,7 @@ void rpn_MUL(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
expr->nVal = (src1->nVal * src2->nVal);
|
||||
expr->nVal = ((uint32_t)src1->nVal * (uint32_t)src2->nVal);
|
||||
pushbyte(expr, RPN_MUL);
|
||||
}
|
||||
|
||||
@@ -397,10 +404,19 @@ void rpn_DIV(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
if (src2->nVal == 0)
|
||||
fatalerror("Division by zero");
|
||||
|
||||
expr->nVal = (src1->nVal / src2->nVal);
|
||||
if (!expr->isReloc) {
|
||||
if (src2->nVal == 0)
|
||||
fatalerror("Division by zero");
|
||||
|
||||
if (src1->nVal == INT32_MIN && src2->nVal == -1) {
|
||||
warning("Division of min value by -1");
|
||||
expr->nVal = INT32_MIN;
|
||||
} else {
|
||||
expr->nVal = (src1->nVal / src2->nVal);
|
||||
}
|
||||
}
|
||||
|
||||
pushbyte(expr, RPN_DIV);
|
||||
}
|
||||
|
||||
@@ -408,17 +424,24 @@ void rpn_MOD(struct Expression *expr, const struct Expression *src1,
|
||||
const struct Expression *src2)
|
||||
{
|
||||
joinexpr();
|
||||
if (src2->nVal == 0)
|
||||
fatalerror("Division by zero");
|
||||
|
||||
expr->nVal = (src1->nVal % src2->nVal);
|
||||
if (!expr->isReloc) {
|
||||
if (src2->nVal == 0)
|
||||
fatalerror("Division by zero");
|
||||
|
||||
if (src1->nVal == INT32_MIN && src2->nVal == -1)
|
||||
expr->nVal = 0;
|
||||
else
|
||||
expr->nVal = (src1->nVal % src2->nVal);
|
||||
}
|
||||
|
||||
pushbyte(expr, RPN_MOD);
|
||||
}
|
||||
|
||||
void rpn_UNNEG(struct Expression *expr, const struct Expression *src)
|
||||
{
|
||||
*expr = *src;
|
||||
expr->nVal = -expr->nVal;
|
||||
expr->nVal = -(uint32_t)expr->nVal;
|
||||
pushbyte(expr, RPN_UNSUB);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user