Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When using 32-bit OS, automation errors can be seen due to subnet mask calculations #3310

Closed
panlinux opened this issue Mar 2, 2020 · 16 comments · Fixed by #3311
Closed
Assignees
Labels
api API related issues bug Undesired behaviour resolved A fixed issue
Milestone

Comments

@panlinux
Copy link
Contributor

panlinux commented Mar 2, 2020

Describe the bug
Function automation_get_valid_mask() fails in in https://github.com/Cacti/cacti/blob/develop/lib/api_automation.php#L3029 when converting a binary string to integer on a 32bit architecture like armhf.

This results in errors like these in the logs:

02/27/2020 03:15:08 - ERROR PHP WARNING: long2ip() expects parameter 1 to be int, float given in file: /usr/share/cacti/site/lib/api_automation.php  on line: 3006
02/27/2020 03:15:08 - CMDPHP PHP ERROR WARNING Backtrace:  (/poller_automation.php[272]:automation_primeIPAddressTable(), /lib/api_automation.php[3179]:automation_calculate_total_ips(), /lib/api_automation.php[3128]:automation_get_network_info(), /lib/api_automation.php[3034]:automation_get_valid_mask(), /lib/api_automation.php[3006]:long2ip(), CactiErrorHandler())
02/27/2020 03:15:08 - ERROR PHP WARNING: long2ip() expects parameter 1 to be int, float given in file: /usr/share/cacti/site/lib/api_automation.php  on line: 3006
02/27/2020 03:15:08 - CMDPHP PHP ERROR WARNING Backtrace:  (/poller_automation.php[272]:automation_primeIPAddressTable(), /lib/api_automation.php[3182]:automation_calculate_start(), /lib/api_automation.php[3116]:automation_get_network_info(), /lib/api_automation.php[3034]:automation_get_valid_mask(), /lib/api_automation.php[3006]:long2ip(), CactiErrorHandler())

To Reproduce
The error is showing up in the DEP8 Ubuntu tests at http://autopkgtest.ubuntu.com/packages/cacti. One can see they are failing for armhf: http://autopkgtest.ubuntu.com/packages/cacti/focal/armhf

A simple way to reproduce it is to call the automation_get_valid_mask() function with any argument between 0 and 33. For example, 24, on an amd64 machine returns this (with some debugging of mine):

$ php ./automation_get_valid_mask.php 24

bindec(11111111111111111111111100000000):
4294967040
Array
(
    [cidr] => 24
    [subnet] => 255.255.255.0
    [count] => 255
)

On armhf, however:

$ php ./automation_get_valid_mask.php 24

bindec(11111111111111111111111100000000):
4294967040PHP Warning:  long2ip() expects parameter 1 to be int, float given in /home/ubuntu/automation_get_valid_mask.php on line 13

Array
(
    [cidr] => 24
    [subnet] => 
    [count] => 255
)

There are warnings about issues with 32bit architectures on https://www.php.net/manual/en/function.bindec.php and https://www.php.net/manual/en/function.long2ip.php

Expected behavior
On armhf 32bit, automation_get_valid_mask() should work properly. For example, return "255.255.255.0" for "subnet" when called with a range == 24.

Cacti server

  • OS: Ubuntu 20.04 Focal Fossa (development release)
  • php 7.3.15
  • architecture: armhf (32bits)

Misc
This is the script I used. I copied the function from lib/api_automation.php and added some debugging:

<?php
function automation_get_valid_mask($range) {
    $cidr = false;
    if (is_numeric($range)) {
        if ($range > 0 && $range < 33) {
            $cidr = $range;
	    $ones = str_repeat('1',$range);
	    $zeroes = str_repeat('0',32-$range);
	    echo "\nbindec(" . $ones . $zeroes . "):\n";
	    print_r(bindec($ones . $zeroes));
            $mask = array(
                'cidr' => $cidr,
                'subnet' => long2ip(bindec($ones . $zeroes)));
        } else {
            $mask = false;
        }
    } else {
        $mask = automation_get_valid_subnet_cidr($range);
    }

    if ($mask !== false) {
        $mask['count'] = bindec(str_repeat('0',$mask['cidr']) . str_repeat('1',32-$mask['cidr']));
        if ($mask['count'] == 0) {
            $mask['count'] = 1;
        }
    }
    return $mask;
}

$bits = strval($argv[1]);
$mask = automation_get_valid_mask($bits);
echo "\n";
print_r($mask);
?>
@panlinux panlinux changed the title 32bit failure: long2ip(bindec()) 32bit failure in automation_get_valid_mask(): long2ip(bindec()) Mar 2, 2020
@TheWitness
Copy link
Member

Can you do a pull request against the 1.2.x branch?

@panlinux
Copy link
Contributor Author

panlinux commented Mar 2, 2020

I wonder if this is enough to fix it? I'm wary of casting variable types and its behavior between different architectures:

