Apparently, JavaScript can’t do 64-bit math

Sometimes fuzzy variable types can bite you in the ass.

I tested my initial implementation of IPv6 support in Flagfox on quite a few sites and all seemed well when I released the first test version with it, Flagfox 4.1b2. Today I decided to test a wider variety of IPv6 addresses and compare them to expected results in the raw list. It turns out that a few of them were being looked up wrong. The problem in adding IPv6 support is that IPv6 addresses are big. Not just a few times bigger; way bigger. IPv4 addresses are 32-bit and IPv6 addresses are 128-bit. Four times the bits does not mean four times the number of possible address. Each bit you go up doubles the maximum number that can be represented, thus the number range isn’t 4 times larger, it’s 79228162514264337593543950336 times larger. To borrow a metaphor from The TCP/IP Guide: If you imagine the IPv4 address space is the size of a 1.6 in (4.1 cm) square, then the IPv6 address space would be represented by a square the size of the solar system. These IPv6 addresses when represented in full are quite big numbers, too big for me to deal with in Flagfox in fact. Fortunately, the way IPv6 is set up I don’t need to know about the whole 128 bits, I only really care about the first 64 bits. The first half is for the network and the second half is for the host on that network. The data Maxmind has for IPv6 is a list of those network prefixes and what country they’re in. Most are 32 bits and none are larger than 64 bits. So, I basically did the same thing I did for 32-bit IPv4 addresses except now with the 64-bit network prefix of the IPv6 addresses. Now, anyone guess where I went wrong? Logically, I didn’t; it’s the technical aspect that got me. Flagfox, like most extensions, is written in JavaScript. JavaScript, it turns out, can’t think in 64-bit numbers and its fuzzy variable typing was just silently dropping the less significant parts it couldn’t handle and just plugged along its way without any warning. (I was led astray by some Mozilla docs, but I’ll get to that later) I didn’t catch the problem until I tested artificially made up addresses that were too much for a JS engine to handle, at which point my search algorithm gave incorrect results.

So, what’s my fix then if JavaScript simply cannot handle the math needed to deal with the full IPv6 network prefix? I could theoretically switch out of JavaScript into C for the needed math using JS-ctypes, as C can do 64-bit math just fine, but that would be messy and JS-ctypes is new so I’d have to drop support for everything but Firefox 4.0+ which I don’t want to do. Instead, I’ve decided to just work within the limits. Those 64-bits for the network prefix can be broken down into two parts: the 48-bit global routing prefix and the 16-bit subnet ID. (The TCP/IP Guide reference on that is here; that site has been really helpful) Fortunately, JavaScript can deal with 48-bit numbers, which may be a weird size to do math with in programs, but it works. So for Flagfox 4.1b3 I’ve changed everything to search by the 48-bit main network prefix instead of the full 64-bit prefix including the sub-network ID. Out of the 6352 IPv6 address ranges in Maxmind’s current database, only 3 go down to the subnet level, so over 99.95% of the ranges are just fine with this. Unfortunately, this means any networks with subnet ranges in the database that span multiple countries aren’t able to be supported, but this is a very rare occurrence so it shouldn’t be a problem (for now). Hopefully in the future JavaScript just gets 64-bit integer support and I can upgrade to the full route I initially tried.

Now, how did I make this mistake in the first place? I’m using the nsIBinaryInputStream interface to read my IP database files and it has a perfectly normally listed read64() method just like the read32() method I was already using. There’s actually not anything wrong with this if you’re using that interface from C, but if you’re using it from JS, then you’re going to hit this problem. In order to hopefully avoid someone else making this same mistake, I have edited the online documentation to add a little warning next to the read64() method about the fact that JS does not currently support 64-bit integers.

Advertisements

2 Comments

  1. RobertJ

    Thanks you for your fantastic effort!

    This reminds me of one of my first uses of a computer to invert a 115 x 115 matrix in a machine that had a total of 24K (that’s correct 24K) words of memory for the program and all the data.

    • You’re welcome, and thanks for the thanks. 🙂

      Writing Flagfox is sometimes fun and sometimes a pain in the ass, but it’s nice to know that people appreciate the work. (and that someone is actually reading this blog)