Use an anonymous function that's invoked immediately to create a new scope.
This way the anonymous function of the change event becomes a closure.
for (var i = 0; i < products.length; i++) {
    for (var j = 0; j < products.length; j++) {
        //anonymous function (outer function)
        (function() {
            var product = products[i][j];
            //now the anonymous function of the change event will be called
            //within this new scope, with each unique product in it
            $(document).on('change', function () {
                //this is function is now a closure with scope of the outer function
                product.checked ? product.checked = false : product.checked = true;
            };
        })();
    }
}
The scope of the product variable will fall inside the anonymous function closure and each change event will have a unique product value associated.
Fiddle: http://jsfiddle.net/mattdlockyer/t2h50aun/1/
Closures: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures
Immediately Invoked Functions: http://en.wikipedia.org/wiki/Immediately-invoked_function_expression
There seems to be another issue with your code, why are you checking document for change?