Bug 531915 - part 6 - optionally use fdlibm's sin, cos, and tan in jsmath r=tjr,arai

Adds a new preference javascript.options.use_fdlibm_for_sin_cos_tan (default
false) and uses fdlibm for Math.sin, Math.cos, and Math.tan conditioned on it.

Differential Revision: https://phabricator.services.mozilla.com/D119426
This commit is contained in:
sanketh 2021-08-12 09:59:03 +00:00
Родитель 468c33c7ab
Коммит 21ef77522a
14 изменённых файлов: 249 добавлений и 17 удалений

Просмотреть файл

@ -20,6 +20,7 @@ https_first_disabled = true
skip-if =
(os == "mac") #Bug 1570812
os == 'linux' && bits == 64 && !debug # Bug 1570812
[browser_math.js]
[browser_navigator.js]
https_first_disabled = true
skip-if =

Просмотреть файл

@ -0,0 +1,107 @@
/**
* Bug 531915 - A test for verifying that the JS Math fingerprint is constant
* when using fdlibm for Math.sin, Math.cos, and Math.tan.
*/
async function test_math(rfp_pref, fdlibm_pref) {
await SpecialPowers.pushPrefEnv({
set: [
["javascript.options.use_fdlibm_for_sin_cos_tan", fdlibm_pref],
["privacy.resistFingerprinting", rfp_pref],
],
});
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
TEST_PATH + "file_dummy.html"
);
await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
// override is to check strict equality like we would do in a JS ref test.
function strictly_is(a, b) {
ok(a === b);
}
//
// Tests adapted from https://github.com/arkenfox/TZP/blob/5e3f5ff2c64b4edc7beecd8308aa4f7a3efb49e3/tests/math.html#L158-L319
//
strictly_is(Math.cos(1e251), -0.37419577499634155);
strictly_is(Math.cos(1e140), -0.7854805190645291);
strictly_is(Math.cos(1e12), 0.7914463018528902);
strictly_is(Math.cos(1e130), -0.767224894221913);
strictly_is(Math.cos(1e272), -0.7415825695514536);
strictly_is(Math.cos(1), 0.5403023058681398);
strictly_is(Math.cos(1e284), 0.7086865671674247);
strictly_is(Math.cos(1e75), -0.7482651726250321);
strictly_is(Math.cos(Math.PI), -1);
strictly_is(Math.cos(-1e308), -0.8913089376870335);
strictly_is(Math.cos(13 * Math.E), -0.7108118501064332);
strictly_is(Math.cos(57 * Math.E), -0.536911695749024);
strictly_is(Math.cos(21 * Math.LN2), -0.4067775970251724);
strictly_is(Math.cos(51 * Math.LN2), -0.7017203400855446);
strictly_is(Math.cos(21 * Math.LOG2E), 0.4362848063618998);
strictly_is(Math.cos(25 * Math.SQRT2), -0.6982689820462377);
strictly_is(Math.cos(50 * Math.SQRT1_2), -0.6982689820462377);
strictly_is(Math.cos(21 * Math.SQRT1_2), -0.6534063185820198);
strictly_is(Math.cos(17 * Math.LOG10E), 0.4537557425982784);
strictly_is(Math.cos(2 * Math.LOG10E), 0.6459044007438142);
strictly_is(Math.sin(1e251), -0.9273497301314576);
strictly_is(Math.sin(1e140), -0.6188863822787813);
strictly_is(Math.sin(1e12), -0.6112387023768895);
strictly_is(Math.sin(1e130), 0.6413781736901984);
strictly_is(Math.sin(1e272), 0.6708616046081811);
strictly_is(Math.sin(1), 0.8414709848078965);
strictly_is(Math.sin(1e284), -0.7055234578073583);
strictly_is(Math.sin(1e75), 0.66339975236386);
strictly_is(Math.sin(Math.PI), 1.2246467991473532e-16);
strictly_is(Math.sin(39 * Math.E), -0.7181630308570678);
strictly_is(Math.sin(35 * Math.LN2), -0.765996413898051);
strictly_is(Math.sin(110 * Math.LOG2E), 0.9989410140273757);
strictly_is(Math.sin(7 * Math.LOG10E), 0.10135692924965616);
strictly_is(Math.sin(35 * Math.SQRT1_2), -0.3746357547858202);
strictly_is(Math.sin(21 * Math.SQRT2), -0.9892668187780497);
strictly_is(Math.tan(1e251), 2.478247463217681);
strictly_is(Math.tan(1e140), 0.7879079967710036);
strictly_is(Math.tan(1e12), -0.7723059681318761);
strictly_is(Math.tan(1e130), -0.8359715365344825);
strictly_is(Math.tan(1e272), -0.904635076595654);
strictly_is(Math.tan(1), 1.5574077246549023);
strictly_is(Math.tan(1e284), -0.9955366596368418);
strictly_is(Math.tan(1e75), -0.8865837628611647);
strictly_is(Math.tan(-1e308), 0.5086861259107568);
strictly_is(Math.tan(Math.PI), -1.2246467991473532e-16);
strictly_is(Math.tan(6 * Math.E), 0.6866761546452431);
strictly_is(Math.tan(6 * Math.LN2), 1.6182817135715877);
strictly_is(Math.tan(10 * Math.LOG2E), -3.3537128705376014);
strictly_is(Math.tan(17 * Math.SQRT2), -1.9222955461799982);
strictly_is(Math.tan(34 * Math.SQRT1_2), -1.9222955461799982);
strictly_is(Math.tan(10 * Math.LOG10E), 2.5824856130712432);
//
// Tests adapted from https://github.com/fingerprintjs/fingerprintjs/blob/7096a5589af495f1f46067963e13ad27d887d185/src/sources/math.ts#L32-L64
//
strictly_is(Math.acos(0.123124234234234242), 1.4473588658278522);
strictly_is(Math.acosh(1e308), 709.889355822726);
strictly_is(Math.asin(0.123124234234234242), 0.12343746096704435);
strictly_is(Math.asinh(1), 0.881373587019543);
strictly_is(Math.atanh(0.5), 0.5493061443340548);
strictly_is(Math.atan(0.5), 0.4636476090008061);
strictly_is(Math.sin(-1e300), 0.8178819121159085);
strictly_is(Math.sinh(1), 1.1752011936438014);
strictly_is(Math.cos(10.000000000123), -0.8390715290095377);
strictly_is(Math.cosh(1), 1.5430806348152437);
strictly_is(Math.tan(-1e300), -1.4214488238747245);
strictly_is(Math.tanh(1), 0.7615941559557649);
strictly_is(Math.exp(1), 2.718281828459045);
strictly_is(Math.expm1(1), 1.718281828459045);
strictly_is(Math.log1p(10), 2.3978952727983707);
});
BrowserTestUtils.removeTab(tab);
}
add_task(test_math.bind(null, false, true));
add_task(test_math.bind(null, true, false));
add_task(test_math.bind(null, true, true));

