Problem:
The (int) conversion in _db_query_callback() causes all integers that are bigger than the maximum value of a signed integer to overflow.
This can be a problem when working with dates >= ~year 2038. The data type for timestamps used in Drupal is UNSIGNED INT(11) with a range between 0- 4294967295. The (int) conversion of %d modifier variables in _db_query_callback() forces to a range of -2147483648 to 2147483647. (Is this range platform dependent?)
Demonstration:
$maxdateunsigned = 4294967295; // 2106-02-07 02:28:15 - already 2147483648 would cause an overflow
$maxdatesigned = 2147483647; // 2038-01-18 23:14:07
db_query("INSERT INTO {node} ( `nid` ,
`vid` ,
`type` ,
`title` ,
`uid` ,
`status` ,
`created` ,
`changed` ,
`comment` ,
`promote` ,
`moderate` ,
`sticky` )
VALUES (
%d , %d, '', '', '0', '1', %d, '0', '0', '0', '0', '0'
)", $maxdatesigned, $maxdatesigned, $maxdatesigned);
/*
resulting query:
INSERT INTO node ( `nid` , `vid` , `type` , `title` , `uid` , `status` , `created` , `changed` , `comment` , `promote` , `moderate` , `sticky` ) VALUES ( 2147483647 , 2147483647, '', '', '0', '1', 2147483647, '0', '0', '0', '0', '0' )
*/
db_query("INSERT INTO {node} ( `nid` ,
`vid` ,
`type` ,
`title` ,
`uid` ,
`status` ,
`created` ,
`changed` ,
`comment` ,
`promote` ,
`moderate` ,
`sticky` )
VALUES (
%d , %d, '', '', '0', '1', %d, '0', '0', '0', '0', '0'
)", $maxdateunsigned, $maxdateunsigned, $maxdateunsigned);
/*
resulting query:
INSERT INTO node ( `nid` , `vid` , `type` , `title` , `uid` , `status` , `created` , `changed` , `comment` , `promote` , `moderate` , `sticky` ) VALUES ( -1 , -1, '', '', '0', '1', -1, '0', '0', '0', '0', '0' )
*/
Solution:
Passing the value through modifier %f circumvents this problem. The actual problem is that PHP is lacking an (unsigned int) type conversion. An intermediate solution might be to implement a fake %u modifier for unsigned integers that converts with (float):
function _db_query_callback($match, $init = FALSE) {
static $args = NULL;
if ($init) {
$args = $match;
return;
}
switch ($match[1]) {
case '%d': // We must use type casting to int to convert FALSE/NULL/(TRUE?)
return (int) array_shift($args); // We don't need db_escape_string as numbers are db-safe
// new
case '%u':
return (float) array_shift($args);
case '%s':
return db_escape_string(array_shift($args));
case '%%':
return '%';
case '%f':
return (float) array_shift($args);
case '%b': // binary data
return db_encode_blob(array_shift($args));
}
}
Comments
Comment #1
chx commentedhttp://drupal.org/node/143933