[runtime] Zero padding of sequential strings
SeqStrings have up to kTaggedSize-1 bytes of padding, due to allocation sizes being rounded up to kObjectAlignment. This CL ensures that all (non-external) sequential strings on the heap have (and keep) this padding set to all zeroes. The approach is to unconditionally zero the last tagged word of the uninitialized allocation of string objects. Change-Id: I32ee4a53e25fa470f79562a71b8c648c7205523f Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4143019 Commit-Queue: Olivier Flückiger <olivf@chromium.org> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Reviewed-by: Nico Hartmann <nicohartmann@chromium.org> Cr-Commit-Position: refs/heads/main@{#85294}
This commit is contained in:
parent
e02e128455
commit
44df2a5bd5
@ -575,6 +575,7 @@ void ProcessorImpl::ToStringImpl(char* out, int* out_length, Digits X,
|
||||
}
|
||||
int excess = formatter.Finish();
|
||||
*out_length -= excess;
|
||||
memset(out + *out_length, 0, excess);
|
||||
}
|
||||
|
||||
Status Processor::ToString(char* out, int* out_length, Digits X, int radix,
|
||||
|
@ -488,6 +488,8 @@ const kFixedDoubleArrayMaxLength:
|
||||
constexpr int31 generates 'FixedDoubleArray::kMaxLength';
|
||||
const kObjectAlignmentMask: constexpr intptr
|
||||
generates 'kObjectAlignmentMask';
|
||||
const kObjectAlignment: constexpr intptr
|
||||
generates 'kObjectAlignment';
|
||||
const kMinAddedElementsCapacity:
|
||||
constexpr int31 generates 'JSObject::kMinAddedElementsCapacity';
|
||||
const kMaxFastArrayLength:
|
||||
|
@ -267,17 +267,23 @@ extern macro Allocate(
|
||||
const kAllocateBaseFlags: constexpr AllocationFlag =
|
||||
AllocationFlag::kAllowLargeObjectAllocation;
|
||||
macro AllocateFromNew(
|
||||
sizeInBytes: intptr, map: Map, pretenured: bool): UninitializedHeapObject {
|
||||
sizeInBytes: intptr, map: Map, pretenured: bool,
|
||||
clearPadding: bool): UninitializedHeapObject {
|
||||
dcheck(ValidAllocationSize(sizeInBytes, map));
|
||||
let res: UninitializedHeapObject;
|
||||
if (pretenured) {
|
||||
return Allocate(
|
||||
res = Allocate(
|
||||
sizeInBytes,
|
||||
%RawConstexprCast<constexpr AllocationFlag>(
|
||||
%RawConstexprCast<constexpr int32>(kAllocateBaseFlags) |
|
||||
%RawConstexprCast<constexpr int32>(AllocationFlag::kPretenured)));
|
||||
} else {
|
||||
return Allocate(sizeInBytes, kAllocateBaseFlags);
|
||||
res = Allocate(sizeInBytes, kAllocateBaseFlags);
|
||||
}
|
||||
if (clearPadding) {
|
||||
*unsafe::NewReference<Zero>(res, sizeInBytes - kObjectAlignment) = kZero;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
macro InitializeFieldsFromIterator<T: type, Iterator: type>(
|
||||
|
@ -3725,6 +3725,10 @@ TNode<String> CodeStubAssembler::AllocateSeqOneByteString(
|
||||
return EmptyStringConstant();
|
||||
}
|
||||
TNode<HeapObject> result = Allocate(SeqOneByteString::SizeFor(length), flags);
|
||||
StoreNoWriteBarrier(MachineRepresentation::kTaggedSigned, result,
|
||||
IntPtrConstant(SeqOneByteString::SizeFor(length) -
|
||||
kObjectAlignment - kHeapObjectTag),
|
||||
SmiConstant(0));
|
||||
DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kOneByteStringMap));
|
||||
StoreMapNoWriteBarrier(result, RootIndex::kOneByteStringMap);
|
||||
StoreObjectFieldNoWriteBarrier(result, SeqOneByteString::kLengthOffset,
|
||||
@ -3747,6 +3751,10 @@ TNode<String> CodeStubAssembler::AllocateSeqTwoByteString(
|
||||
return EmptyStringConstant();
|
||||
}
|
||||
TNode<HeapObject> result = Allocate(SeqTwoByteString::SizeFor(length), flags);
|
||||
StoreNoWriteBarrier(MachineRepresentation::kTaggedSigned, result,
|
||||
IntPtrConstant(SeqTwoByteString::SizeFor(length) -
|
||||
kObjectAlignment - kHeapObjectTag),
|
||||
SmiConstant(0));
|
||||
DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kStringMap));
|
||||
StoreMapNoWriteBarrier(result, RootIndex::kStringMap);
|
||||
StoreObjectFieldNoWriteBarrier(result, SeqTwoByteString::kLengthOffset,
|
||||
|
@ -4305,6 +4305,11 @@ Node* EffectControlLinearizer::LowerStringFromSingleCharCode(Node* node) {
|
||||
Node* vfalse1 =
|
||||
__ Allocate(AllocationType::kYoung,
|
||||
__ IntPtrConstant(SeqTwoByteString::SizeFor(1)));
|
||||
__ Store(StoreRepresentation(MachineRepresentation::kTaggedSigned,
|
||||
kNoWriteBarrier),
|
||||
vfalse1,
|
||||
SeqTwoByteString::SizeFor(1) - kObjectAlignment - kHeapObjectTag,
|
||||
__ SmiConstant(0));
|
||||
__ StoreField(AccessBuilder::ForMap(), vfalse1,
|
||||
__ HeapConstant(factory()->string_map()));
|
||||
__ StoreField(AccessBuilder::ForNameRawHashField(), vfalse1,
|
||||
@ -4401,6 +4406,11 @@ Node* EffectControlLinearizer::LowerStringFromSingleCodePoint(Node* node) {
|
||||
Node* vfalse1 =
|
||||
__ Allocate(AllocationType::kYoung,
|
||||
__ IntPtrConstant(SeqTwoByteString::SizeFor(1)));
|
||||
__ Store(StoreRepresentation(MachineRepresentation::kTaggedSigned,
|
||||
kNoWriteBarrier),
|
||||
vfalse1,
|
||||
SeqTwoByteString::SizeFor(1) - kObjectAlignment - kHeapObjectTag,
|
||||
__ SmiConstant(0));
|
||||
__ StoreField(AccessBuilder::ForMap(), vfalse1,
|
||||
__ HeapConstant(factory()->string_map()));
|
||||
__ StoreField(AccessBuilder::ForNameRawHashField(), vfalse1,
|
||||
@ -4441,6 +4451,11 @@ Node* EffectControlLinearizer::LowerStringFromSingleCodePoint(Node* node) {
|
||||
Node* vfalse0 =
|
||||
__ Allocate(AllocationType::kYoung,
|
||||
__ IntPtrConstant(SeqTwoByteString::SizeFor(2)));
|
||||
__ Store(StoreRepresentation(MachineRepresentation::kTaggedSigned,
|
||||
kNoWriteBarrier),
|
||||
vfalse0,
|
||||
SeqTwoByteString::SizeFor(2) - kObjectAlignment - kHeapObjectTag,
|
||||
__ SmiConstant(0));
|
||||
__ StoreField(AccessBuilder::ForMap(), vfalse0,
|
||||
__ HeapConstant(factory()->string_map()));
|
||||
__ StoreField(AccessBuilder::ForNameRawHashField(), vfalse0,
|
||||
|
@ -342,9 +342,12 @@ void MaglevAssembler::MaybeEmitDeoptBuiltinsCall(size_t eager_deopt_count,
|
||||
|
||||
void MaglevAssembler::AllocateTwoByteString(RegisterSnapshot register_snapshot,
|
||||
Register result, int length) {
|
||||
Allocate(register_snapshot, result, SeqTwoByteString::SizeFor(length));
|
||||
int size = SeqTwoByteString::SizeFor(length);
|
||||
Allocate(register_snapshot, result, size);
|
||||
UseScratchRegisterScope scope(this);
|
||||
Register scratch = scope.AcquireX();
|
||||
Move(scratch, 0);
|
||||
StoreTaggedField(scratch, FieldMemOperand(result, size - kObjectAlignment));
|
||||
LoadRoot(scratch, RootIndex::kStringMap);
|
||||
StoreTaggedField(scratch, FieldMemOperand(result, HeapObject::kMapOffset));
|
||||
Move(scratch, Name::kEmptyHashField);
|
||||
|
@ -87,8 +87,10 @@ void MaglevAssembler::AllocateHeapNumber(RegisterSnapshot register_snapshot,
|
||||
|
||||
void MaglevAssembler::AllocateTwoByteString(RegisterSnapshot register_snapshot,
|
||||
Register result, int length) {
|
||||
Allocate(register_snapshot, result, SeqTwoByteString::SizeFor(length));
|
||||
int size = SeqTwoByteString::SizeFor(length);
|
||||
Allocate(register_snapshot, result, size);
|
||||
LoadRoot(kScratchRegister, RootIndex::kStringMap);
|
||||
StoreTaggedField(FieldOperand(result, size - kObjectAlignment), Immediate(0));
|
||||
StoreTaggedField(FieldOperand(result, HeapObject::kMapOffset),
|
||||
kScratchRegister);
|
||||
StoreTaggedField(FieldOperand(result, Name::kRawHashFieldOffset),
|
||||
|
@ -969,6 +969,7 @@ MaybeHandle<String> BigInt::ToString(Isolate* isolate, Handle<BigInt> bigint,
|
||||
// out whether all characters were used.
|
||||
chars_written = chars_allocated - static_cast<int>(out - start);
|
||||
std::memmove(start, out, chars_written);
|
||||
memset(start + chars_written, 0, chars_allocated - chars_written);
|
||||
}
|
||||
} else {
|
||||
// Generic path, handles anything.
|
||||
|
@ -1850,6 +1850,18 @@ SeqString::DataAndPaddingSizes SeqTwoByteString::GetDataAndPaddingSizes()
|
||||
return DataAndPaddingSizes{data_size, padding_size};
|
||||
}
|
||||
|
||||
#ifdef VERIFY_HEAP
|
||||
V8_EXPORT_PRIVATE void SeqString::SeqStringVerify(Isolate* isolate) {
|
||||
TorqueGeneratedSeqString<SeqString, String>::SeqStringVerify(isolate);
|
||||
DataAndPaddingSizes sz = GetDataAndPaddingSizes();
|
||||
auto padding = reinterpret_cast<char*>(address() + sz.data_size);
|
||||
CHECK(sz.padding_size <= kTaggedSize);
|
||||
for (int i = 0; i < sz.padding_size; ++i) {
|
||||
CHECK_EQ(padding[i], 0);
|
||||
}
|
||||
}
|
||||
#endif // VERIFY_HEAP
|
||||
|
||||
void SeqString::ClearPadding() {
|
||||
DataAndPaddingSizes sz = GetDataAndPaddingSizes();
|
||||
DCHECK_EQ(address() + sz.data_size + sz.padding_size, address() + Size());
|
||||
|
@ -721,6 +721,10 @@ class SeqString : public TorqueGeneratedSeqString<SeqString, String> {
|
||||
// Zero out only the padding bytes of this string.
|
||||
void ClearPadding();
|
||||
|
||||
#ifdef VERIFY_HEAP
|
||||
V8_EXPORT_PRIVATE void SeqStringVerify(Isolate* isolate);
|
||||
#endif
|
||||
|
||||
TQ_OBJECT_CONSTRUCTORS(SeqString)
|
||||
};
|
||||
|
||||
|
@ -134,7 +134,7 @@ type DirectString extends String;
|
||||
macro AllocateNonEmptySeqOneByteString<Iterator: type>(
|
||||
length: uint32, content: Iterator): SeqOneByteString {
|
||||
dcheck(length != 0 && length <= kStringMaxLength);
|
||||
return new SeqOneByteString{
|
||||
return new (ClearPadding) SeqOneByteString{
|
||||
map: kOneByteStringMap,
|
||||
raw_hash_field: kNameEmptyHashField,
|
||||
length: Signed(length),
|
||||
@ -145,7 +145,7 @@ macro AllocateNonEmptySeqOneByteString<Iterator: type>(
|
||||
macro AllocateNonEmptySeqTwoByteString<Iterator: type>(
|
||||
length: uint32, content: Iterator): SeqTwoByteString {
|
||||
dcheck(length > 0 && length <= kStringMaxLength);
|
||||
return new SeqTwoByteString{
|
||||
return new (ClearPadding) SeqTwoByteString{
|
||||
map: kStringMap,
|
||||
raw_hash_field: kNameEmptyHashField,
|
||||
length: Signed(length),
|
||||
|
@ -595,11 +595,13 @@ struct AssumeTypeImpossibleExpression : Expression {
|
||||
struct NewExpression : Expression {
|
||||
DEFINE_AST_NODE_LEAF_BOILERPLATE(NewExpression)
|
||||
NewExpression(SourcePosition pos, TypeExpression* type,
|
||||
std::vector<NameAndExpression> initializers, bool pretenured)
|
||||
std::vector<NameAndExpression> initializers, bool pretenured,
|
||||
bool clear_padding)
|
||||
: Expression(kKind, pos),
|
||||
type(type),
|
||||
initializers(std::move(initializers)),
|
||||
pretenured(pretenured) {}
|
||||
pretenured(pretenured),
|
||||
clear_padding(clear_padding) {}
|
||||
|
||||
void VisitAllSubExpressions(VisitCallback callback) override {
|
||||
for (auto& initializer : initializers) {
|
||||
@ -611,6 +613,7 @@ struct NewExpression : Expression {
|
||||
TypeExpression* type;
|
||||
std::vector<NameAndExpression> initializers;
|
||||
bool pretenured;
|
||||
bool clear_padding;
|
||||
};
|
||||
|
||||
enum class ImplicitKind { kNoImplicit, kJSImplicit, kImplicit };
|
||||
|
@ -1691,6 +1691,8 @@ VisitResult ImplementationVisitor::Visit(NewExpression* expr) {
|
||||
allocate_arguments.parameters.push_back(object_map);
|
||||
allocate_arguments.parameters.push_back(
|
||||
GenerateBoolConstant(expr->pretenured));
|
||||
allocate_arguments.parameters.push_back(
|
||||
GenerateBoolConstant(expr->clear_padding));
|
||||
VisitResult allocate_result = GenerateCall(
|
||||
QualifiedName({TORQUE_INTERNAL_NAMESPACE_STRING}, "AllocateFromNew"),
|
||||
allocate_arguments, {class_type}, false);
|
||||
|
@ -420,12 +420,13 @@ base::Optional<ParseResult> MakeMethodCall(ParseResultIterator* child_results) {
|
||||
base::Optional<ParseResult> MakeNewExpression(
|
||||
ParseResultIterator* child_results) {
|
||||
bool pretenured = child_results->NextAs<bool>();
|
||||
bool clear_padding = child_results->NextAs<bool>();
|
||||
|
||||
auto type = child_results->NextAs<TypeExpression*>();
|
||||
auto initializers = child_results->NextAs<std::vector<NameAndExpression>>();
|
||||
|
||||
Expression* result =
|
||||
MakeNode<NewExpression>(type, std::move(initializers), pretenured);
|
||||
Expression* result = MakeNode<NewExpression>(type, std::move(initializers),
|
||||
pretenured, clear_padding);
|
||||
return ParseResult{result};
|
||||
}
|
||||
|
||||
@ -2565,6 +2566,7 @@ struct TorqueGrammar : Grammar {
|
||||
Symbol newExpression = {
|
||||
Rule({Token("new"),
|
||||
CheckIf(Sequence({Token("("), Token("Pretenured"), Token(")")})),
|
||||
CheckIf(Sequence({Token("("), Token("ClearPadding"), Token(")")})),
|
||||
&simpleType, &initializerList},
|
||||
MakeNewExpression)};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user