mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 700822: Randomize JIT VirtualAlloc location on win32, sans XP. (r=dmandelin)
--HG-- extra : rebase_source : 155b9cf9bb9fc6e70efd5ca4a2b20e76f3526fc5
This commit is contained in:
parent
1680f7db68
commit
355fa9c5a4
@ -996,11 +996,6 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
|
||||
else
|
||||
newDefaultJSOptions &= ~JSOPTION_METHODJIT_ALWAYS;
|
||||
|
||||
if (useHardening)
|
||||
newDefaultJSOptions &= ~JSOPTION_SOFTEN;
|
||||
else
|
||||
newDefaultJSOptions |= JSOPTION_SOFTEN;
|
||||
|
||||
if (useTypeInference)
|
||||
newDefaultJSOptions |= JSOPTION_TYPE_INFERENCE;
|
||||
else
|
||||
@ -1035,6 +1030,9 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
|
||||
// Save the new defaults for the next page load (InitContext).
|
||||
context->mDefaultJSOptions = newDefaultJSOptions;
|
||||
|
||||
JSRuntime *rt = JS_GetRuntime(context->mContext);
|
||||
JS_SetJitHardening(rt, useHardening);
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
PRInt32 zeal = Preferences::GetInt(js_zeal_option_str, -1);
|
||||
PRInt32 frequency = Preferences::GetInt(js_zeal_frequency_str, JS_DEFAULT_ZEAL_FREQ);
|
||||
|
@ -243,11 +243,6 @@ PrefCallback(const char* aPrefName, void* aClosure)
|
||||
newOptions |= JSOPTION_TYPE_INFERENCE;
|
||||
}
|
||||
|
||||
// This one is special, it's enabled by default and only needs to be unset.
|
||||
if (!Preferences::GetBool(gPrefsToWatch[PREF_jit_hardening])) {
|
||||
newOptions |= JSOPTION_SOFTEN;
|
||||
}
|
||||
|
||||
RuntimeService::SetDefaultJSContextOptions(newOptions);
|
||||
rts->UpdateAllWorkerJSContextOptions();
|
||||
}
|
||||
|
@ -27,6 +27,8 @@
|
||||
|
||||
#if ENABLE_ASSEMBLER
|
||||
|
||||
#include "prmjtime.h"
|
||||
|
||||
namespace JSC {
|
||||
|
||||
size_t ExecutableAllocator::pageSize = 0;
|
||||
|
@ -165,13 +165,23 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
enum AllocationBehavior
|
||||
{
|
||||
AllocationCanRandomize,
|
||||
AllocationDeterministic
|
||||
};
|
||||
|
||||
class ExecutableAllocator {
|
||||
typedef void (*DestroyCallback)(void* addr, size_t size);
|
||||
enum ProtectionSetting { Writable, Executable };
|
||||
DestroyCallback destroyCallback;
|
||||
|
||||
void initSeed();
|
||||
|
||||
public:
|
||||
ExecutableAllocator() : destroyCallback(NULL)
|
||||
explicit ExecutableAllocator(AllocationBehavior allocBehavior)
|
||||
: destroyCallback(NULL),
|
||||
allocBehavior(allocBehavior)
|
||||
{
|
||||
if (!pageSize) {
|
||||
pageSize = determinePageSize();
|
||||
@ -186,6 +196,10 @@ public:
|
||||
largeAllocSize = pageSize * 16;
|
||||
}
|
||||
|
||||
#if WTF_OS_WINDOWS
|
||||
initSeed();
|
||||
#endif
|
||||
|
||||
JS_ASSERT(m_smallPools.empty());
|
||||
}
|
||||
|
||||
@ -236,9 +250,16 @@ public:
|
||||
this->destroyCallback = destroyCallback;
|
||||
}
|
||||
|
||||
void setRandomize(bool enabled) {
|
||||
allocBehavior = enabled ? AllocationCanRandomize : AllocationDeterministic;
|
||||
}
|
||||
|
||||
private:
|
||||
static size_t pageSize;
|
||||
static size_t largeAllocSize;
|
||||
#if WTF_OS_WINDOWS
|
||||
static int64_t rngSeed;
|
||||
#endif
|
||||
|
||||
static const size_t OVERSIZE_ALLOCATION = size_t(-1);
|
||||
|
||||
@ -261,8 +282,9 @@ private:
|
||||
}
|
||||
|
||||
// On OOM, this will return an Allocation where pages is NULL.
|
||||
static ExecutablePool::Allocation systemAlloc(size_t n);
|
||||
ExecutablePool::Allocation systemAlloc(size_t n);
|
||||
static void systemRelease(const ExecutablePool::Allocation& alloc);
|
||||
void *computeRandomAllocationAddress();
|
||||
|
||||
ExecutablePool* createPool(size_t n)
|
||||
{
|
||||
@ -466,6 +488,7 @@ private:
|
||||
typedef js::HashSet<ExecutablePool *, js::DefaultHasher<ExecutablePool *>, js::SystemAllocPolicy>
|
||||
ExecPoolHashSet;
|
||||
ExecPoolHashSet m_pools; // All pools, just for stats purposes.
|
||||
AllocationBehavior allocBehavior;
|
||||
|
||||
static size_t determinePageSize();
|
||||
};
|
||||
|
@ -20,18 +20,28 @@
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "ExecutableAllocator.h"
|
||||
|
||||
#if ENABLE_ASSEMBLER && WTF_OS_WINDOWS
|
||||
|
||||
#include "jswin.h"
|
||||
#include "prmjtime.h"
|
||||
|
||||
extern void random_setSeed(int64_t *, int64_t);
|
||||
extern uint64_t random_next(int64_t *, int);
|
||||
|
||||
namespace JSC {
|
||||
|
||||
int64_t ExecutableAllocator::rngSeed;
|
||||
|
||||
void ExecutableAllocator::initSeed()
|
||||
{
|
||||
random_setSeed(&rngSeed, (PRMJ_Now() / 1000) ^ int64_t(this));
|
||||
}
|
||||
|
||||
size_t ExecutableAllocator::determinePageSize()
|
||||
{
|
||||
SYSTEM_INFO system_info;
|
||||
@ -39,16 +49,72 @@ size_t ExecutableAllocator::determinePageSize()
|
||||
return system_info.dwPageSize;
|
||||
}
|
||||
|
||||
void *ExecutableAllocator::computeRandomAllocationAddress()
|
||||
{
|
||||
/*
|
||||
* Inspiration is V8's OS::Allocate in platform-win32.cc.
|
||||
*
|
||||
* VirtualAlloc takes 64K chunks out of the virtual address space, so we
|
||||
* keep 16b alignment.
|
||||
*
|
||||
* x86: V8 comments say that keeping addresses in the [64MiB, 1GiB) range
|
||||
* tries to avoid system default DLL mapping space. In the end, we get 13
|
||||
* bits of randomness in our selection.
|
||||
* x64: [2GiB, 4TiB), with 25 bits of randomness.
|
||||
*/
|
||||
static const uintN chunkBits = 16;
|
||||
#if WTF_CPU_X86_64
|
||||
static const uintptr_t base = 0x0000000080000000;
|
||||
static const uintptr_t mask = 0x000003ffffff0000;
|
||||
#elif WTF_CPU_X86
|
||||
static const uintptr_t base = 0x04000000;
|
||||
static const uintptr_t mask = 0x3fff0000;
|
||||
#else
|
||||
# error "Unsupported architecture"
|
||||
#endif
|
||||
uint64_t rand = random_next(&rngSeed, 32) << chunkBits;
|
||||
return (void *) (base | rand & mask);
|
||||
}
|
||||
|
||||
static bool
|
||||
RandomizeIsBrokenImpl()
|
||||
{
|
||||
OSVERSIONINFO osvi;
|
||||
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
|
||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
||||
|
||||
GetVersionEx(&osvi);
|
||||
|
||||
// Version number mapping is available at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724832%28v=vs.85%29.aspx
|
||||
// We disable everything before Vista, for now.
|
||||
return osvi.dwMajorVersion <= 5;
|
||||
}
|
||||
|
||||
static bool
|
||||
RandomizeIsBroken()
|
||||
{
|
||||
static int result = RandomizeIsBrokenImpl();
|
||||
return !!result;
|
||||
}
|
||||
|
||||
ExecutablePool::Allocation ExecutableAllocator::systemAlloc(size_t n)
|
||||
{
|
||||
void *allocation = VirtualAlloc(0, n, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
||||
void *allocation = NULL;
|
||||
if (allocBehavior == AllocationCanRandomize && !RandomizeIsBroken()) {
|
||||
void *randomAddress = computeRandomAllocationAddress();
|
||||
allocation = VirtualAlloc(randomAddress, n, MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_EXECUTE_READWRITE);
|
||||
}
|
||||
if (!allocation)
|
||||
allocation = VirtualAlloc(0, n, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
||||
ExecutablePool::Allocation alloc = { reinterpret_cast<char*>(allocation), n };
|
||||
return alloc;
|
||||
}
|
||||
|
||||
void ExecutableAllocator::systemRelease(const ExecutablePool::Allocation& alloc)
|
||||
{
|
||||
VirtualFree(alloc.pages, 0, MEM_RELEASE);
|
||||
{
|
||||
VirtualFree(alloc.pages, 0, MEM_RELEASE);
|
||||
}
|
||||
|
||||
#if ENABLE_ASSEMBLER_WX_EXCLUSIVE
|
||||
|
@ -1,6 +1,45 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sw=4 et tw=99 ft=c:
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla SpiderMonkey JavaScript code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "jsapi.h"
|
||||
|
||||
// See testValueABI.cpp
|
||||
/* See testValueABI.cpp */
|
||||
|
||||
JSBool
|
||||
C_ValueToObject(JSContext *cx, jsval v, JSObject **obj)
|
||||
|
@ -1328,6 +1328,12 @@ JS_ToggleOptions(JSContext *cx, uint32_t options)
|
||||
return SetOptionsCommon(cx, newopts);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_SetJitHardening(JSRuntime *rt, JSBool enabled)
|
||||
{
|
||||
rt->setJitHardening(!!enabled);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(const char *)
|
||||
JS_GetImplementationVersion(void)
|
||||
{
|
||||
|
@ -2571,12 +2571,11 @@ JS_StringToVersion(const char *string);
|
||||
#define JSOPTION_PCCOUNT JS_BIT(17) /* Collect per-op execution counts */
|
||||
|
||||
#define JSOPTION_TYPE_INFERENCE JS_BIT(18) /* Perform type inference. */
|
||||
#define JSOPTION_SOFTEN JS_BIT(19) /* Disable JIT hardening. */
|
||||
|
||||
/* Options which reflect compile-time properties of scripts. */
|
||||
#define JSCOMPILEOPTION_MASK (JSOPTION_XML)
|
||||
|
||||
#define JSRUNOPTION_MASK (JS_BITMASK(20) & ~JSCOMPILEOPTION_MASK)
|
||||
#define JSRUNOPTION_MASK (JS_BITMASK(19) & ~JSCOMPILEOPTION_MASK)
|
||||
#define JSALLOPTION_MASK (JSCOMPILEOPTION_MASK | JSRUNOPTION_MASK)
|
||||
|
||||
extern JS_PUBLIC_API(uint32_t)
|
||||
@ -2588,6 +2587,9 @@ JS_SetOptions(JSContext *cx, uint32_t options);
|
||||
extern JS_PUBLIC_API(uint32_t)
|
||||
JS_ToggleOptions(JSContext *cx, uint32_t options);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_SetJitHardening(JSRuntime *rt, JSBool enabled);
|
||||
|
||||
extern JS_PUBLIC_API(const char *)
|
||||
JS_GetImplementationVersion(void);
|
||||
|
||||
|
@ -79,6 +79,7 @@
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
# include "assembler/assembler/MacroAssembler.h"
|
||||
# include "methodjit/MethodJIT.h"
|
||||
#endif
|
||||
#include "frontend/TokenStream.h"
|
||||
#include "frontend/ParseMaps.h"
|
||||
@ -124,13 +125,23 @@ JSRuntime::triggerOperationCallback()
|
||||
JS_ATOMIC_SET(&interrupt, 1);
|
||||
}
|
||||
|
||||
void
|
||||
JSRuntime::setJitHardening(bool enabled)
|
||||
{
|
||||
jitHardening = enabled;
|
||||
if (execAlloc_)
|
||||
execAlloc_->setRandomize(enabled);
|
||||
}
|
||||
|
||||
JSC::ExecutableAllocator *
|
||||
JSRuntime::createExecutableAllocator(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(!execAlloc_);
|
||||
JS_ASSERT(cx->runtime == this);
|
||||
|
||||
execAlloc_ = new_<JSC::ExecutableAllocator>();
|
||||
JSC::AllocationBehavior randomize =
|
||||
jitHardening ? JSC::AllocationCanRandomize : JSC::AllocationDeterministic;
|
||||
execAlloc_ = new_<JSC::ExecutableAllocator>(randomize);
|
||||
if (!execAlloc_)
|
||||
js_ReportOutOfMemory(cx);
|
||||
return execAlloc_;
|
||||
@ -1241,6 +1252,9 @@ void
|
||||
JSContext::updateJITEnabled()
|
||||
{
|
||||
#ifdef JS_METHODJIT
|
||||
// This allocator randomization is actually a compartment-wide option.
|
||||
if (compartment && compartment->hasJaegerCompartment())
|
||||
compartment->jaegerCompartment()->execAlloc()->setRandomize(runtime->getJitHardening());
|
||||
methodJitEnabled = (runOptions & JSOPTION_METHODJIT) && !IsJITBrokenHere();
|
||||
#endif
|
||||
}
|
||||
|
@ -531,6 +531,8 @@ struct JSRuntime : js::RuntimeFriendFields
|
||||
*/
|
||||
int32_t inOOMReport;
|
||||
|
||||
bool jitHardening;
|
||||
|
||||
JSRuntime();
|
||||
~JSRuntime();
|
||||
|
||||
@ -624,6 +626,11 @@ struct JSRuntime : js::RuntimeFriendFields
|
||||
|
||||
JS_FRIEND_API(void) triggerOperationCallback();
|
||||
|
||||
void setJitHardening(bool enabled);
|
||||
bool getJitHardening() const {
|
||||
return jitHardening;
|
||||
}
|
||||
|
||||
void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
|
||||
size_t *regexpCode, size_t *stackCommitted);
|
||||
|
||||
@ -915,7 +922,6 @@ struct JSContext : js::ContextFriendFields
|
||||
bool hasStrictOption() const { return hasRunOption(JSOPTION_STRICT); }
|
||||
bool hasWErrorOption() const { return hasRunOption(JSOPTION_WERROR); }
|
||||
bool hasAtLineOption() const { return hasRunOption(JSOPTION_ATLINE); }
|
||||
bool hasJITHardeningOption() const { return !hasRunOption(JSOPTION_SOFTEN); }
|
||||
|
||||
js::LifoAlloc &tempLifoAlloc() { return runtime->tempLifoAlloc; }
|
||||
inline js::LifoAlloc &typeLifoAlloc();
|
||||
|
@ -141,7 +141,7 @@ JSCompartment::ensureJaegerCompartmentExists(JSContext *cx)
|
||||
mjit::JaegerCompartment *jc = cx->new_<mjit::JaegerCompartment>();
|
||||
if (!jc)
|
||||
return false;
|
||||
if (!jc->Initialize()) {
|
||||
if (!jc->Initialize(cx)) {
|
||||
cx->delete_(jc);
|
||||
return false;
|
||||
}
|
||||
|
@ -516,10 +516,10 @@ static const jsdouble RNG_DSCALE = jsdouble(1LL << 53);
|
||||
/*
|
||||
* Math.random() support, lifted from java.util.Random.java.
|
||||
*/
|
||||
static inline void
|
||||
random_setSeed(JSContext *cx, int64_t seed)
|
||||
extern void
|
||||
random_setSeed(int64_t *rngSeed, int64_t seed)
|
||||
{
|
||||
cx->rngSeed = (seed ^ RNG_MULTIPLIER) & RNG_MASK;
|
||||
*rngSeed = (seed ^ RNG_MULTIPLIER) & RNG_MASK;
|
||||
}
|
||||
|
||||
void
|
||||
@ -531,26 +531,24 @@ js_InitRandom(JSContext *cx)
|
||||
* the context and its successor. We don't just use the context because it might be
|
||||
* possible to reverse engineer the context pointer if one guesses the time right.
|
||||
*/
|
||||
random_setSeed(cx,
|
||||
(PRMJ_Now() / 1000) ^
|
||||
int64_t(cx) ^
|
||||
int64_t(cx->link.next));
|
||||
random_setSeed(&cx->rngSeed, (PRMJ_Now() / 1000) ^ int64_t(cx) ^ int64_t(cx->link.next));
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
random_next(JSContext *cx, int bits)
|
||||
extern uint64_t
|
||||
random_next(int64_t *rngSeed, int bits)
|
||||
{
|
||||
uint64_t nextseed = cx->rngSeed * RNG_MULTIPLIER;
|
||||
uint64_t nextseed = *rngSeed * RNG_MULTIPLIER;
|
||||
nextseed += RNG_ADDEND;
|
||||
nextseed &= RNG_MASK;
|
||||
cx->rngSeed = nextseed;
|
||||
*rngSeed = nextseed;
|
||||
return nextseed >> (48 - bits);
|
||||
}
|
||||
|
||||
static inline jsdouble
|
||||
random_nextDouble(JSContext *cx)
|
||||
{
|
||||
return jsdouble((random_next(cx, 26) << 27) + random_next(cx, 27)) / RNG_DSCALE;
|
||||
return jsdouble((random_next(&cx->rngSeed, 26) << 27) + random_next(&cx->rngSeed, 27)) /
|
||||
RNG_DSCALE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
|
@ -989,9 +989,10 @@ JaegerCompartment::JaegerCompartment()
|
||||
{}
|
||||
|
||||
bool
|
||||
JaegerCompartment::Initialize()
|
||||
JaegerCompartment::Initialize(JSContext *cx)
|
||||
{
|
||||
execAlloc_ = js::OffTheBooks::new_<JSC::ExecutableAllocator>();
|
||||
execAlloc_ = js::OffTheBooks::new_<JSC::ExecutableAllocator>(
|
||||
cx->runtime->getJitHardening() ? JSC::AllocationCanRandomize : JSC::AllocationDeterministic);
|
||||
if (!execAlloc_)
|
||||
return false;
|
||||
|
||||
|
@ -473,7 +473,7 @@ class JaegerCompartment {
|
||||
void Finish();
|
||||
|
||||
public:
|
||||
bool Initialize();
|
||||
bool Initialize(JSContext *cx);
|
||||
|
||||
JaegerCompartment();
|
||||
~JaegerCompartment() { Finish(); }
|
||||
|
Loading…
Reference in New Issue
Block a user