Просмотреть файл

@ -167,6 +167,8 @@ class JS_PUBLIC_API TransitiveCompileOptions {
uint32_t introductionOffset = 0;
bool hasIntroductionInfo = false;
bool useFdlibmForSinCosTan = false;
protected:
TransitiveCompileOptions() = default;

Просмотреть файл

@ -0,0 +1,7 @@
// |jit-test| --use-fdlibm-for-sin-cos-tan
// Test that fdlibm is being used for sin, cos, and tan.
// Tests adapted from https://github.com/arkenfox/TZP/blob/master/tests/math.html#L158-L319
assertEq(Math.cos(1e284), 0.7086865671674247);
assertEq(Math.sin(7*Math.LOG10E), 0.10135692924965616);
assertEq(Math.tan(6*Math.E), 0.6866761546452431);

Просмотреть файл

@ -2329,6 +2329,7 @@ void JS::TransitiveCompileOptions::copyPODTransitiveOptions(
classStaticBlocks = rhs.classStaticBlocks;
useStencilXDR = rhs.useStencilXDR;
useOffThreadParseGlobal = rhs.useOffThreadParseGlobal;
useFdlibmForSinCosTan = rhs.useFdlibmForSinCosTan;
};
void JS::ReadOnlyCompileOptions::copyPODNonTransitiveOptions(
@ -2417,6 +2418,8 @@ JS::CompileOptions::CompileOptions(JSContext* cx) : ReadOnlyCompileOptions() {
useStencilXDR = !UseOffThreadParseGlobal();
useOffThreadParseGlobal = UseOffThreadParseGlobal();
useFdlibmForSinCosTan = math_use_fdlibm_for_sin_cos_tan();
sourcePragmas_ = cx->options().sourcePragmas();
// Certain modes of operation force strict-mode in general.

Просмотреть файл

@ -64,6 +64,16 @@
/************************************************************************/
namespace JS {
/**
* Tell JS engine whether to use fdlibm for Math.sin, Math.cos, and Math.tan.
* Using fdlibm ensures that we don't expose a math fingerprint.
*/
extern JS_PUBLIC_API void SetUseFdlibmForSinCosTan(bool value);
} // namespace JS
/************************************************************************/
struct JSFunctionSpec;
struct JSPropertySpec;

Просмотреть файл

@ -52,6 +52,12 @@ using mozilla::NumberEqualsInt32;
using mozilla::PositiveInfinity;
using mozilla::WrappingMultiply;
static mozilla::Atomic<bool, mozilla::Relaxed> sUseFdlibmForSinCosTan;
JS_PUBLIC_API void JS::SetUseFdlibmForSinCosTan(bool value) {
sUseFdlibmForSinCosTan = value;
}
template <UnaryMathFunctionType F>
static bool math_function(JSContext* cx, HandleValue val,
MutableHandleValue res) {
@ -206,13 +212,25 @@ bool js::math_clz32(JSContext* cx, unsigned argc, Value* vp) {
return true;
}
double js::math_cos_impl(double x) {
bool js::math_use_fdlibm_for_sin_cos_tan() { return sUseFdlibmForSinCosTan; }
double js::math_cos_fdlibm_impl(double x) {
MOZ_ASSERT(sUseFdlibmForSinCosTan);
AutoUnsafeCallWithABI unsafe;
return fdlibm::cos(x);
}
double js::math_cos_native_impl(double x) {
MOZ_ASSERT(!sUseFdlibmForSinCosTan);
AutoUnsafeCallWithABI unsafe;
return cos(x);
}
bool js::math_cos(JSContext* cx, unsigned argc, Value* vp) {
return math_function<math_cos_impl>(cx, argc, vp);
if (sUseFdlibmForSinCosTan) {
return math_function<math_cos_fdlibm_impl>(cx, argc, vp);
}
return math_function<math_cos_native_impl>(cx, argc, vp);
}
double js::math_exp_impl(double x) {
@ -582,18 +600,31 @@ js::math_round(JSContext* cx, unsigned argc, Value* vp) {
return math_round_handle(cx, args[0], args.rval());
}
double js::math_sin_impl(double x) {
double js::math_sin_fdlibm_impl(double x) {
MOZ_ASSERT(sUseFdlibmForSinCosTan);
AutoUnsafeCallWithABI unsafe;
return fdlibm::sin(x);
}
double js::math_sin_native_impl(double x) {
MOZ_ASSERT(!sUseFdlibmForSinCosTan);
AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
return sin(x);
}
bool js::math_sin_handle(JSContext* cx, HandleValue val,
MutableHandleValue res) {
return math_function<math_sin_impl>(cx, val, res);
if (sUseFdlibmForSinCosTan) {
return math_function<math_sin_fdlibm_impl>(cx, val, res);
}
return math_function<math_sin_native_impl>(cx, val, res);
}
bool js::math_sin(JSContext* cx, unsigned argc, Value* vp) {
return math_function<math_sin_impl>(cx, argc, vp);
if (sUseFdlibmForSinCosTan) {
return math_function<math_sin_fdlibm_impl>(cx, argc, vp);
}
return math_function<math_sin_native_impl>(cx, argc, vp);
}
double js::math_sqrt_impl(double x) {
@ -610,13 +641,23 @@ bool js::math_sqrt(JSContext* cx, unsigned argc, Value* vp) {
return math_function<math_sqrt_impl>(cx, argc, vp);
}
double js::math_tan_impl(double x) {
double js::math_tan_fdlibm_impl(double x) {
MOZ_ASSERT(sUseFdlibmForSinCosTan);
AutoUnsafeCallWithABI unsafe;
return fdlibm::tan(x);
}
double js::math_tan_native_impl(double x) {
MOZ_ASSERT(!sUseFdlibmForSinCosTan);
AutoUnsafeCallWithABI unsafe;
return tan(x);
}
bool js::math_tan(JSContext* cx, unsigned argc, Value* vp) {
return math_function<math_tan_impl>(cx, argc, vp);
if (sUseFdlibmForSinCosTan) {
return math_function<math_tan_fdlibm_impl>(cx, argc, vp);
}
return math_function<math_tan_native_impl>(cx, argc, vp);
}
double js::math_log10_impl(double x) {
@ -885,13 +926,22 @@ UnaryMathFunctionType js::GetUnaryMathFunctionPtr(UnaryMathFunction fun) {
case UnaryMathFunction::Log:
return math_log_impl;
case UnaryMathFunction::Sin:
return math_sin_impl;
if (sUseFdlibmForSinCosTan) {
return math_sin_fdlibm_impl;
}
return math_sin_native_impl;
case UnaryMathFunction::Cos:
return math_cos_impl;
if (sUseFdlibmForSinCosTan) {
return math_cos_fdlibm_impl;
}
return math_cos_native_impl;
case UnaryMathFunction::Exp:
return math_exp_impl;
case UnaryMathFunction::Tan:
return math_tan_impl;
if (sUseFdlibmForSinCosTan) {
return math_tan_fdlibm_impl;
}
return math_tan_native_impl;
case UnaryMathFunction::ATan:
return math_atan_impl;
case UnaryMathFunction::ASin:

Просмотреть файл

@ -109,16 +109,20 @@ extern double math_log_impl(double x);
extern bool math_log_handle(JSContext* cx, HandleValue val,
MutableHandleValue res);
extern bool math_use_fdlibm_for_sin_cos_tan();
extern bool math_sin(JSContext* cx, unsigned argc, js::Value* vp);
extern double math_sin_impl(double x);
extern double math_sin_fdlibm_impl(double x);
extern double math_sin_native_impl(double x);
extern bool math_sin_handle(JSContext* cx, HandleValue val,
MutableHandleValue res);
extern bool math_cos(JSContext* cx, unsigned argc, js::Value* vp);
extern double math_cos_impl(double x);
extern double math_cos_fdlibm_impl(double x);
extern double math_cos_native_impl(double x);
extern bool math_exp(JSContext* cx, unsigned argc, js::Value* vp);
@ -126,7 +130,8 @@ extern double math_exp_impl(double x);
extern bool math_tan(JSContext* cx, unsigned argc, js::Value* vp);
extern double math_tan_impl(double x);
extern double math_tan_fdlibm_impl(double x);
extern double math_tan_native_impl(double x);
extern bool math_log10(JSContext* cx, unsigned argc, js::Value* vp);

Просмотреть файл

@ -638,6 +638,8 @@ bool shell::compileOnly = false;
bool shell::disableOOMFunctions = false;
bool shell::defaultToSameCompartment = true;
bool shell::useFdlibmForSinCosTan = false;
#ifdef DEBUG
bool shell::dumpEntrainedVariables = false;
bool shell::OOM_printAllocationCount = false;
@ -11274,6 +11276,7 @@ static bool SetContextOptions(JSContext* cx, const OptionParser& op) {
enableTopLevelAwait = op.getBoolOption("enable-top-level-await");
enableClassStaticBlocks = op.getBoolOption("enable-class-static-blocks");
useOffThreadParseGlobal = op.getBoolOption("off-thread-parse-global");
useFdlibmForSinCosTan = op.getBoolOption("use-fdlibm-for-sin-cos-tan");
JS::ContextOptionsRef(cx)
.setAsmJS(enableAsmJS)
@ -11307,6 +11310,7 @@ static bool SetContextOptions(JSContext* cx, const OptionParser& op) {
.setClassStaticBlocks(enableClassStaticBlocks);
JS::SetUseOffThreadParseGlobal(useOffThreadParseGlobal);
JS::SetUseFdlibmForSinCosTan(useFdlibmForSinCosTan);
// Check --fast-warmup first because it sets default warm-up thresholds. These
// thresholds can then be overridden below by --ion-eager and other flags.
@ -12490,7 +12494,9 @@ int main(int argc, char** argv) {
!op.addBoolOption('\0', "reprl", "Enable REPRL mode for fuzzing") ||
#endif
!op.addStringOption('\0', "telemetry-dir", "[directory]",
"Output telemetry results in a directory")) {
"Output telemetry results in a directory") ||
!op.addBoolOption('\0', "use-fdlibm-for-sin-cos-tan",
"Use fdlibm for Math.sin, Math.cos, and Math.tan")) {
return EXIT_FAILURE;
}

Просмотреть файл

@ -157,6 +157,8 @@ extern bool dumpEntrainedVariables;
extern bool OOM_printAllocationCount;
#endif
extern bool useFdlibmForSinCosTan;
extern UniqueChars processWideModuleLoadPath;
// Alias the global dstName to namespaceObj.srcName. For example, if dstName is

Просмотреть файл

@ -1427,10 +1427,12 @@ bool wasm::NeedsBuiltinThunk(SymbolicAddress sym) {
// Each JS builtin can have several overloads. These must all be enumerated in
// PopulateTypedNatives() so they can be included in the process-wide thunk set.
#define FOR_EACH_SIN_COS_TAN_NATIVE(_) \
_(math_sin, MathSin) \
_(math_tan, MathTan) \
_(math_cos, MathCos)
#define FOR_EACH_UNARY_NATIVE(_) \
_(math_sin, MathSin) \
_(math_tan, MathTan) \
_(math_cos, MathCos) \
_(math_exp, MathExp) \
_(math_log, MathLog) \
_(math_asin, MathASin) \
@ -1455,6 +1457,14 @@ bool wasm::NeedsBuiltinThunk(SymbolicAddress sym) {
_(ecmaHypot, MathHypot) \
_(ecmaPow, MathPow)
#define DEFINE_SIN_COS_TAN_FLOAT_WRAPPER(func, _) \
static float func##_impl_f32(float x) { \
if (math_use_fdlibm_for_sin_cos_tan()) { \
return float(func##_fdlibm_impl(double(x))); \
} \
return float(func##_native_impl(double(x))); \
}
#define DEFINE_UNARY_FLOAT_WRAPPER(func, _) \
static float func##_impl_f32(float x) { \
return float(func##_impl(double(x))); \
@ -1465,6 +1475,7 @@ bool wasm::NeedsBuiltinThunk(SymbolicAddress sym) {
return float(func(double(x), double(y))); \
}
FOR_EACH_SIN_COS_TAN_NATIVE(DEFINE_SIN_COS_TAN_FLOAT_WRAPPER)
FOR_EACH_UNARY_NATIVE(DEFINE_UNARY_FLOAT_WRAPPER)
FOR_EACH_BINARY_NATIVE(DEFINE_BINARY_FLOAT_WRAPPER)
@ -1496,6 +1507,14 @@ static bool PopulateTypedNatives(TypedNativeToFuncPtrMap* typedNatives) {
FuncCast(funcName, abiType))) \
return false;
#define ADD_SIN_COS_TAN_OVERLOADS(funcName, native) \
if (math_use_fdlibm_for_sin_cos_tan()) { \
ADD_OVERLOAD(funcName##_fdlibm_impl, native, Args_Double_Double) \
} else { \
ADD_OVERLOAD(funcName##_native_impl, native, Args_Double_Double) \
} \
ADD_OVERLOAD(funcName##_impl_f32, native, Args_Float32_Float32)
#define ADD_UNARY_OVERLOADS(funcName, native) \
ADD_OVERLOAD(funcName##_impl, native, Args_Double_Double) \
ADD_OVERLOAD(funcName##_impl_f32, native, Args_Float32_Float32)
@ -1504,6 +1523,7 @@ static bool PopulateTypedNatives(TypedNativeToFuncPtrMap* typedNatives) {
ADD_OVERLOAD(funcName, native, Args_Double_DoubleDouble) \
ADD_OVERLOAD(funcName##_f32, native, Args_Float32_Float32Float32)
FOR_EACH_SIN_COS_TAN_NATIVE(ADD_SIN_COS_TAN_OVERLOADS)
FOR_EACH_UNARY_NATIVE(ADD_UNARY_OVERLOADS)
FOR_EACH_BINARY_NATIVE(ADD_BINARY_OVERLOADS)

Просмотреть файл

@ -1048,6 +1048,10 @@ static void ReloadPrefsCallback(const char* pref, void* aXpccx) {
.setErgnomicBrandChecks(ergnomicBrandChecksEnabled)
.setTopLevelAwait(topLevelAwaitEnabled);
JS::SetUseFdlibmForSinCosTan(
Preferences::GetBool(JS_OPTIONS_DOT_STR "use_fdlibm_for_sin_cos_tan") ||
Preferences::GetBool("privacy.resistFingerprinting"));
nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1");
if (xr) {
bool safeMode = false;

Просмотреть файл

@ -6150,6 +6150,14 @@
mirror: always
do_not_use_directly: true
# Whether to use fdlibm for Math.sin, Math.cos, and Math.tan. When
# privacy.resistFingerprinting is true, this pref is ignored and fdlibm is used
# anyway.
- name: javascript.options.use_fdlibm_for_sin_cos_tan
type: bool
value: false
mirror: always
#---------------------------------------------------------------------------
# Prefs starting with "layers."
#---------------------------------------------------------------------------

Просмотреть файл

@ -33,6 +33,7 @@
#include "mozilla/RefPtr.h"
#include "mozilla/Services.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPrefs_javascript.h"
#include "mozilla/StaticPrefs_privacy.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/TextEvents.h"
@ -716,6 +717,12 @@ void nsRFPService::UpdateRFPPref() {
bool privacyResistFingerprinting =
StaticPrefs::privacy_resistFingerprinting();
// set fdlibm pref
JS::SetUseFdlibmForSinCosTan(
StaticPrefs::javascript_options_use_fdlibm_for_sin_cos_tan() ||
privacyResistFingerprinting);
if (privacyResistFingerprinting) {
PR_SetEnv("TZ=UTC");
} else if (sInitialized) {