Preventing Off-by-One Holes in PHP (and C++) Off-by-one errors are a common programming mistake. Here is a simple solution to prevent these kind of bugs. Autor/Author: Dipl.-Inform. (FH) Steffen Wendzel (Date/Datum: 2008-09-29-13:35, Hits: 1431)
Let me first explain what an off-by-one error is: The following code checks if the user ID of a given user is within the range of a priviledged user (user ID 1 to 999) on some virtual system.
if (uid >= 1 && uid <= 1000) {
admin_mode = true;
}
This code contains a so called off-by-one bug. Since the uid value is compared with everything less/eqal thousand the user ID 1000 (an unpriviledged user!) is a priviledged user too!
The correct check should either be uid <= 999 or uid < 1000.
An easy solution
I wrote a very simple solution for C++ and PHP called libcmle. It is a beta code and far away from being finished due to the lack of features but who cares. It is stable enough to take a look at it. The C++ version is documented on the project site but the PHP version was added today and I will describe it within this posting.
You can find the PHP code here. It is only one file and one can include it in its own projects by a simple call to include("cmle.php");.
How does it work?
The mechanism is pretty simple: First one has to create a _mNum object and stores the minimal and the maximal allowed values of this object in it (it is also possibly just to store one of these values in it if there is only one limit).
The second step is to write a tiny handler class with a method called 'call' that is called by the _nNum class if the value went out of range.
Here is a full example:
include("cmle.php");
/* User defined handler class with a call function */
class Handler {
function call($old, $new)
{
echo 'This should NOT happen and seems to be an off-by-one bug! ';
echo 'The value is still ',$old,' but the order was to set it to ',$new,' ';
exit(1);
}
}
/* Create a new _mNum object */
$Ney = new _mNum();
/* define the range of priviledged user IDs */
$Ney->SetMin(1);
$Ney->SetMax(999);
/* set our handler */
$Ney->SetHandler(new Handler);
/* play around */
$Ney->Set(998);
echo "value=",$Ney->Get(),' ';
$Ney->Set(999);
echo "value=",$Ney->Get(),' ';
/* This line should kill it */
$Ney->Set(1000);
echo "value=",$Ney->Get(),' ';
The output of this code would be:
value=998
value=999
This should NOT happen and seems to be an off-by-one bug!
The value is still 999 but the order was to set it to 1000
But is it possibly to do some calculations? Yes, but you need a little bit more code:
$Ney->Set($Ney->Get() + 9999999);
More information is available in the ranges part of the libcmle documentation:
here ____________
|