Security fixes toward Firefox 58 level but might miss some. Check later.

Security fixes toward Firefox 58 level but might miss some. Check later.
This commit is contained in:
win7-7 2022-10-12 16:16:29 +03:00 committed by wuggy
commit 98e863440f
20 changed files with 222 additions and 74 deletions

View file

@ -132,6 +132,12 @@ function loadCallgraph(file)
addGCFunction(func, "annotation");
}
// mess up the id <-> name correspondence. Also, we need to know if the
// functions even exist in the first place.)
for (var func of extraGCFunctions()) {
addGCFunction(func, "annotation");
}
// Initialize suppressedFunctions to the set of all functions, and the
// worklist to all toplevel callers.
var worklist = [];

View file

@ -1020,7 +1020,7 @@ class MOZ_STACK_CLASS TokenStream
return true;
}
void skipChars(uint8_t n) {
void skipChars(uint32_t n) {
while (n-- > 0) {
MOZ_ASSERT(userbuf.hasRawChars());
mozilla::DebugOnly<int32_t> c = getCharIgnoreEOL();

View file

@ -35,6 +35,7 @@
#include "jit/MacroAssembler-inl.h"
#include "vm/Interpreter-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/TypeInference-inl.h"
using namespace js;
using namespace js::jit;
@ -97,7 +98,8 @@ BaselineCompiler::compile()
AutoTraceLog logScript(logger, scriptEvent);
AutoTraceLog logCompile(logger, TraceLogger_BaselineCompilation);
if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx))
AutoKeepTypeScripts keepTypes(cx);
if (!script->ensureHasTypes(cx, keepTypes) || !script->ensureHasAnalyzedArgsUsage(cx))
return Method_Error;
// When code coverage is only enabled for optimizations, or when a Debugger

View file

@ -16,6 +16,7 @@
#include "jit/JitFrames-inl.h"
#include "jit/MacroAssembler-inl.h"
#include "vm/Stack-inl.h"
#include "vm/TypeInference-inl.h"
using namespace js;
using namespace js::jit;
@ -670,6 +671,7 @@ RecompileBaselineScriptForDebugMode(JSContext* cx, JSScript* script,
JitSpew(JitSpew_BaselineDebugModeOSR, "Recompiling (%s:%" PRIuSIZE ") for %s",
script->filename(), script->lineno(), observing ? "DEBUGGING" : "NORMAL EXECUTION");
AutoKeepTypeScripts keepTypes(cx);
script->setBaselineScript(cx->runtime(), nullptr);
MethodStatus status = BaselineCompile(cx, script, /* forceDebugMode = */ observing);

View file

@ -4180,7 +4180,8 @@ jit::AnalyzeArgumentsUsage(JSContext* cx, JSScript* scriptArg)
if (script->length() > MAX_SCRIPT_SIZE)
return true;
if (!script->ensureHasTypes(cx))
AutoKeepTypeScripts keepTypes(cx);
if (!script->ensureHasTypes(cx, keepTypes))
return false;
TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());

View file

@ -1226,9 +1226,9 @@ IonCacheIRCompiler::emitCallStringSplitResult()
static bool
GroupHasPropertyTypes(ObjectGroup* group, jsid* id, Value* v)
{
if (group->unknownProperties())
if (group->unknownPropertiesDontCheckGeneration())
return true;
HeapTypeSet* propTypes = group->maybeGetProperty(*id);
HeapTypeSet* propTypes = group->maybeGetPropertyDontCheckGeneration(*id);
if (!propTypes)
return true;
if (!propTypes->nonConstantProperty())

View file

@ -544,7 +544,8 @@ CreateThis(JSContext* cx, HandleObject callee, HandleObject newTarget, MutableHa
RootedFunction fun(cx, &callee->as<JSFunction>());
if (fun->isInterpreted() && fun->isConstructor()) {
JSScript* script = JSFunction::getOrCreateScript(cx, fun);
if (!script || !script->ensureHasTypes(cx))
AutoKeepTypeScripts keepTypes(cx);
if (!script || !script->ensureHasTypes(cx, keepTypes))
return false;
if (fun->isBoundFunction() || script->isDerivedClassConstructor()) {
rval.set(MagicValue(JS_UNINITIALIZED_LEXICAL));

View file

@ -863,7 +863,7 @@ template <class Iter>
static void
TraceOneDataRelocation(JSTracer* trc, Iter* iter)
{
Instruction* ins = iter->cur();
Iter iterCopy(*iter);
Register dest;
Assembler::RelocStyle rs;
const void* prior = Assembler::GetPtr32Target(iter, &dest, &rs);
@ -874,12 +874,12 @@ TraceOneDataRelocation(JSTracer* trc, Iter* iter)
"ion-masm-ptr");
if (ptr != prior) {
MacroAssemblerARM::ma_mov_patch(Imm32(int32_t(ptr)), dest, Assembler::Always, rs, ins);
MacroAssemblerARM::ma_mov_patch(Imm32(int32_t(ptr)), dest, Assembler::Always, rs, iterCopy);
// L_LDR won't cause any instructions to be updated.
if (rs != Assembler::L_LDR) {
AutoFlushICache::flush(uintptr_t(ins), 4);
AutoFlushICache::flush(uintptr_t(ins->next()), 4);
AutoFlushICache::flush(uintptr_t(iter->cur()), 4);
AutoFlushICache::flush(uintptr_t(iter->next()), 4);
}
}
}
@ -899,7 +899,7 @@ TraceDataRelocations(JSTracer* trc, ARMBuffer* buffer, CompactBufferReader& read
{
while (reader.more()) {
BufferOffset offset(reader.readUnsigned());
ARMBuffer::AssemblerBufferInstIterator iter(offset, buffer);
BufferInstructionIterator iter(offset, buffer);
TraceOneDataRelocation(trc, &iter);
}
}
@ -3161,14 +3161,15 @@ Assembler::PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newVal
PatchedImmPtr expectedValue)
{
Instruction* ptr = reinterpret_cast<Instruction*>(label.raw());
InstructionIterator iter(ptr);
Register dest;
Assembler::RelocStyle rs;
InstructionIterator iter(ptr);
DebugOnly<const uint32_t*> val = GetPtr32Target(&iter, &dest, &rs);
MOZ_ASSERT(uint32_t((const uint32_t*)val) == uint32_t(expectedValue.value));
MacroAssembler::ma_mov_patch(Imm32(int32_t(newValue.value)), dest, Always, rs, ptr);
iter = InstructionIterator(ptr);
MacroAssembler::ma_mov_patch(Imm32(int32_t(newValue.value)), dest, Always, rs, iter);
// L_LDR won't cause any instructions to be updated.
if (rs != L_LDR) {
@ -3221,6 +3222,20 @@ InstIsGuard(Instruction* inst, const PoolHeader** ph)
return *ph != nullptr;
}
static bool
InstIsGuard(BufferInstructionIterator& iter, const PoolHeader** ph)
{
Instruction* inst = iter.cur();
Assembler::Condition c = inst->extractCond();
if (c != Assembler::Always)
return false;
if (!(inst->is<InstBXReg>() || inst->is<InstBImm>()))
return false;
// See if the next instruction is a pool header.
*ph = iter.peek()->as<const PoolHeader>();
return *ph != nullptr;
}
static bool
InstIsBNop(Instruction* inst)
{
@ -3266,6 +3281,28 @@ Instruction::skipPool()
return this;
}
void
BufferInstructionIterator::skipPool()
{
// If this is a guard, and the next instruction is a header, always work
// around the pool. If it isn't a guard, then start looking ahead.
const PoolHeader* ph;
if (InstIsGuard(*this, &ph)) {
// Don't skip a natural guard.
if (ph->isNatural())
return;
advance(sizeof(Instruction) * (1 + ph->size()));
skipPool();
return;
}
if (InstIsBNop(cur())) {
next();
skipPool();
}
}
// Cases to be handled:
// 1) no pools or branches in sight => return this+1
// 2) branch to next instruction => return this+2, because a nop needed to be inserted into the stream.
@ -3409,13 +3446,6 @@ Assembler::BailoutTableStart(uint8_t* code)
return (uint8_t*) inst;
}
InstructionIterator::InstructionIterator(Instruction* i_)
: i(i_)
{
// Work around pools with an artificial pool guard and around nop-fill.
i = i->skipPool();
}
uint32_t Assembler::NopFill = 0;
uint32_t

View file

@ -2246,20 +2246,35 @@ class InstMOV : public InstALU
class InstructionIterator
{
private:
Instruction* i;
Instruction* inst_;
public:
explicit InstructionIterator(Instruction* i_);
explicit InstructionIterator(Instruction* inst) : inst_(inst) {
skipPool();
}
void skipPool() {
inst_ = inst_->skipPool();
}
Instruction* next() {
i = i->next();
inst_ = inst_->next();
return cur();
}
Instruction* cur() const {
return i;
return inst_;
}
};
class BufferInstructionIterator : public ARMBuffer::AssemblerBufferInstIterator
{
public:
BufferInstructionIterator(BufferOffset bo, ARMBuffer* buffer)
: ARMBuffer::AssemblerBufferInstIterator(bo, buffer)
{}
void skipPool();
};
static const uint32_t NumIntArgRegs = 4;
// There are 16 *float* registers available for arguments

View file

@ -308,6 +308,25 @@ MacroAssembler::add64(Imm64 imm, Register64 dest)
ma_adc(imm.hi(), dest.high, scratch, LeaveCC);
}
CodeOffset
MacroAssembler::add32ToPtrWithPatch(Register src, Register dest)
{
ScratchRegisterScope scratch(*this);
CodeOffset offs = CodeOffset(currentOffset());
ma_movPatchable(Imm32(0), scratch, Always);
ma_add(src, scratch, dest);
return offs;
}
void
MacroAssembler::patchAdd32ToPtr(CodeOffset offset, Imm32 imm)
{
ScratchRegisterScope scratch(*this);
BufferInstructionIterator iter(BufferOffset(offset.offset()), &m_buffer);
iter.maybeSkipAutomaticInstructions();
ma_mov_patch(imm, scratch, Always, HasMOVWT() ? L_MOVWT : L_LDR, iter);
}
void
MacroAssembler::addDouble(FloatRegister src, FloatRegister dest)
{

View file

@ -345,35 +345,36 @@ MacroAssemblerARM::ma_movPatchable(ImmPtr imm, Register dest, Assembler::Conditi
ma_movPatchable(Imm32(int32_t(imm.value)), dest, c);
}
/* static */ void
MacroAssemblerARM::ma_mov_patch(Imm32 imm_, Register dest, Assembler::Condition c,
RelocStyle rs, Instruction* i)
/* static */
template<class Iter>
void
MacroAssemblerARM::ma_mov_patch(Imm32 imm32, Register dest, Assembler::Condition c,
RelocStyle rs, Iter iter)
{
MOZ_ASSERT(i);
int32_t imm = imm_.value;
MOZ_ASSERT(iter.cur());
// Make sure the current instruction is not an artificial guard inserted
// by the assembler buffer.
i = i->skipPool();
iter.skipPool();
int32_t imm = imm32.value;
switch(rs) {
case L_MOVWT:
Assembler::as_movw_patch(dest, Imm16(imm & 0xffff), c, i);
i = i->next();
Assembler::as_movt_patch(dest, Imm16(imm >> 16 & 0xffff), c, i);
Assembler::as_movw_patch(dest, Imm16(imm & 0xffff), c, iter.cur());
Assembler::as_movt_patch(dest, Imm16(imm >> 16 & 0xffff), c, iter.next());
break;
case L_LDR:
Assembler::WritePoolEntry(i, c, imm);
Assembler::WritePoolEntry(iter.cur(), c, imm);
break;
}
}
/* static */ void
MacroAssemblerARM::ma_mov_patch(ImmPtr imm, Register dest, Assembler::Condition c,
RelocStyle rs, Instruction* i)
{
ma_mov_patch(Imm32(int32_t(imm.value)), dest, c, rs, i);
}
template void
MacroAssemblerARM::ma_mov_patch(Imm32 imm32, Register dest, Assembler::Condition c,
RelocStyle rs, InstructionIterator iter);
template void
MacroAssemblerARM::ma_mov_patch(Imm32 imm32, Register dest, Assembler::Condition c,
RelocStyle rs, BufferInstructionIterator iter);
void
MacroAssemblerARM::ma_mov(Register src, Register dest, SBit s, Assembler::Condition c)

View file

@ -124,10 +124,10 @@ class MacroAssemblerARM : public Assembler
void ma_movPatchable(Imm32 imm, Register dest, Assembler::Condition c);
void ma_movPatchable(ImmPtr imm, Register dest, Assembler::Condition c);
// To be used with Iter := InstructionIterator or BufferInstructionIterator.
template<class Iter>
static void ma_mov_patch(Imm32 imm, Register dest, Assembler::Condition c,
RelocStyle rs, Instruction* i);
static void ma_mov_patch(ImmPtr imm, Register dest, Assembler::Condition c,
RelocStyle rs, Instruction* i);
RelocStyle rs, Iter iter);
// ALU based ops
// mov

View file

@ -141,7 +141,6 @@ class AssemblerBuffer
{
protected:
typedef BufferSlice<SliceSize> Slice;
typedef AssemblerBuffer<SliceSize, Inst> AssemblerBuffer_;
// Doubly-linked list of BufferSlices, with the most recent in tail position.
Slice* head;
@ -388,24 +387,32 @@ class AssemblerBuffer
return BufferOffset(bufferSize);
}
typedef AssemblerBuffer<SliceSize, Inst> ThisClass;
class AssemblerBufferInstIterator
{
BufferOffset bo;
AssemblerBuffer_* m_buffer;
BufferOffset bo_;
ThisClass* buffer_;
public:
explicit AssemblerBufferInstIterator(BufferOffset off, AssemblerBuffer_* buffer)
: bo(off), m_buffer(buffer)
explicit AssemblerBufferInstIterator(BufferOffset bo, ThisClass* buffer)
: bo_(bo), buffer_(buffer)
{ }
void advance(int offset) {
bo_ = BufferOffset(bo_.getOffset() + offset);
}
Inst* next() {
Inst* i = m_buffer->getInst(bo);
bo = BufferOffset(bo.getOffset() + i->size());
advance(cur()->size());
return cur();
}
Inst* cur() {
return m_buffer->getInst(bo);
Inst* peek() {
return buffer_->getInst(BufferOffset(bo_.getOffset() + cur()->size()));
}
Inst* cur() const {
return buffer_->getInst(bo_);
}
};
};

View file

@ -1689,7 +1689,7 @@ class JSScript : public js::gc::TenuredCell
bool isTopLevel() { return code() && !functionNonDelazifying(); }
/* Ensure the script has a TypeScript. */
inline bool ensureHasTypes(JSContext* cx);
inline bool ensureHasTypes(JSContext* cx, js::AutoKeepTypeScripts&);
inline js::TypeScript* types();

View file

@ -28,6 +28,7 @@
#include "jsscriptinlines.h"
#include "vm/Stack-inl.h"
#include "vm/TypeInference-inl.h"
using namespace js;
using namespace js::gc;
@ -1466,7 +1467,8 @@ class DebugEnvironmentProxyHandler : public BaseProxyHandler
CallObject& callobj = env->as<CallObject>();
RootedFunction fun(cx, &callobj.callee());
RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx))
AutoKeepTypeScripts keepTypes(cx);
if (!script->ensureHasTypes(cx, keepTypes) || !script->ensureHasAnalyzedArgsUsage(cx))
return false;
BindingIter bi(script);

View file

@ -383,7 +383,8 @@ class ObjectGroup : public gc::TenuredCell
inline HeapTypeSet* getProperty(ExclusiveContext* cx, JSObject* obj, jsid id);
/* Get a property only if it already exists. */
inline HeapTypeSet* maybeGetProperty(jsid id);
MOZ_ALWAYS_INLINE HeapTypeSet* maybeGetProperty(jsid id);
MOZ_ALWAYS_INLINE HeapTypeSet* maybeGetPropertyDontCheckGeneration(jsid id);
/*
* Iterate through the group's properties. getPropertyCount overapproximates
@ -461,6 +462,7 @@ class ObjectGroup : public gc::TenuredCell
}
inline uint32_t basePropertyCount();
inline uint32_t basePropertyCountDontCheckGeneration();
private:
inline void setBasePropertyCount(uint32_t count);

View file

@ -367,10 +367,10 @@ TypeMonitorCall(JSContext* cx, const js::CallArgs& args, bool constructing)
inline bool
TrackPropertyTypes(ExclusiveContext* cx, JSObject* obj, jsid id)
{
if (obj->hasLazyGroup() || obj->group()->unknownProperties())
if (obj->hasLazyGroup() || obj->group()->unknownPropertiesDontCheckGeneration())
return false;
if (obj->isSingleton() && !obj->group()->maybeGetProperty(id))
if (obj->isSingleton() && !obj->group()->maybeGetPropertyDontCheckGeneration(id))
return false;
return true;
@ -405,7 +405,15 @@ PropertyHasBeenMarkedNonConstant(JSObject* obj, jsid id)
inline bool
HasTypePropertyId(JSObject* obj, jsid id, TypeSet::Type type)
{
if (obj->hasLazyGroup())
MOZ_ASSERT(id == IdToTypeId(id));
MOZ_ASSERT(TrackPropertyTypes(obj, id));
if (HeapTypeSet* types = obj->group()->maybeGetPropertyDontCheckGeneration(id)) {
if (!types->hasType(type))
return false;
// Non-constant properties are only relevant for singleton objects.
if (obj->isSingleton() && !types->nonConstantProperty())
return false;
return true;
if (obj->group()->unknownProperties())
@ -657,6 +665,21 @@ TypeScript::SetArgument(JSContext* cx, JSScript* script, unsigned arg, const js:
SetArgument(cx, script, arg, TypeSet::GetValueType(value));
}
inline
AutoKeepTypeScripts::AutoKeepTypeScripts(JSContext* cx)
: zone_(cx->zone()->types),
prev_(zone_.keepTypeScripts)
{
zone_.keepTypeScripts = true;
}
inline
AutoKeepTypeScripts::~AutoKeepTypeScripts()
{
MOZ_ASSERT(zone_.keepTypeScripts);
zone_.keepTypeScripts = prev_;
}
/////////////////////////////////////////////////////////////////////
// TypeHashSet
/////////////////////////////////////////////////////////////////////
@ -1024,10 +1047,18 @@ TypeSet::getObjectClass(unsigned i) const
// ObjectGroup
/////////////////////////////////////////////////////////////////////
inline uint32_t
ObjectGroup::basePropertyCountDontCheckGeneration()
{
uint32_t flags = flagsDontCheckGeneration();
return (flags & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT;
}
inline uint32_t
ObjectGroup::basePropertyCount()
{
return (flags() & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT;
maybeSweep(nullptr);
return basePropertyCountDontCheckGeneration();
}
inline void
@ -1083,18 +1114,25 @@ ObjectGroup::getProperty(ExclusiveContext* cx, JSObject* obj, jsid id)
}
inline HeapTypeSet*
ObjectGroup::maybeGetProperty(jsid id)
ObjectGroup::maybeGetPropertyDontCheckGeneration(jsid id)
{
MOZ_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id) || JSID_IS_SYMBOL(id));
MOZ_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id));
MOZ_ASSERT(!unknownProperties());
MOZ_ASSERT(!unknownPropertiesDontCheckGeneration());
Property* prop = TypeHashSet::Lookup<jsid, Property, Property>
(propertySet, basePropertyCount(), id);
(propertySet, basePropertyCountDontCheckGeneration(), id);
return prop ? &prop->types : nullptr;
}
inline HeapTypeSet*
ObjectGroup::maybeGetProperty(jsid id)
{
maybeSweep(nullptr);
return maybeGetPropertyDontCheckGeneration(id);
}
inline unsigned
ObjectGroup::getPropertyCount()
{
@ -1125,7 +1163,7 @@ JSScript::types()
}
inline bool
JSScript::ensureHasTypes(JSContext* cx)
JSScript::ensureHasTypes(JSContext* cx, js::AutoKeepTypeScripts&)
{
return types() || makeTypes(cx);
}

View file

@ -4436,6 +4436,7 @@ JSScript::maybeSweepTypes(AutoClearTypeInferenceStateOnOOM* oom)
// only do this if nothing has been compiled for the script, which will be
// the case unless the script has been compiled since we started sweeping.
if (types.sweepReleaseTypes &&
!types.keepTypeScripts &&
!hasBaselineScript() &&
!hasIonScript())
{
@ -4492,20 +4493,24 @@ Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
TypeZone::TypeZone(Zone* zone)
: zone_(zone),
typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
generation(0),
compilerOutputs(nullptr),
sweepTypeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
sweepCompilerOutputs(nullptr),
sweepReleaseTypes(false),
activeAnalysis(nullptr)
typeLifoAlloc(zone->group(), (size_t) TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
generation(zone->group(), 0),
compilerOutputs(zone->group(), nullptr),
sweepTypeLifoAlloc(zone->group(), (size_t) TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
sweepCompilerOutputs(zone->group(), nullptr),
sweepReleaseTypes(zone->group(), false),
sweepingTypes(zone->group(), false),
keepTypeScripts(zone->group(), false),
activeAnalysis(zone->group(), nullptr)
{
}
TypeZone::~TypeZone()
{
js_delete(compilerOutputs);
js_delete(sweepCompilerOutputs);
js_delete(compilerOutputs.ref());
js_delete(sweepCompilerOutputs.ref());
MOZ_RELEASE_ASSERT(!sweepingTypes);
MOZ_ASSERT(!keepTypeScripts);
}
void

View file

@ -1255,6 +1255,19 @@ class TypeScript
#endif
};
// Ensures no TypeScripts are purged in the current zone.
class MOZ_RAII AutoKeepTypeScripts
{
TypeZone& zone_;
bool prev_;
AutoKeepTypeScripts(const AutoKeepTypeScripts&) = delete;
void operator=(const AutoKeepTypeScripts&) = delete;
public:
explicit inline AutoKeepTypeScripts(JSContext* cx);
inline ~AutoKeepTypeScripts();
};
void
FillBytecodeTypeMap(JSScript* script, uint32_t* bytecodeMap);
@ -1325,6 +1338,9 @@ struct TypeZone
static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 8 * 1024;
LifoAlloc typeLifoAlloc;
TypeZone(const TypeZone&) = delete;
void operator=(const TypeZone&) = delete;
// Current generation for sweeping.
uint32_t generation : 1;
@ -1348,6 +1364,7 @@ struct TypeZone
// information attached to scripts.
bool sweepReleaseTypes;
ZoneGroupData<bool> keepTypeScripts;
// The topmost AutoEnterAnalysis on the stack, if there is one.
AutoEnterAnalysis* activeAnalysis;

View file

@ -362,7 +362,7 @@ UnboxedPlainObject::ensureExpando(JSContext* cx, Handle<UnboxedPlainObject*> obj
bool
UnboxedPlainObject::containsUnboxedOrExpandoProperty(ExclusiveContext* cx, jsid id) const
{
if (layout().lookup(id))
if (layoutDontCheckGeneration().lookup(id))
return true;
if (maybeExpando() && maybeExpando()->containsShapeOrElement(cx, id))