diff --git a/lib/api_automation.php b/lib/api_automation.php
index a1373af08..c71b08705 100644
--- a/lib/api_automation.php
+++ b/lib/api_automation.php
@@ -3003,7 +3003,7 @@ function automation_get_valid_mask($range) {
 			$cidr = $range;
 			$mask = array(
 				'cidr' => $cidr,
-				'subnet' => long2ip(bindec(str_repeat('1',$range) . str_repeat('0',32-$range))));
+				'subnet' => long2ip((int)bindec(str_repeat('1',$range) . str_repeat('0',32-$range))));
 		} else {
 			$mask = false;
 		}

@panlinux
Copy link
Contributor Author

panlinux commented Mar 2, 2020

With the casting change you mean?

@panlinux
Copy link
Contributor Author

panlinux commented Mar 2, 2020

Maybe this is better:

diff --git a/lib/api_automation.php b/lib/api_automation.php
index a1373af08..b9fffbeeb 100644
--- a/lib/api_automation.php
+++ b/lib/api_automation.php
@@ -3003,7 +3003,7 @@ function automation_get_valid_mask($range) {
 			$cidr = $range;
 			$mask = array(
 				'cidr' => $cidr,
-				'subnet' => long2ip(bindec(str_repeat('1',$range) . str_repeat('0',32-$range))));
+				'subnet' => long2ip((2**$range-1) << (32-$range)))
 		} else {
 			$mask = false;
 		}

@netniV netniV added api API related issues bug Undesired behaviour labels Mar 2, 2020
@netniV netniV self-assigned this Mar 2, 2020
@netniV netniV linked a pull request Mar 3, 2020 that will close this issue
@netniV
Copy link
Member

netniV commented Mar 5, 2020

Has this been tested against PHP 5.4 or 5.5? I suspect not as I just noticed that Travis has moaned about the use of 2**

@panlinux
Copy link
Contributor Author

panlinux commented Mar 5, 2020

No, I didn't test against those versions of php. Is there no ** operator there, or is it an issue with parenthesis?

@netniV
Copy link
Member

netniV commented Mar 5, 2020

I was not sure, I briefly saw that Travis didn't like it, so I looked at the php docs:

$a ** $b | Exponentiation | Result of raising $a to the $b'th power. Introduced in PHP 5.6.

So, we can only use that if PHP 5.4 or later, which unfortunately mean we either roll back the change or make an if statement and accept that under 5.4/5.5, there is a flaw in the logic on little endian systems making it a known issue.

@panlinux
Copy link
Contributor Author

panlinux commented Mar 5, 2020 via email

@netniV
Copy link
Member

netniV commented Mar 6, 2020

That would be good thanks as unfortunately I don't have a system to test that against. Took me a while to come up with my original code but yours was a lot simpler :)

Could we reverse the bit order depending on the endian using my original code for older PHP versions?

@panlinux
Copy link
Contributor Author

panlinux commented Mar 6, 2020

How far back in terms of PHP do you need to go?

@panlinux
Copy link
Contributor Author

panlinux commented Mar 6, 2020

If you prefer to leave your old code with "1" and "0" strings, then maybe the (int) casting change from #3310 (comment) would be best, but we would have to test on the old php versions as well.

I did test the ** fix for endianess, and no changes were required, I'm guessing long2ip() knows about these things. At least in php7.3.

The oldest php I can get is the one from Ubuntu Precise: 5.3.10.

@panlinux
Copy link
Contributor Author

panlinux commented Mar 6, 2020

This worked on precise's php 5.3.10:

'subnet' => long2ip((pow(2,$range)-1) << (32-$range)));

Let me try on other architectures now.

@panlinux
Copy link
Contributor Author

panlinux commented Mar 6, 2020

The oldest php I can get on big-endian (s390x in this case) is php 7.0.33, from ubuntu xenial, same from debian stretch. The script keeps working fine there, with the pow() modification. Would you like a PR for this change?

@netniV
Copy link
Member

netniV commented Mar 6, 2020

Sure if you already have it that would be great :) Otherwise I can just apply the change over the weekend.

@cigamit
Copy link
Member

cigamit commented Mar 8, 2020

The pow() function is working well in x86_64 on PHP5.4. So, I guess this is resolved then?

@cigamit cigamit added the resolved A fixed issue label Mar 8, 2020
@cigamit cigamit added this to the 1.2.11 milestone Mar 8, 2020
@netniV
Copy link
Member

netniV commented Mar 8, 2020

Yes, I believe so. Be nice when PHP 5 is no longer a worry.

@netniV netniV closed this as completed Mar 8, 2020
@netniV netniV changed the title 32bit failure in automation_get_valid_mask(): long2ip(bindec()) When using 32-bit OS, automation errors can be seen due to subnet mask calculations Apr 5, 2020
@github-actions github-actions bot locked and limited conversation to collaborators Jul 5, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api API related issues bug Undesired behaviour resolved A fixed issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants