[ubsan][compiler] Avoid OOB array accesses in Node::inputs_

Since we allocate raw zone memory for its inputs right behind the Node
object anyway, drop the previously OOB-accessed 1-element array within
the Node and use address computation to get to the inputs storage.
Note that this saves one pointer per Node, except for Nodes with zero
inputs, where it uses 1*sizeof(Use) more memory than before.

Bug: v8:3770
Change-Id: I7f5965c6f1b49013eb7f5a447b685d47decaa8fb
Reviewed-on: https://chromium-review.googlesource.com/c/1436218
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59141}
This commit is contained in:
Jakob Kummerow 2019-01-24 16:35:36 -08:00 committed by Commit Bot
parent c640296e5a
commit e8faf62ca0
2 changed files with 52 additions and 34 deletions

View File

@ -25,7 +25,7 @@ void Node::OutOfLineInputs::ExtractFrom(Use* old_use_ptr, Node** old_input_ptr,
// Extract the inputs from the old use and input pointers and copy them
// to this out-of-line-storage.
Use* new_use_ptr = reinterpret_cast<Use*>(this) - 1;
Node** new_input_ptr = inputs_;
Node** new_input_ptr = inputs();
for (int current = 0; current < count; current++) {
new_use_ptr->bit_field_ =
Use::InputIndexField::encode(current) | Use::InlineField::encode(false);
@ -72,20 +72,21 @@ Node* Node::New(Zone* zone, NodeId id, const Operator* op, int input_count,
has_extensible_inputs ? input_count + kMaxInlineCapacity : input_count;
OutOfLineInputs* outline = OutOfLineInputs::New(zone, capacity);
// Allocate node.
void* node_buffer = zone->New(sizeof(Node));
// Allocate node, with space for OutOfLineInputs pointer.
void* node_buffer = zone->New(sizeof(Node) + sizeof(OutOfLineInputs*));
node = new (node_buffer) Node(id, op, kOutlineMarker, 0);
node->inputs_.outline_ = outline;
node->set_outline_inputs(outline);
outline->node_ = node;
outline->count_ = input_count;
input_ptr = outline->inputs_;
input_ptr = outline->inputs();
use_ptr = reinterpret_cast<Use*>(outline);
is_inline = false;
} else {
// Allocate node with inline inputs.
int capacity = input_count;
// Allocate node with inline inputs. Capacity must be at least 1 so that
// an OutOfLineInputs pointer can be stored when inputs are added later.
int capacity = std::max(1, input_count);
if (has_extensible_inputs) {
const int max = kMaxInlineCapacity;
capacity = std::min(input_count + 3, max);
@ -97,7 +98,7 @@ Node* Node::New(Zone* zone, NodeId id, const Operator* op, int input_count,
reinterpret_cast<void*>(raw_buffer + capacity * sizeof(Use));
node = new (node_buffer) Node(id, op, input_count, capacity);
input_ptr = node->inputs_.inline_;
input_ptr = node->inline_inputs();
use_ptr = reinterpret_cast<Use*>(node);
is_inline = true;
}
@ -119,8 +120,8 @@ Node* Node::New(Zone* zone, NodeId id, const Operator* op, int input_count,
Node* Node::Clone(Zone* zone, NodeId id, const Node* node) {
int const input_count = node->InputCount();
Node* const* const inputs = node->has_inline_inputs()
? node->inputs_.inline_
: node->inputs_.outline_->inputs_;
? node->inline_inputs()
: node->outline_inputs()->inputs();
Node* const clone = New(zone, id, node->op(), input_count, inputs, false);
clone->set_type(node->type());
return clone;
@ -158,16 +159,16 @@ void Node::AppendInput(Zone* zone, Node* new_to) {
outline->node_ = this;
outline->ExtractFrom(GetUsePtr(0), GetInputPtr(0), input_count);
bit_field_ = InlineCountField::update(bit_field_, kOutlineMarker);
inputs_.outline_ = outline;
set_outline_inputs(outline);
} else {
// use current out of line inputs.
outline = inputs_.outline_;
outline = outline_inputs();
if (input_count >= outline->capacity_) {
// out of space in out-of-line inputs.
outline = OutOfLineInputs::New(zone, input_count * 2 + 3);
outline->node_ = this;
outline->ExtractFrom(GetUsePtr(0), GetInputPtr(0), input_count);
inputs_.outline_ = outline;
set_outline_inputs(outline);
}
}
outline->count_++;
@ -247,7 +248,7 @@ void Node::TrimInputCount(int new_input_count) {
if (has_inline_inputs()) {
bit_field_ = InlineCountField::update(bit_field_, new_input_count);
} else {
inputs_.outline_->count_ = new_input_count;
outline_inputs()->count_ = new_input_count;
}
}

View File

@ -60,7 +60,7 @@ class V8_EXPORT_PRIVATE Node final {
int InputCount() const {
return has_inline_inputs() ? InlineCountField::decode(bit_field_)
: inputs_.outline_->count_;
: outline_inputs()->count_;
}
#ifdef DEBUG
@ -170,7 +170,9 @@ class V8_EXPORT_PRIVATE Node final {
Node* node_;
int count_;
int capacity_;
Node* inputs_[1];
// Inputs are allocated right behind the OutOfLineInputs instance.
inline Node** inputs();
static OutOfLineInputs* New(Zone* zone, int capacity);
void ExtractFrom(Use* use_ptr, Node** input_ptr, int count);
@ -189,8 +191,8 @@ class V8_EXPORT_PRIVATE Node final {
int index = input_index();
Use* start = this + 1 + index;
Node** inputs = is_inline_use()
? reinterpret_cast<Node*>(start)->inputs_.inline_
: reinterpret_cast<OutOfLineInputs*>(start)->inputs_;
? reinterpret_cast<Node*>(start)->inline_inputs()
: reinterpret_cast<OutOfLineInputs*>(start)->inputs();
return &inputs[index];
}
@ -239,17 +241,29 @@ class V8_EXPORT_PRIVATE Node final {
Node(NodeId id, const Operator* op, int inline_count, int inline_capacity);
inline Address inputs_location() const;
Node** inline_inputs() const {
return reinterpret_cast<Node**>(inputs_location());
}
OutOfLineInputs* outline_inputs() const {
return *reinterpret_cast<OutOfLineInputs**>(inputs_location());
}
void set_outline_inputs(OutOfLineInputs* outline) {
*reinterpret_cast<OutOfLineInputs**>(inputs_location()) = outline;
}
Node* const* GetInputPtrConst(int input_index) const {
return has_inline_inputs() ? &(inputs_.inline_[input_index])
: &inputs_.outline_->inputs_[input_index];
return has_inline_inputs() ? &(inline_inputs()[input_index])
: &(outline_inputs()->inputs()[input_index]);
}
Node** GetInputPtr(int input_index) {
return has_inline_inputs() ? &(inputs_.inline_[input_index])
: &inputs_.outline_->inputs_[input_index];
return has_inline_inputs() ? &(inline_inputs()[input_index])
: &(outline_inputs()->inputs()[input_index]);
}
Use* GetUsePtr(int input_index) {
Use* ptr = has_inline_inputs() ? reinterpret_cast<Use*>(this)
: reinterpret_cast<Use*>(inputs_.outline_);
: reinterpret_cast<Use*>(outline_inputs());
return &ptr[-1 - input_index];
}
@ -287,11 +301,6 @@ class V8_EXPORT_PRIVATE Node final {
Mark mark_;
uint32_t bit_field_;
Use* first_use_;
union {
// Inline storage for inputs or out-of-line storage.
Node* inline_[1];
OutOfLineInputs* outline_;
} inputs_;
friend class Edge;
friend class NodeMarkerBase;
@ -300,6 +309,14 @@ class V8_EXPORT_PRIVATE Node final {
DISALLOW_COPY_AND_ASSIGN(Node);
};
Address Node::inputs_location() const {
return reinterpret_cast<Address>(this) + sizeof(Node);
}
Node** Node::OutOfLineInputs::inputs() {
return reinterpret_cast<Node**>(reinterpret_cast<Address>(this) +
sizeof(Node::OutOfLineInputs));
}
std::ostream& operator<<(std::ostream& os, const Node& n);
@ -402,21 +419,21 @@ bool Node::IsDead() const {
Node::InputEdges Node::input_edges() {
int inline_count = InlineCountField::decode(bit_field_);
if (inline_count != kOutlineMarker) {
return InputEdges(inputs_.inline_, reinterpret_cast<Use*>(this) - 1,
return InputEdges(inline_inputs(), reinterpret_cast<Use*>(this) - 1,
inline_count);
} else {
return InputEdges(inputs_.outline_->inputs_,
reinterpret_cast<Use*>(inputs_.outline_) - 1,
inputs_.outline_->count_);
return InputEdges(outline_inputs()->inputs(),
reinterpret_cast<Use*>(outline_inputs()) - 1,
outline_inputs()->count_);
}
}
Node::Inputs Node::inputs() const {
int inline_count = InlineCountField::decode(bit_field_);
if (inline_count != kOutlineMarker) {
return Inputs(inputs_.inline_, inline_count);
return Inputs(inline_inputs(), inline_count);
} else {
return Inputs(inputs_.outline_->inputs_, inputs_.outline_->count_);
return Inputs(outline_inputs()->inputs(), outline_inputs()->count_);
}
}